diff options
| author | gingerBill <bill@gingerbill.org> | 2020-12-05 15:14:21 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2020-12-05 15:14:21 +0000 |
| commit | 14ae2e0a8d36010d41c52e6aee4d8c60607ce1bc (patch) | |
| tree | d71212418896ac49f3cf85d5c74440acdefcc35d /core/bufio | |
| parent | 2ab6cdb98e0f08bcbc7fccd58b5d9192fda9328c (diff) | |
Add bufio.Writer and bufio.Read_Writer
Diffstat (limited to 'core/bufio')
| -rw-r--r-- | core/bufio/read_writer.odin | 68 | ||||
| -rw-r--r-- | core/bufio/writer.odin | 255 |
2 files changed, 323 insertions, 0 deletions
diff --git a/core/bufio/read_writer.odin b/core/bufio/read_writer.odin new file mode 100644 index 000000000..f60e17d2d --- /dev/null +++ b/core/bufio/read_writer.odin @@ -0,0 +1,68 @@ +package bufio + +import "core:io" + +// Read_Writer stores pointers to a Reader and a Writer +Read_Writer :: struct { + r: ^Reader, + w: ^Writer, +} + + +read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) { + rw.r, rw.w = r, w; +} + +read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) { + s.stream_data = rw; + s.stream_vtable = _read_writer_vtable; + return; +} + +@(private) +_read_writer_vtable := &io.Stream_VTable{ + impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_read(b, p); + }, + impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_read_byte(b); + }, + impl_unread_byte = proc(s: io.Stream) -> io.Error { + b := (^Read_Writer)(s.stream_data).r; + return reader_unread_byte(b); + }, + impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_read_rune(b); + }, + impl_unread_rune = proc(s: io.Stream) -> io.Error { + b := (^Read_Writer)(s.stream_data).r; + return reader_unread_rune(b); + }, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + b := (^Read_Writer)(s.stream_data).r; + return reader_write_to(b, w); + }, + impl_flush = proc(s: io.Stream) -> io.Error { + b := (^Read_Writer)(s.stream_data).w; + return writer_flush(b); + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Read_Writer)(s.stream_data).w; + return writer_write(b, p); + }, + impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { + b := (^Read_Writer)(s.stream_data).w; + return writer_write_byte(b, c); + }, + impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { + b := (^Read_Writer)(s.stream_data).w; + return writer_write_rune(b, r); + }, + impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { + b := (^Read_Writer)(s.stream_data).w; + return writer_read_from(b, r); + }, +}; diff --git a/core/bufio/writer.odin b/core/bufio/writer.odin new file mode 100644 index 000000000..97f24a224 --- /dev/null +++ b/core/bufio/writer.odin @@ -0,0 +1,255 @@ +package bufio + +import "core:io" +import "core:mem" +import "core:unicode/utf8" +// import "core:bytes" + +// Writer is a buffered wrapper for an io.Writer +Writer :: struct { + buf: []byte, + buf_allocator: mem.Allocator, + + wr: io.Writer, + n: int, + + err: io.Error, + +} + +writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) { + size := size; + size = max(size, MIN_READ_BUFFER_SIZE); + writer_reset(b, wr); + b.buf_allocator = allocator; + b.buf = make([]byte, size, allocator); +} + +writer_init_with_buf :: proc(b: ^Writer, wr: io.Writer, buf: []byte) { + writer_reset(b, wr); + b.buf_allocator = {}; + b.buf = buf; +} + +// writer_destroy destroys the underlying buffer with its associated allocator IFF that allocator has been set +writer_destroy :: proc(b: ^Writer) { + delete(b.buf, b.buf_allocator); + b^ = {}; +} + +// writer_size returns the size of underlying buffer in bytes +writer_size :: proc(b: ^Writer) -> int { + return len(b.buf); +} + +writer_reset :: proc(b: ^Writer, w: io.Writer) { + b.wr = w; + b.n = 0; + b.err = nil; +} + + +// writer_flush writes any buffered data into the underlying io.Writer +writer_flush :: proc(b: ^Writer) -> io.Error { + if b.err != nil { + return b.err; + } + if b.n == 0 { + return nil; + } + + n, err := io.write(b.wr, b.buf[0:b.n]); + if n < b.n && err == nil { + err = .Short_Write; + } + if err != nil { + if n > 0 && n < b.n { + copy(b.buf[:b.n-n], b.buf[n : b.n]); + } + b.n -= n; + b.err = err; + return err; + } + b.n = 0; + return nil; +} + +// writer_available returns how many bytes are unused in the buffer +writer_available :: proc(b: ^Writer) -> int { + return len(b.buf) - b.n; +} + +// writer_buffered returns the number of bytes that have been writted into the current buffer +writer_buffered :: proc(b: ^Writer) -> int { + return b.n; +} + +// writer_write writes the contents of p into the buffer +// It returns the number of bytes written +// If n < len(p), it will return an error explaining why the write is short +writer_write :: proc(b: ^Writer, p: []byte) -> (n: int, err: io.Error) { + p := p; + for len(p) > writer_available(b) && b.err == nil { + m: int; + if writer_buffered(b) == 0 { + m, b.err = io.write(b.wr, p); + } else { + m = copy(b.buf[b.n:], p); + b.n += m; + writer_flush(b); + } + n += m; + p = p[m:]; + } + if b.err != nil { + return n, b.err; + } + m := copy(b.buf[b.n:], p); + b.n += m; + m += n; + return m, nil; +} + +// writer_write_byte writes a single byte +writer_write_byte :: proc(b: ^Writer, c: byte) -> io.Error { + if b.err != nil { + return b.err; + } + if writer_available(b) <= 0 && writer_flush(b) != nil { + return b.err; + } + b.buf[b.n] = c; + b.n += 1; + return nil; +} + +// writer_write_rune writes a single unicode code point, and returns the number of bytes written with any error +writer_write_rune :: proc(b: ^Writer, r: rune) -> (size: int, err: io.Error) { + if r < utf8.RUNE_SELF { + err = writer_write_byte(b, byte(r)); + size = 0 if err != nil else 1; + return; + } + if b.err != nil { + return 0, b.err; + } + + buf: [4]u8; + + n := writer_available(b); + if n < utf8.UTF_MAX { + writer_flush(b); + if b.err != nil { + return 0, b.err; + } + n = writer_available(b); + if n < utf8.UTF_MAX { + // this only happens if the buffer is very small + w: int; + buf, w = utf8.encode_rune(r); + return writer_write(b, buf[:w]); + } + } + + buf, size = utf8.encode_rune(r); + copy(b.buf[b.n:], buf[:size]); + b.n += size; + return; +} + +// writer_write writes a string into the buffer +// It returns the number of bytes written +// If n < len(p), it will return an error explaining why the write is short +writer_write_string :: proc(b: ^Writer, s: string) -> (int, io.Error) { + return writer_write(b, transmute([]byte)s); +} + +// writer_read_from is to support io.Reader_From types +// If the underlying writer supports the io,read_from, and b has no buffered data yet, +// this procedure calls the underlying read_from implementation without buffering +writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) { + if b.err != nil { + return 0, b.err; + } + if writer_buffered(b) == 0 { + if w, cerr := io.to_reader_from(b.wr); cerr != nil { + n, err = io.read_from(w, r); + b.err = err; + return; + } + } + + for { + if writer_available(b) == 0 { + if ferr := writer_flush(b); ferr != nil { + return n, ferr; + } + } + m: int; + nr := 0; + for nr < MAX_CONSECUTIVE_EMPTY_READS { + m, err = io.read(r, b.buf[b.n:]); + if m != 0 || err != nil { + break; + } + nr += 1; + } + if nr == MAX_CONSECUTIVE_EMPTY_READS { + return n, .No_Progress; + } + b.n += m; + n += i64(m); + if err != nil { + break; + } + } + + if err == .EOF { + if writer_available(b) == 0 { + err = writer_flush(b); + } else { + err = nil; + } + } + return; +} + + + +// writer_to_stream converts a Writer into an io.Stream +writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) { + s.stream_data = b; + s.stream_vtable = _writer_vtable; + return; +} + + + +@(private) +_writer_vtable := &io.Stream_VTable{ + impl_destroy = proc(s: io.Stream) -> io.Error { + b := (^Writer)(s.stream_data); + writer_destroy(b); + return nil; + }, + impl_flush = proc(s: io.Stream) -> io.Error { + b := (^Writer)(s.stream_data); + return writer_flush(b); + }, + impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { + b := (^Writer)(s.stream_data); + return writer_write(b, p); + }, + impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error { + b := (^Writer)(s.stream_data); + return writer_write_byte(b, c); + }, + impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) { + b := (^Writer)(s.stream_data); + return writer_write_rune(b, r); + }, + impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { + b := (^Writer)(s.stream_data); + return writer_read_from(b, r); + }, +}; |