Files
c3c/lib/std/io/stream/scanner.c3
Christoffer Lerno 25bccf4883 New faults and syntax (#2034)
- Remove `[?]` syntax.
- Change `int!` to `int?` syntax.
- New `fault` declarations.
- Enum associated values can reference the calling enum.
2025-03-10 00:11:35 +01:00

123 lines
2.8 KiB
Plaintext

module std::io;
struct Scanner (InStream)
{
InStream wrapped_stream;
char[] buf;
usz pattern_idx;
usz read_idx;
}
<*
Scanner provides a way to read delimited data (with newlines as the default).
The supplied buffer must be at least as large as the expected data length
including its pattern.
@param [&in] stream : "The stream to read data from."
@require buffer.len > 0 : "Non-empty buffer required."
*>
fn void Scanner.init(&self, InStream stream, char[] buffer)
{
*self = { .wrapped_stream = stream, .buf = buffer };
}
<*
Return and clear any remaining unscanned data.
*>
fn char[] Scanner.flush(&self) @dynamic
{
assert(self.read_idx >= self.pattern_idx);
usz n = self.read_idx - self.pattern_idx;
char[] buf = self.buf[self.pattern_idx:n];
self.pattern_idx = 0;
self.read_idx = 0;
return buf;
}
fn void? Scanner.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
<*
Scan the stream for the next split character and return data up to the match.
@require pattern.len > 0 : "Non-empty pattern required."
@require self.buf.len > pattern.len : "Pattern too large."
*>
fn char[]? Scanner.scan(&self, String pattern = "\n")
{
if (self.read_idx == 0)
{
// First read.
self.read_idx = self.refill(self.buf)!;
self.pattern_idx = 0;
}
assert(self.read_idx >= self.pattern_idx);
usz n = self.read_idx - self.pattern_idx;
char[] buf = self.buf[self.pattern_idx:n];
if (try i = self.find(buf, pattern))
{
self.pattern_idx += i + pattern.len;
return buf[:i];
}
if (self.pattern_idx == 0 || self.read_idx < self.buf.len)
{
// Split pattern not found with maximized search, abort.
// Split pattern not found and already read as much as possible.
return NOT_FOUND?;
}
// Split pattern not found: maximize the search and try one more time.
self.buf[:n] = buf[..];
self.pattern_idx = 0;
buf = self.buf[n..];
usz p = self.refill(buf)!;
self.read_idx = n + p;
buf = buf[:p];
usz i = self.find(buf, pattern)!;
self.pattern_idx = n + i + pattern.len;
return self.buf[:n + i];
}
macro usz? Scanner.find(&self, buf, pattern) @private
{
return ((String)buf).index_of(pattern);
}
macro usz? Scanner.refill(&self, buf) @private
{
usz? n = self.wrapped_stream.read(buf);
if (catch err = n)
{
if (err == io::EOF) return NOT_FOUND?;
return err?;
}
return n;
}
fn usz? Scanner.read(&self, char[] bytes) @dynamic
{
usz n;
if (self.pattern_idx < self.read_idx)
{
n = min(bytes.len, self.read_idx - self.pattern_idx);
bytes[:n] = self.buf[self.pattern_idx:n];
self.pattern_idx += n;
bytes = bytes[n..];
}
n += self.wrapped_stream.read(bytes)!;
return n;
}
fn char? Scanner.read_byte(&self) @dynamic
{
if (self.pattern_idx < self.read_idx)
{
return self.buf[self.pattern_idx++];
}
return self.wrapped_stream.read_byte();
}