mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-28 17:04:34 +00:00
315 lines
7.1 KiB
Odin
315 lines
7.1 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
|
|
}
|
|
/*
|
|
Initializes a string Reader with the provided string
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
- s: The input string to be read
|
|
*/
|
|
reader_init :: proc(r: ^Reader, s: string) {
|
|
r.s = s
|
|
r.i = 0
|
|
r.prev_rune = -1
|
|
}
|
|
/*
|
|
Converts a Reader into an `io.Stream`
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
|
|
Returns:
|
|
- s: An io.Stream for the given Reader
|
|
*/
|
|
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
|
|
s.data = r
|
|
s.procedure = _reader_proc
|
|
return
|
|
}
|
|
/*
|
|
Initializes a string Reader and returns an `io.Reader` for the given string
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
- s: The input string to be read
|
|
|
|
Returns:
|
|
- res: An io.Reader for the given string
|
|
*/
|
|
to_reader :: proc(r: ^Reader, s: string) -> (res: io.Reader) {
|
|
reader_init(r, s)
|
|
rr, _ := io.to_reader(reader_to_stream(r))
|
|
return rr
|
|
}
|
|
/*
|
|
Initializes a string Reader and returns an `io.Reader_At` for the given string
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
- s: The input string to be read
|
|
|
|
Returns:
|
|
- res: An `io.Reader_At` for the given string
|
|
*/
|
|
to_reader_at :: proc(r: ^Reader, s: string) -> (res: io.Reader_At) {
|
|
reader_init(r, s)
|
|
rr, _ := io.to_reader_at(reader_to_stream(r))
|
|
return rr
|
|
}
|
|
/*
|
|
Returns the remaining length of the Reader
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
|
|
Returns:
|
|
- res: The remaining length of the Reader
|
|
*/
|
|
reader_length :: proc(r: ^Reader) -> (res: int) {
|
|
if r.i >= i64(len(r.s)) {
|
|
return 0
|
|
}
|
|
return int(i64(len(r.s)) - r.i)
|
|
}
|
|
/*
|
|
Returns the length of the string stored in the Reader
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
|
|
Returns:
|
|
- res: The length of the string stored in the Reader
|
|
*/
|
|
reader_size :: proc(r: ^Reader) -> (res: i64) {
|
|
return i64(len(r.s))
|
|
}
|
|
/*
|
|
Reads len(p) bytes from the Reader's string and copies into the provided slice.
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
- p: A byte slice to copy data into
|
|
|
|
Returns:
|
|
- n: The number of bytes read
|
|
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
|
*/
|
|
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 from the Reader's string and copies into the provided slice, at the specified offset from the current index.
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
- p: A byte slice to copy data into
|
|
- off: The offset from which to read
|
|
|
|
Returns:
|
|
- n: The number of bytes read
|
|
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
|
*/
|
|
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 from the Reader's string
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
|
|
Returns:
|
|
- The byte read from the Reader
|
|
- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
|
|
*/
|
|
reader_read_byte :: proc(r: ^Reader) -> (res: byte, err: 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
|
|
}
|
|
/*
|
|
Decrements the Reader's index (i) by 1
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
|
|
Returns:
|
|
- err: An `io.Error` if `r.i <= 0` (`.Invalid_Unread`), otherwise `nil` denotes success.
|
|
*/
|
|
reader_unread_byte :: proc(r: ^Reader) -> (err: 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 its `size` from the Reader's string
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
|
|
Returns:
|
|
- rr: The rune read from the Reader
|
|
- size: The size of the rune in bytes
|
|
- err: An `io.Error` if an error occurs while reading
|
|
*/
|
|
reader_read_rune :: proc(r: ^Reader) -> (rr: 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
|
|
}
|
|
rr, size = utf8.decode_rune_in_string(r.s[r.i:])
|
|
r.i += i64(size)
|
|
return
|
|
}
|
|
/*
|
|
Decrements the Reader's index (i) by the size of the last read rune
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
|
|
WARNING: May only be used once and after a valid `read_rune` call
|
|
|
|
Returns:
|
|
- err: An `io.Error` if an error occurs while unreading (`.Invalid_Unread`), else `nil` denotes success.
|
|
*/
|
|
reader_unread_rune :: proc(r: ^Reader) -> (err: 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's index to a new position
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
- offset: The new offset position
|
|
- whence: The reference point for the new position (`.Start`, `.Current`, or `.End`)
|
|
|
|
Returns:
|
|
- The absolute offset after seeking
|
|
- err: An `io.Error` if an error occurs while seeking (`.Invalid_Whence`, `.Invalid_Offset`)
|
|
*/
|
|
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (res: i64, err: 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 remaining content of the Reader's string into the provided `io.Writer`
|
|
|
|
Inputs:
|
|
- r: A pointer to a Reader struct
|
|
- w: The io.Writer to write the remaining content into
|
|
|
|
WARNING: Panics if writer writes more bytes than remainig length of string.
|
|
|
|
Returns:
|
|
- n: The number of bytes written
|
|
- err: An io.Error if an error occurs while writing (`.Short_Write`)
|
|
*/
|
|
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
|
|
}
|
|
/*
|
|
VTable containing implementations for various `io.Stream` methods
|
|
|
|
This VTable is used by the Reader struct to provide its functionality
|
|
as an `io.Stream`.
|
|
*/
|
|
@(private)
|
|
_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
|
r := (^Reader)(stream_data)
|
|
#partial switch mode {
|
|
case .Size:
|
|
n = reader_size(r)
|
|
return
|
|
case .Read:
|
|
return io._i64_err(reader_read(r, p))
|
|
case .Read_At:
|
|
return io._i64_err(reader_read_at(r, p, offset))
|
|
case .Seek:
|
|
n, err = reader_seek(r, offset, whence)
|
|
return
|
|
case .Query:
|
|
return io.query_utility({.Size, .Read, .Read_At, .Seek, .Query})
|
|
}
|
|
return 0, .Unsupported
|
|
}
|