diff options
Diffstat (limited to 'src/bufio/reader.c')
-rw-r--r-- | src/bufio/reader.c | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/src/bufio/reader.c b/src/bufio/reader.c new file mode 100644 index 0000000..915c531 --- /dev/null +++ b/src/bufio/reader.c @@ -0,0 +1,198 @@ +#include "impl.h" + +struct bufio·Reader +{ + io·Reader r; + Buffer buf; +}; + +bufio·Reader* +bufio·newreader(io·Reader r) +{ + bufio·Reader* br = newbuffer(offsetof(bufio·Reader, buf), 0); + br->r = r; + br->buf.pos = br->buf.end; + + return br; +} + +error +bufio·freereader(bufio·Reader* r) +{ + free(r); + return 0; +} + +static int +refill(bufio·Reader* r) +{ + if (r->buf.eof) return BUF·err; + + int nr = r->r.read(r->r.impl, r->buf.base, r->buf.size); + if (nr <= 0) { + r->buf.eof = 1; + goto end; + } + + r->buf.off += nr; + r->buf.pos = r->buf.base; + + if (nr < r->buf.size) { + int d = (r->buf.size - nr); + r->buf.eof = 1; + memmove(r->buf.pos + d, r->buf.pos, nr); + memmove(r->buf.pos + d - BUF·ungets, r->buf.b, BUF·ungets); + r->buf.pos += d; + } +end: + return nr; +} + +// Read will attempt to read n bytes from the buffer, starting at the offet +// offset. Returns the actual number of bytes read from the stream. Input buf +// can not alias b +int +bufio·read(bufio·Reader* r, byte* buf, int count) +{ + if (count == 0 || buf == nil || r->buf.off == BUF·eof) return BUF·err; + + int remain = count; + byte* rdr = buf; + + int n, nr; + while (remain) { + int bdiff = r->buf.end - r->buf.pos; + n = (bdiff > remain) ? remain : bdiff; + if (!n) { + if (r->buf.eof | (refill(r) <= 0)) break; + } + + memmove(rdr, r->buf.pos, n); + rdr += n; + r->buf.pos += n; + remain -= n; + } + return count - remain; +} + +int +bufio·readln(bufio·Reader* r, byte* buf, int count, error* err) +{ + byte *it, *end, *cnt; + int nb; +Search: + nb = 0; + cnt = r->buf.pos + count; + end = MIN(r->buf.end, cnt); + + for (it = r->buf.pos; it != end && *it != EOF; ++it, nb++) { + if (*it == '\n') { + *err = 0; + goto End; + } + } + + if (it == r->buf.end) { + memmove(buf, r->buf.pos, nb); + r->buf.pos = r->buf.end; + if (refill(r) <= 0) { + *err = 1; + return nb; + } + Assert(nb < count); + count -= nb; + buf += nb; + goto Search; + } + + if (it == cnt) { + Assert(nb == count); + *err = 2; + } + + Assert(*it == EOF || *err == 2); + +End: + memmove(buf, r->buf.pos, nb); + r->buf.pos = it; + return nb; +} + +// advanceBuffer moves the buffer ahead by n without reading. +void +bufio·discard(bufio·Reader* r, int count) +{ + if (count > BUF·max) + panicf("can't discard %d bytes", count); + byte stack[BUF·max]; + + bufio·read(r, stack, count); +} + +// ReadByte reads a single byte from the buffered stream. +byte +bufio·get(bufio·Reader* r) +{ +getbyte: + if (r->buf.pos < r->buf.end) { return *r->buf.pos++; } + + memmove(r->buf.b, r->buf.end - BUF·ungets, BUF·ungets); + if (refill(r) <= 0) { return EOF; } + goto getbyte; +} + +error +bufio·unget(bufio·Reader* r) +{ + if (r->buf.pos == r->buf.b) { return BUF·err; } + + r->buf.pos--; + return 0; +} + +byte +bufio·peek(bufio·Reader* r) +{ + if (r->buf.pos != r->buf.end) return *r->buf.pos; + + byte* old = r->buf.pos; + byte c = bufio·get(r); + if (r->buf.pos != old && (bufio·unget(r) == BUF·err)) { + panicf("buffer failure, can't move backwards"); + } + + return c; +} + +// peekBytes has the same behavior as read, but doesn't advance the buffer. +int +bufio·peekfor(bufio·Reader* r, byte* buf, int count) +{ + if (count == 0 || buf == nil) return 0; + + int delta = r->buf.end - r->buf.pos; + if (delta > count) { + memcpy(buf, r->buf.pos, count); + return count; + } + + byte c, *it; + for (it = buf; it != buf + count; ++it) { + c = bufio·get(r); + *it = c; + if (c == EOF) break; + } + + int diff = it - buf; + for (int n = 0; n < diff; n++) { + bufio·unget(r); + } + + return diff; +} + +void +bufio·clear(bufio·Reader* r) +{ + memset(r->buf.b, 0, r->buf.size); +} |