commit a986adec473af9dd117676d15acf351d386017ee
parent 678e4f644cfd39043150f015ac7339d0d7555800
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Thu, 28 Sep 2023 07:58:09 +0200
bufio::scanner: support unread
Diffstat:
3 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/bufio/scanner.ha b/bufio/scanner.ha
@@ -271,6 +271,15 @@ export fn scan_buffer(scan: *scanner) []u8 = {
return scan.buffer[..scan.pending];
};
+fn scan_unread(scan: *scanner, buf: []u8) void = {
+ if (len(buf) == 0) {
+ return;
+ };
+ assert(len(buf) <= scan.readout);
+ scan.buffer[scan.readout - len(buf)..scan.readout] = buf;
+ scan.readout -= len(buf);
+};
+
// Reads a single byte from an [[io::handle]].
export fn read_byte(file: io::handle) (u8 | io::EOF | io::error) = {
let buf: [1]u8 = [0...];
diff --git a/bufio/scanner_test+test.ha b/bufio/scanner_test+test.ha
@@ -119,7 +119,37 @@ use strings;
let in = memio::fixed(expected);
let scanner = newscanner(&in, 2);
+ defer finish(&scanner);
let result = io::drain(&scanner)!;
defer free(result);
assert(bytes::equal(expected, result));
};
+
+@test fn scan_unread() void = {
+ const expected: str = " I will not repeat \nDone!\n";
+ let in = memio::fixed(strings::toutf8(expected));
+
+ let scanner = newscanner(&in, 32);
+ defer finish(&scanner);
+ let l = scan_line(&scanner)! as const str;
+ assert(l == " I will not repeat ");
+
+ unread(&scanner, strings::toutf8("\n"));
+ unread(&scanner, strings::toutf8(l));
+ let l = scan_line(&scanner)! as const str;
+ assert(l == " I will not repeat ");
+
+ unread(&scanner, strings::toutf8("\n"));
+ unread(&scanner, strings::toutf8(strings::trim(l)));
+ let l = scan_line(&scanner)! as const str;
+ assert(l == "I will not repeat");
+
+ unread(&scanner, strings::toutf8("See?\n"));
+ let l = scan_line(&scanner)! as const str;
+ assert(l == "See?");
+
+ let l = scan_line(&scanner)! as const str;
+ assert(l == "Done!");
+
+ assert(scan_line(&scanner) is io::EOF);
+};
diff --git a/bufio/stream.ha b/bufio/stream.ha
@@ -128,15 +128,22 @@ export fn setflush(s: io::handle, b: []u8) void = {
// buffered stream. Attempting to unread more data than can fit into the read
// buffer will abort the program.
export fn unread(s: io::handle, buf: []u8) void = {
- let s = match (s) {
+ match (s) {
case let st: *io::stream =>
- if (st.reader != &read) {
+ switch (st.reader) {
+ case &read =>
+ stream_unread(s: *stream, buf);
+ case &scan_read =>
+ scan_unread(s: *scanner, buf);
+ case =>
abort("Attempted unread on unbuffered stream");
};
- yield st: *stream;
case =>
abort("Attempted unread on unbuffered stream");
};
+};
+
+fn stream_unread(s: *stream, buf: []u8) void = {
assert(s.rpos >= len(buf),
"Attempted to unread more data than buffer has available");
s.rbuffer[s.rpos - len(buf)..s.rpos] = buf;