From 21055a1e5d34dda1b8151dd46a6bedca22bd73d9 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Sat, 18 Apr 2020 13:40:53 -0700 Subject: feat: added prototype of io/buffered io --- src/bufio/buffer.c | 24 +++++++ src/bufio/impl.h | 34 +++++++++ src/bufio/reader.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bufio/rules.mk | 38 ++++++++++ src/bufio/writer.c | 95 +++++++++++++++++++++++++ src/test.c | 2 + 6 files changed, 391 insertions(+) create mode 100644 src/bufio/buffer.c create mode 100644 src/bufio/impl.h create mode 100644 src/bufio/reader.c create mode 100644 src/bufio/rules.mk create mode 100644 src/bufio/writer.c (limited to 'src') diff --git a/src/bufio/buffer.c b/src/bufio/buffer.c new file mode 100644 index 0000000..8963652 --- /dev/null +++ b/src/bufio/buffer.c @@ -0,0 +1,24 @@ +#include "impl.h" + +// NewBuffer allocates a bytes buffer with size 'size'. +// This is unallocated usage. +// Returns the fat pointer by value. + +void* +newbuffer(uintptr off, uintptr size) +{ + if (size == 0 || size > BUF·max) size = BUF·size; + + void *mem = malloc(off + sizeof(Buffer) + size); + Buffer *buf = mem + off; + + buf->eof = 0; + buf->off = 0; + buf->base = buf->b + BUF·ungets; + buf->end = buf->b + size; + buf->size = buf->end - buf->base; + + memset(buf->b, 0, size); + + return mem; +} diff --git a/src/bufio/impl.h b/src/bufio/impl.h new file mode 100644 index 0000000..f5c3bcf --- /dev/null +++ b/src/bufio/impl.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include + +enum +{ + BUF·max = 4 * 8096, + BUF·size = 4 * 4096, + BUF·ungets = 8, + BUF·err = -1, + BUF·eof = +1, +}; + +typedef struct Buffer +{ + uint8 eof; + int64 off; + int64 size; + + byte* base; + byte* pos; + byte* end; + byte b[]; +} Buffer; + +void* newbuffer(uintptr off, uintptr size); + +struct bufio·Scanner +{ + io·Reader r; + Buffer buf; +}; 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); +} diff --git a/src/bufio/rules.mk b/src/bufio/rules.mk new file mode 100644 index 0000000..e400860 --- /dev/null +++ b/src/bufio/rules.mk @@ -0,0 +1,38 @@ +# ---- Push on stack ---- +SP := $(SP).x +DIRSTACK_$(SP) := $(d) +d := $(DIR) + +# Iterate through subdirectory tree +# ... + +# Local variables +SRCS_$(d) := $(wildcard $(d)/*.c) +OBJS_$(d) := $(SRCS_$(d):.c=.o) +OBJS_$(d) := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(OBJS_$(d))) + +OBJS := $(OBJS) $(OBJS_$(d)) + +DEPS_$(d) := $(OBJS_$(d):.o=.d) + +DEPS := $(DEPS) $(DEPS_$(d)) + +LIBS_$(d) := $(d)/libbufio.a +LIBS_$(d) := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(LIBS_$(d))) +BINS_$(d) := + +LIBS := $(LIBS) $(LIBS_$(d)) + +# Local rules +# $(LIBS_$(d)) := TGTFLAGS := +# $(LIBS_$(d)) := TGTINCS := +# $(LIBS_$(d)) := TGTLIBS := + +$(LIBS_$(d)): $(OBJS_$(d)) + $(ARCHIVE) + +# ---- Pop off stack ---- +-include $(DEPS_$(d)) + +d := $(DIRSTACK_$(SP)) +SP := $(basename $(SP)) diff --git a/src/bufio/writer.c b/src/bufio/writer.c new file mode 100644 index 0000000..07573d5 --- /dev/null +++ b/src/bufio/writer.c @@ -0,0 +1,95 @@ +#include "impl.h" + +struct bufio·Writer +{ + io·Writer w; + Buffer buf; +}; + +// Constructor/destructor. + +bufio·Writer* +bufio·newwriter(io·Writer w) +{ + bufio·Writer* bw = newbuffer(offsetof(bufio·Writer, buf), 0); + bw->w = w; + bw->buf.pos = bw->buf.base; + + return bw; +} + +error +bufio·freewriter(bufio·Writer* w) +{ + free(w); + + return 0; +} + +// Write will write n bytes from the input buffer into the storage buffer. +// Returns the actual number of bytes written. +int +bufio·write(bufio·Writer* w, byte* buf, int count) +{ + int remain = count; + byte* wtr = buf; + + int n, nw; + while (remain > 0) { + int bdiff = w->buf.end - w->buf.pos; + n = (bdiff > remain) ? remain : bdiff; + if (!n) { + if (w->buf.eof) break; + nw = w->w.write(w->w.impl, buf, count); + if (nw < w->buf.size) { + w->buf.eof = 1; + return BUF·err; + } + w->buf.off += nw; + w->buf.pos = w->buf.base; + } + memmove(w->buf.pos, wtr, n); + wtr += n; + w->buf.pos += n; + remain -= n; + } + + return count - remain; +} + +error +bufio·put(bufio·Writer* w, byte c) +{ + int diff; +putbyte: + diff = w->buf.end - w->buf.pos; + if (diff) { + *w->buf.pos++ = c; + return 0; + } + if (bufio·flush(w) != BUF·err) goto putbyte; + + return BUF·err; +} + +error +bufio·flush(bufio·Writer* w) +{ + int active = (w->buf.eof == 0); + + int n, nw; + if (active && w) { + n = w->buf.pos - w->buf.base; + if (!n) return 0; + + nw = w->w.write(w->w.impl, w->buf.base, n); + if (nw == n) { + w->buf.off += n; + return 0; + } + w->buf.eof = 1; + w->buf.pos = w->buf.end; + } + + return BUF·err; +} diff --git a/src/test.c b/src/test.c index e9743cb..3f8e608 100644 --- a/src/test.c +++ b/src/test.c @@ -34,6 +34,8 @@ filter(coro *c, uintptr data) coro *seq; struct PrimeMsg *msg; + // Need to copy relevant variables onto the local stack + // Data is volatile. msg = (struct PrimeMsg*)data; seq = msg->seq; p = msg->p; -- cgit v1.2.1