aboutsummaryrefslogtreecommitdiff
path: root/src/bufio/reader.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bufio/reader.c')
-rw-r--r--src/bufio/reader.c198
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);
+}