123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- 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,
- max_consecutive_empty_writes: int,
- }
- 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
- }
- for {
- if writer_available(b) == 0 {
- writer_flush(b) or_return
- }
- if b.max_consecutive_empty_writes <= 0 {
- b.max_consecutive_empty_writes = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS
- }
- m: int
- nr := 0
- for nr < b.max_consecutive_empty_writes {
- m, err = io.read(r, b.buf[b.n:])
- if m != 0 || err != nil {
- break
- }
- nr += 1
- }
- if nr == b.max_consecutive_empty_writes {
- 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.data = b
- s.procedure = _writer_proc
- return
- }
- // writer_to_stream converts a Writer into an io.Stream
- writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) {
- return writer_to_stream(b)
- }
- @(private)
- _writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
- b := (^Writer)(stream_data)
- #partial switch mode {
- case .Flush:
- err = writer_flush(b)
- return
- case .Write:
- n_int: int
- n_int, err = writer_write(b, p)
- n = i64(n_int)
- return
- case .Destroy:
- writer_destroy(b)
- return
- case .Query:
- return io.query_utility({.Flush, .Write, .Destroy, .Query})
- }
- return 0, .Empty
- }
|