Files
Odin/core/strings/reader.odin

218 lines
5.2 KiB
Odin

package strings
import "core:io"
import "core:unicode/utf8"
/*
io stream data for a string reader that can read based on bytes or runes
implements the vtable when using the io.Reader variants
"read" calls advance the current reading offset `i`
*/
Reader :: struct {
s: string, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
// init the reader to the string `s`
reader_init :: proc(r: ^Reader, s: string) {
r.s = s
r.i = 0
r.prev_rune = -1
}
// returns a stream from the reader data
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
s.stream_data = r
s.stream_vtable = &_reader_vtable
return
}
// init a reader to the string `s` and return an io.Reader
to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
reader_init(r, s)
rr, _ := io.to_reader(reader_to_stream(r))
return rr
}
// init a reader to the string `s` and return an io.Reader_At
to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
reader_init(r, s)
rr, _ := io.to_reader_at(reader_to_stream(r))
return rr
}
// remaining length of the reader
reader_length :: proc(r: ^Reader) -> int {
if r.i >= i64(len(r.s)) {
return 0
}
return int(i64(len(r.s)) - r.i)
}
// returns the string length stored by the reader
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s))
}
// reads len(p) bytes into the slice from the string in the reader
// returns `n` amount of read bytes and an io.Error
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF
}
r.prev_rune = -1
n = copy(p, r.s[r.i:])
r.i += i64(n)
return
}
// reads len(p) bytes into the slice from the string in the reader at an offset
// returns `n` amount of read bytes and an io.Error
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset
}
if off >= i64(len(r.s)) {
return 0, .EOF
}
n = copy(p, r.s[off:])
if n < len(p) {
err = .EOF
}
return
}
// reads and returns a single byte - error when out of bounds
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
return 0, .EOF
}
b := r.s[r.i]
r.i += 1
return b, nil
}
// decreases the reader offset - error when below 0
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread
}
r.prev_rune = -1
r.i -= 1
return nil
}
// reads and returns a single rune and the rune size - error when out bounds
reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1
return 0, 0, .EOF
}
r.prev_rune = int(r.i)
if c := r.s[r.i]; c < utf8.RUNE_SELF {
r.i += 1
return rune(c), 1, nil
}
ch, size = utf8.decode_rune_in_string(r.s[r.i:])
r.i += i64(size)
return
}
// decreases the reader offset by the last rune
// can only be used once and after a valid read_rune call
reader_unread_rune :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread
}
if r.prev_rune < 0 {
return .Invalid_Unread
}
r.i = i64(r.prev_rune)
r.prev_rune = -1
return nil
}
// seeks the reader offset to a wanted offset
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r.prev_rune = -1
abs: i64
switch whence {
case .Start:
abs = offset
case .Current:
abs = r.i + offset
case .End:
abs = i64(len(r.s)) + offset
case:
return 0, .Invalid_Whence
}
if abs < 0 {
return 0, .Invalid_Offset
}
r.i = abs
return abs, nil
}
// writes the string content left to read into the io.Writer `w`
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
return 0, nil
}
s := r.s[r.i:]
m: int
m, err = io.write_string(w, s)
if m > len(s) {
panic("bytes.Reader.write_to: invalid io.write_string count")
}
r.i += i64(m)
n = i64(m)
if m != len(s) && err == nil {
err = .Short_Write
}
return
}
@(private)
_reader_vtable := io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
r := (^Reader)(s.stream_data)
return reader_size(r)
},
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data)
return reader_read(r, p)
},
impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
r := (^Reader)(s.stream_data)
return reader_read_at(r, p, off)
},
impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
r := (^Reader)(s.stream_data)
return reader_read_byte(r)
},
impl_unread_byte = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data)
return reader_unread_byte(r)
},
impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
r := (^Reader)(s.stream_data)
return reader_read_rune(r)
},
impl_unread_rune = proc(s: io.Stream) -> io.Error {
r := (^Reader)(s.stream_data)
return reader_unread_rune(r)
},
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r := (^Reader)(s.stream_data)
return reader_seek(r, offset, whence)
},
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
r := (^Reader)(s.stream_data)
return reader_write_to(r, w)
},
}