aboutsummaryrefslogtreecommitdiff
path: root/core/encoding/csv/reader.odin
diff options
context:
space:
mode:
authorAndreas T Jonsson <mail@andreasjonsson.se>2024-04-25 22:04:40 +0200
committerAndreas T Jonsson <mail@andreasjonsson.se>2024-04-25 22:04:40 +0200
commit9a008d10f3d8f97ff11ba948d6939bec7e6beb6d (patch)
tree8b2fd91da22620b6e0a7158a66429f1af4aca210 /core/encoding/csv/reader.odin
parent3000508c027c9d30c168266d0ae276cc14de3982 (diff)
parentf745fff640ab1582bdfdd18a7239c58fa37db753 (diff)
Merge branch 'master' into netbsd
Diffstat (limited to 'core/encoding/csv/reader.odin')
-rw-r--r--core/encoding/csv/reader.odin26
1 files changed, 20 insertions, 6 deletions
diff --git a/core/encoding/csv/reader.odin b/core/encoding/csv/reader.odin
index 44a9fdcc4..f8c72c423 100644
--- a/core/encoding/csv/reader.odin
+++ b/core/encoding/csv/reader.odin
@@ -1,6 +1,6 @@
// package csv reads and writes comma-separated values (CSV) files.
// This package supports the format described in RFC 4180 <https://tools.ietf.org/html/rfc4180.html>
-package csv
+package encoding_csv
import "core:bufio"
import "core:bytes"
@@ -91,7 +91,10 @@ DEFAULT_RECORD_BUFFER_CAPACITY :: 256
// reader_init initializes a new Reader from r
reader_init :: proc(reader: ^Reader, r: io.Reader, buffer_allocator := context.allocator) {
- reader.comma = ','
+ switch reader.comma {
+ case '\x00', '\n', '\r', 0xfffd:
+ reader.comma = ','
+ }
context.allocator = buffer_allocator
reserve(&reader.record_buffer, DEFAULT_RECORD_BUFFER_CAPACITY)
@@ -121,6 +124,7 @@ reader_destroy :: proc(r: ^Reader) {
// read reads a single record (a slice of fields) from r
//
// All \r\n sequences are normalized to \n, including multi-line field
+@(require_results)
read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, err: Error) {
if r.reuse_record {
record, err = _read_record(r, &r.last_record, allocator)
@@ -133,6 +137,7 @@ read :: proc(r: ^Reader, allocator := context.allocator) -> (record: []string, e
}
// is_io_error checks where an Error is a specific io.Error kind
+@(require_results)
is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
if v, ok := err.(io.Error); ok {
return v == io_err
@@ -140,10 +145,10 @@ is_io_error :: proc(err: Error, io_err: io.Error) -> bool {
return false
}
-
// read_all reads all the remaining records from r.
// Each record is a slice of fields.
// read_all is defined to read until an EOF, and does not treat, and does not treat EOF as an error
+@(require_results)
read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Error) {
context.allocator = allocator
records: [dynamic][]string
@@ -153,13 +158,18 @@ read_all :: proc(r: ^Reader, allocator := context.allocator) -> ([][]string, Err
return records[:], nil
}
if rerr != nil {
- return nil, rerr
+ // allow for a partial read
+ if record != nil {
+ append(&records, record)
+ }
+ return records[:], rerr
}
append(&records, record)
}
}
// read reads a single record (a slice of fields) from the provided input.
+@(require_results)
read_from_string :: proc(input: string, record_allocator := context.allocator, buffer_allocator := context.allocator) -> (record: []string, n: int, err: Error) {
ir: strings.Reader
strings.reader_init(&ir, input)
@@ -175,6 +185,7 @@ read_from_string :: proc(input: string, record_allocator := context.allocator, b
// read_all reads all the remaining records from the provided input.
+@(require_results)
read_all_from_string :: proc(input: string, records_allocator := context.allocator, buffer_allocator := context.allocator) -> ([][]string, Error) {
ir: strings.Reader
strings.reader_init(&ir, input)
@@ -186,7 +197,7 @@ read_all_from_string :: proc(input: string, records_allocator := context.allocat
return read_all(&r, records_allocator)
}
-@private
+@(private, require_results)
is_valid_delim :: proc(r: rune) -> bool {
switch r {
case 0, '"', '\r', '\n', utf8.RUNE_ERROR:
@@ -195,8 +206,9 @@ is_valid_delim :: proc(r: rune) -> bool {
return utf8.valid_rune(r)
}
-@private
+@(private, require_results)
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
+ @(require_results)
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
if !r.multiline_fields {
line, err := bufio.reader_read_slice(&r.r, '\n')
@@ -266,6 +278,7 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
unreachable()
}
+ @(require_results)
length_newline :: proc(b: []byte) -> int {
if len(b) > 0 && b[len(b)-1] == '\n' {
return 1
@@ -273,6 +286,7 @@ _read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.all
return 0
}
+ @(require_results)
next_rune :: proc(b: []byte) -> rune {
r, _ := utf8.decode_rune(b)
return r