aboutsummaryrefslogtreecommitdiff
path: root/core/bytes/buffer.odin
diff options
context:
space:
mode:
Diffstat (limited to 'core/bytes/buffer.odin')
-rw-r--r--core/bytes/buffer.odin335
1 files changed, 335 insertions, 0 deletions
diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin
new file mode 100644
index 000000000..61fde9605
--- /dev/null
+++ b/core/bytes/buffer.odin
@@ -0,0 +1,335 @@
+package bytes
+
+import "core:io"
+import "core:unicode/utf8"
+
+MIN_READ :: 512;
+
+@(private)
+SMALL_BUFFER_SIZE :: 64;
+
+Buffer :: struct {
+ buf: [dynamic]byte,
+ off: int,
+ last_read: Read_Op,
+}
+
+@(private)
+Read_Op :: enum i8 {
+ Read = -1,
+ Invalid = 0,
+ Read_Rune1 = 1,
+ Read_Rune2 = 2,
+ Read_Rune3 = 3,
+ Read_Rune4 = 4,
+}
+
+
+buffer_init :: proc(b: ^Buffer, buf: []byte) {
+ resize(&b.buf, len(buf));
+ copy(b.buf[:], buf);
+}
+
+buffer_init_string :: proc(b: ^Buffer, s: string) {
+ resize(&b.buf, len(s));
+ copy(b.buf[:], s);
+}
+
+
+buffer_destroy :: proc(b: ^Buffer) {
+ delete(b.buf);
+ buffer_reset(b);
+}
+
+buffer_to_bytes :: proc(b: ^Buffer) -> []byte {
+ return b.buf[b.off:];
+}
+
+buffer_to_string :: proc(b: ^Buffer) -> string {
+ if b == nil {
+ return "<nil>";
+ }
+ return string(b.buf[b.off:]);
+}
+
+buffer_is_empty :: proc(b: ^Buffer) -> bool {
+ return len(b.buf) <= b.off;
+}
+
+buffer_length :: proc(b: ^Buffer) -> int {
+ return len(b.buf) - b.off;
+}
+
+buffer_capacity :: proc(b: ^Buffer) -> int {
+ return cap(b.buf);
+}
+
+buffer_reset :: proc(b: ^Buffer) {
+ clear(&b.buf);
+ b.off = 0;
+ b.last_read = .Invalid;
+}
+
+
+buffer_truncate :: proc(b: ^Buffer, n: int) {
+ if n == 0 {
+ buffer_reset(b);
+ return;
+ }
+ b.last_read = .Invalid;
+ if n < 0 || n > buffer_length(b) {
+ panic("bytes.truncate: truncation out of range");
+ }
+ resize(&b.buf, b.off+n);
+}
+
+@(private)
+_buffer_try_grow :: proc(b: ^Buffer, n: int) -> (int, bool) {
+ if l := len(b.buf); n <= cap(b.buf)-l {
+ resize(&b.buf, l+n);
+ return l, true;
+ }
+ return 0, false;
+}
+
+@(private)
+_buffer_grow :: proc(b: ^Buffer, n: int) -> int {
+ m := buffer_length(b);
+ if m == 0 && b.off != 0 {
+ buffer_reset(b);
+ }
+ if i, ok := _buffer_try_grow(b, n); ok {
+ return i;
+ }
+ if b.buf == nil && n <= SMALL_BUFFER_SIZE {
+ b.buf = make([dynamic]byte, n, SMALL_BUFFER_SIZE);
+ return 0;
+ }
+
+ c := cap(b.buf);
+ if n <= c/2 - m {
+ copy(b.buf[:], b.buf[b.off:]);
+ } else if c > max(int) - c - n {
+ panic("bytes.Buffer: too large");
+ } else {
+ resize(&b.buf, 2*c + n);
+ copy(b.buf[:], b.buf[b.off:]);
+ }
+ b.off = 0;
+ resize(&b.buf, m+n);
+ return m;
+}
+
+buffer_grow :: proc(b: ^Buffer, n: int) {
+ if n < 0 {
+ panic("bytes.buffer_grow: negative count");
+ }
+ m := _buffer_grow(b, n);
+ resize(&b.buf, m);
+}
+
+
+buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, len(p));
+ if !ok {
+ m = _buffer_grow(b, len(p));
+ }
+ return copy(b.buf[m:], p), nil;
+}
+
+buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, len(s));
+ if !ok {
+ m = _buffer_grow(b, len(s));
+ }
+ return copy(b.buf[m:], s), nil;
+}
+
+buffer_write_byte :: proc(b: ^Buffer, c: byte) -> io.Error {
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, 1);
+ if !ok {
+ m = _buffer_grow(b, 1);
+ }
+ b.buf[m] = c;
+ return nil;
+}
+
+buffer_write_rune :: proc(b: ^Buffer, r: rune) -> (n: int, err: io.Error) {
+ if r < utf8.RUNE_SELF {
+ buffer_write_byte(b, byte(r));
+ return 1, nil;
+ }
+ b.last_read = .Invalid;
+ m, ok := _buffer_try_grow(b, utf8.UTF_MAX);
+ if !ok {
+ m = _buffer_grow(b, utf8.UTF_MAX);
+ }
+ res: [4]byte;
+ res, n = utf8.encode_rune(r);
+ copy(b.buf[m:][:utf8.UTF_MAX], res[:n]);
+ resize(&b.buf, m+n);
+ return;
+}
+
+buffer_next :: proc(b: ^Buffer, n: int) -> []byte {
+ n := n;
+ b.last_read = .Invalid;
+ m := buffer_length(b);
+ if n > m {
+ n = m;
+ }
+ data := b.buf[b.off : b.off + n];
+ b.off += n;
+ if n > 0 {
+ b.last_read = .Read;
+ }
+ return data;
+}
+
+buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
+ b.last_read = .Invalid;
+ if buffer_is_empty(b) {
+ buffer_reset(b);
+ if len(p) == 0 {
+ return 0, nil;
+ }
+ return 0, .EOF;
+ }
+ n = copy(p, b.buf[b.off:]);
+ b.off += n;
+ if n > 0 {
+ b.last_read = .Read;
+ }
+ return;
+}
+
+buffer_read_byte :: proc(b: ^Buffer) -> (byte, io.Error) {
+ if buffer_is_empty(b) {
+ buffer_reset(b);
+ return 0, .EOF;
+ }
+ c := b.buf[b.off];
+ b.off += 1;
+ b.last_read = .Read;
+ return c, nil;
+}
+
+buffer_read_rune :: proc(b: ^Buffer) -> (r: rune, size: int, err: io.Error) {
+ if buffer_is_empty(b) {
+ buffer_reset(b);
+ return 0, 0, .EOF;
+ }
+ c := b.buf[b.off];
+ if c < utf8.RUNE_SELF {
+ b.off += 1;
+ b.last_read = .Read_Rune1;
+ return rune(c), 1, nil;
+ }
+ r, size = utf8.decode_rune(b.buf[b.off:]);
+ b.off += size;
+ b.last_read = Read_Op(i8(size));
+ return;
+}
+
+buffer_unread_byte :: proc(b: ^Buffer) -> io.Error {
+ if b.last_read == .Invalid {
+ return .Invalid_Unread;
+ }
+ b.last_read = .Invalid;
+ if b.off > 0 {
+ b.off -= 1;
+ }
+ return nil;
+}
+
+buffer_unread_rune :: proc(b: ^Buffer) -> io.Error {
+ if b.last_read <= .Invalid {
+ return .Invalid_Unread;
+ }
+ if b.off >= int(b.last_read) {
+ b.off -= int(i8(b.last_read));
+ }
+ b.last_read = .Invalid;
+ return nil;
+}
+
+
+buffer_read_bytes :: proc(b: ^Buffer, delim: byte) -> (line: []byte, err: io.Error) {
+ i := index_byte(b.buf[b.off:], delim);
+ end := b.off + i + 1;
+ if i < 0 {
+ end = len(b.buf);
+ err = .EOF;
+ }
+ line = b.buf[b.off:end];
+ b.off = end;
+ b.last_read = .Read;
+ return;
+}
+
+buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Error) {
+ slice: []byte;
+ slice, err = buffer_read_bytes(b, delim);
+ return string(slice), err;
+}
+
+
+
+buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
+ s.stream_data = b;
+ s.stream_vtable = _buffer_vtable;
+ return;
+}
+
+@(private)
+_buffer_vtable := &io.Stream_VTable{
+ impl_size = proc(s: io.Stream) -> i64 {
+ b := (^Buffer)(s.stream_data);
+ return i64(buffer_capacity(b));
+ },
+ impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_read(b, p);
+ },
+ impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_read_byte(b);
+ },
+ impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_read_rune(b);
+ },
+ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_write(b, p);
+ },
+ impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ return buffer_write_byte(b, c);
+ },
+ impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
+ b := (^Buffer)(s.stream_data);
+ return buffer_write_rune(b, r);
+ },
+ impl_unread_byte = proc(s: io.Stream) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ return buffer_unread_byte(b);
+ },
+ impl_unread_rune = proc(s: io.Stream) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ return buffer_unread_rune(b);
+ },
+ impl_destroy = proc(s: io.Stream) -> io.Error {
+ b := (^Buffer)(s.stream_data);
+ buffer_destroy(b);
+ return nil;
+ },
+
+ // TODO(bill): write_to and read_from
+ // impl_write_to = nil,
+ // impl_read_from = nil,
+};
+