Files
c3c/lib/std/io/stream/scanner.c3
Pierre Curto d61482dffc fix Object.free (#982)
* lib/std/collections: add HashMap.@each_entry()
* lib/std/json: fix Object.free() when object is a map
* lib/std/collections: fix allocator use in Object.{set,set_at,append}
* lib/std: add char.from_hex
* lib/std/collections: print arrays and objects compactly
* lib/std/io: fix Formatter.vprintf result
* lib/std/io/stream: rename module for ByteBuffer
* lib/std/io/stream: make Scanner a Stream reader
* lib/std/io: make std{in,err,out} return File* if no libc
2023-09-12 13:49:52 +02:00

126 lines
2.9 KiB
C

module std::io;
struct Scanner
{
inline Stream stream;
Stream* 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."
* @require stream.supports_read() "The stream must support read."
*
**/
fn void Scanner.init(&self, Stream* stream, char[] buffer)
{
*self = { .stream.fns = &SCANNER_INTERFACE, .wrapped_stream = stream, .buf = buffer };
}
const StreamInterface SCANNER_INTERFACE = {
.read_fn = (ReadStreamFn)&Scanner.read,
.read_byte_fn = (ReadByteStreamFn)&Scanner.read_byte,
};
/**
* Return and clear any remaining unscanned data.
**/
fn char[] Scanner.flush(&self)
{
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;
}
/**
* 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 SearchResult.MISSING?;
}
// 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)
{
case IoError.EOF:
return SearchResult.MISSING?;
default:
return err?;
}
return n;
}
fn usz! Scanner.read(&self, char[] bytes)
{
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)
{
if (self.pattern_idx < self.read_idx)
{
return self.buf[self.pattern_idx++];
}
return self.wrapped_stream.read_byte();
}