commit da868e283b1a38de2346695c7e9916e200adc410
parent ef3d2b2d0fce8450e4f91ab5166558c013c0c5fc
Author: Alexey Yerin <yyp@disroot.org>
Date: Sat, 27 Jan 2024 22:47:32 +0300
bufio::scanner: Always shift readout before requesting reads
Some functions did not follow this pattern (most importantly, scan_byte), which
caused the scanner to wrongly assume it has enough capacity and return random
bytes from its buffer.
The reason for that is (scan.pending == 0) check that assumes all of
scan.buffer[0..scan.pending] is the readahead data, which is not the case before
shifting since scan.pending includes scan.readout as well.
This could have been caught by a simple unit test, but there were no tests for
base scanner functionality.
Signed-off-by: Alexey Yerin <yyp@disroot.org>
Diffstat:
2 files changed, 17 insertions(+), 5 deletions(-)
diff --git a/bufio/scanner.ha b/bufio/scanner.ha
@@ -68,6 +68,10 @@ export fn finish(scan: *scanner) void = {
fn scan_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
let scan = s: *scanner;
+
+ // Consume previous read, if any
+ scan_shift(scan);
+
if (scan.pending == 0) {
match (scan_readahead(scan)?) {
case io::EOF =>
@@ -77,9 +81,6 @@ fn scan_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
};
};
- // Consume previous read, if any
- scan_shift(scan);
-
const n = if (len(buf) > scan.pending) scan.pending else len(buf);
buf[..n] = scan_consume(scan, n)[..];
return n;
@@ -133,6 +134,9 @@ fn scan_consume(scan: *scanner, n: size) []u8 = {
// Reads one byte from a [[scanner]].
export fn scan_byte(scan: *scanner) (u8 | io::EOF | io::error) = {
+ // Consume previous read, if any
+ scan_shift(scan);
+
if (scan.pending == 0) {
match (scan_readahead(scan)?) {
case io::EOF =>
@@ -142,8 +146,6 @@ export fn scan_byte(scan: *scanner) (u8 | io::EOF | io::error) = {
};
};
- // Consume previous read, if any
- scan_shift(scan);
return scan_consume(scan, 1)[0];
};
diff --git a/bufio/scanner_test+test.ha b/bufio/scanner_test+test.ha
@@ -115,6 +115,16 @@ use strings;
};
};
+@test fn scan_byte() void = {
+ let in = memio::fixed([1, 2, 3]);
+ let scanner = newscanner(&in, 3);
+
+ assert(scan_byte(&scanner) as u8 == 1);
+ assert(scan_byte(&scanner) as u8 == 2);
+ assert(scan_byte(&scanner) as u8 == 3);
+ assert(scan_byte(&scanner) is io::EOF);
+};
+
@test fn scan_read() void = {
const expected: [_]u8 = [
0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,