mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 04:50:29 +00:00
Merge branch 'odin-lang:master' into fix-omitempty-comma
This commit is contained in:
137
core/encoding/ansi/ansi.odin
Normal file
137
core/encoding/ansi/ansi.odin
Normal file
@@ -0,0 +1,137 @@
|
||||
package ansi
|
||||
|
||||
BEL :: "\a" // Bell
|
||||
BS :: "\b" // Backspace
|
||||
ESC :: "\e" // Escape
|
||||
|
||||
// Fe Escape sequences
|
||||
|
||||
CSI :: ESC + "[" // Control Sequence Introducer
|
||||
OSC :: ESC + "]" // Operating System Command
|
||||
ST :: ESC + "\\" // String Terminator
|
||||
|
||||
// CSI sequences
|
||||
|
||||
CUU :: "A" // Cursor Up
|
||||
CUD :: "B" // Cursor Down
|
||||
CUF :: "C" // Cursor Forward
|
||||
CUB :: "D" // Cursor Back
|
||||
CNL :: "E" // Cursor Next Line
|
||||
CPL :: "F" // Cursor Previous Line
|
||||
CHA :: "G" // Cursor Horizontal Absolute
|
||||
CUP :: "H" // Cursor Position
|
||||
ED :: "J" // Erase in Display
|
||||
EL :: "K" // Erase in Line
|
||||
SU :: "S" // Scroll Up
|
||||
SD :: "T" // Scroll Down
|
||||
HVP :: "f" // Horizontal Vertical Position
|
||||
SGR :: "m" // Select Graphic Rendition
|
||||
AUX_ON :: "5i" // AUX Port On
|
||||
AUX_OFF :: "4i" // AUX Port Off
|
||||
DSR :: "6n" // Device Status Report
|
||||
|
||||
// CSI: private sequences
|
||||
|
||||
SCP :: "s" // Save Current Cursor Position
|
||||
RCP :: "u" // Restore Saved Cursor Position
|
||||
DECAWM_ON :: "?7h" // Auto Wrap Mode (Enabled)
|
||||
DECAWM_OFF :: "?7l" // Auto Wrap Mode (Disabled)
|
||||
DECTCEM_SHOW :: "?25h" // Text Cursor Enable Mode (Visible)
|
||||
DECTCEM_HIDE :: "?25l" // Text Cursor Enable Mode (Invisible)
|
||||
|
||||
// SGR sequences
|
||||
|
||||
RESET :: "0"
|
||||
BOLD :: "1"
|
||||
FAINT :: "2"
|
||||
ITALIC :: "3" // Not widely supported.
|
||||
UNDERLINE :: "4"
|
||||
BLINK_SLOW :: "5"
|
||||
BLINK_RAPID :: "6" // Not widely supported.
|
||||
INVERT :: "7" // Also known as reverse video.
|
||||
HIDE :: "8" // Not widely supported.
|
||||
STRIKE :: "9"
|
||||
FONT_PRIMARY :: "10"
|
||||
FONT_ALT1 :: "11"
|
||||
FONT_ALT2 :: "12"
|
||||
FONT_ALT3 :: "13"
|
||||
FONT_ALT4 :: "14"
|
||||
FONT_ALT5 :: "15"
|
||||
FONT_ALT6 :: "16"
|
||||
FONT_ALT7 :: "17"
|
||||
FONT_ALT8 :: "18"
|
||||
FONT_ALT9 :: "19"
|
||||
FONT_FRAKTUR :: "20" // Rarely supported.
|
||||
UNDERLINE_DOUBLE :: "21" // May be interpreted as "disable bold."
|
||||
NO_BOLD_FAINT :: "22"
|
||||
NO_ITALIC_BLACKLETTER :: "23"
|
||||
NO_UNDERLINE :: "24"
|
||||
NO_BLINK :: "25"
|
||||
PROPORTIONAL_SPACING :: "26"
|
||||
NO_REVERSE :: "27"
|
||||
NO_HIDE :: "28"
|
||||
NO_STRIKE :: "29"
|
||||
|
||||
FG_BLACK :: "30"
|
||||
FG_RED :: "31"
|
||||
FG_GREEN :: "32"
|
||||
FG_YELLOW :: "33"
|
||||
FG_BLUE :: "34"
|
||||
FG_MAGENTA :: "35"
|
||||
FG_CYAN :: "36"
|
||||
FG_WHITE :: "37"
|
||||
FG_COLOR :: "38"
|
||||
FG_COLOR_8_BIT :: "38;5" // Followed by ";n" where n is in 0..=255
|
||||
FG_COLOR_24_BIT :: "38;2" // Followed by ";r;g;b" where r,g,b are in 0..=255
|
||||
FG_DEFAULT :: "39"
|
||||
|
||||
BG_BLACK :: "40"
|
||||
BG_RED :: "41"
|
||||
BG_GREEN :: "42"
|
||||
BG_YELLOW :: "43"
|
||||
BG_BLUE :: "44"
|
||||
BG_MAGENTA :: "45"
|
||||
BG_CYAN :: "46"
|
||||
BG_WHITE :: "47"
|
||||
BG_COLOR :: "48"
|
||||
BG_COLOR_8_BIT :: "48;5" // Followed by ";n" where n is in 0..=255
|
||||
BG_COLOR_24_BIT :: "48;2" // Followed by ";r;g;b" where r,g,b are in 0..=255
|
||||
BG_DEFAULT :: "49"
|
||||
|
||||
NO_PROPORTIONAL_SPACING :: "50"
|
||||
FRAMED :: "51"
|
||||
ENCIRCLED :: "52"
|
||||
OVERLINED :: "53"
|
||||
NO_FRAME_ENCIRCLE :: "54"
|
||||
NO_OVERLINE :: "55"
|
||||
|
||||
// SGR: non-standard bright colors
|
||||
|
||||
FG_BRIGHT_BLACK :: "90" // Also known as grey.
|
||||
FG_BRIGHT_RED :: "91"
|
||||
FG_BRIGHT_GREEN :: "92"
|
||||
FG_BRIGHT_YELLOW :: "93"
|
||||
FG_BRIGHT_BLUE :: "94"
|
||||
FG_BRIGHT_MAGENTA :: "95"
|
||||
FG_BRIGHT_CYAN :: "96"
|
||||
FG_BRIGHT_WHITE :: "97"
|
||||
|
||||
BG_BRIGHT_BLACK :: "100" // Also known as grey.
|
||||
BG_BRIGHT_RED :: "101"
|
||||
BG_BRIGHT_GREEN :: "102"
|
||||
BG_BRIGHT_YELLOW :: "103"
|
||||
BG_BRIGHT_BLUE :: "104"
|
||||
BG_BRIGHT_MAGENTA :: "105"
|
||||
BG_BRIGHT_CYAN :: "106"
|
||||
BG_BRIGHT_WHITE :: "107"
|
||||
|
||||
// Fp Escape sequences
|
||||
|
||||
DECSC :: ESC + "7" // DEC Save Cursor
|
||||
DECRC :: ESC + "8" // DEC Restore Cursor
|
||||
|
||||
// OSC sequences
|
||||
|
||||
WINDOW_TITLE :: "2" // Followed by ";<text>" ST.
|
||||
HYPERLINK :: "8" // Followed by ";[params];<URI>" ST. Closed by OSC HYPERLINK ";;" ST.
|
||||
CLIPBOARD :: "52" // Followed by ";c;<Base64-encoded string>" ST.
|
||||
20
core/encoding/ansi/doc.odin
Normal file
20
core/encoding/ansi/doc.odin
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
package ansi implements constant references to many widely-supported ANSI
|
||||
escape codes, primarily used in terminal emulators for enhanced graphics, such
|
||||
as colors, text styling, and animated displays.
|
||||
|
||||
For example, you can print out a line of cyan text like this:
|
||||
fmt.println(ansi.CSI + ansi.FG_CYAN + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
|
||||
|
||||
Multiple SGR (Select Graphic Rendition) codes can be joined by semicolons:
|
||||
fmt.println(ansi.CSI + ansi.BOLD + ";" + ansi.FG_BLUE + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
|
||||
|
||||
If your terminal supports 24-bit true color mode, you can also do this:
|
||||
fmt.println(ansi.CSI + ansi.FG_COLOR_24_BIT + ";0;255;255" + ansi.SGR + "Hellope!" + ansi.CSI + ansi.RESET + ansi.SGR)
|
||||
|
||||
For more information, see:
|
||||
1. https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
2. https://www.vt100.net/docs/vt102-ug/chapter5.html
|
||||
3. https://invisible-island.net/xterm/ctlseqs/ctlseqs.html
|
||||
*/
|
||||
package ansi
|
||||
@@ -320,8 +320,8 @@ to_diagnostic_format :: proc {
|
||||
|
||||
// Turns the given CBOR value into a human-readable string.
|
||||
// See docs on the proc group `diagnose` for more info.
|
||||
to_diagnostic_format_string :: proc(val: Value, padding := 0, allocator := context.allocator) -> (string, mem.Allocator_Error) #optional_allocator_error {
|
||||
b := strings.builder_make(allocator)
|
||||
to_diagnostic_format_string :: proc(val: Value, padding := 0, allocator := context.allocator, loc := #caller_location) -> (string, mem.Allocator_Error) #optional_allocator_error {
|
||||
b := strings.builder_make(allocator, loc)
|
||||
w := strings.to_stream(&b)
|
||||
err := to_diagnostic_format_writer(w, val, padding)
|
||||
if err == .EOF {
|
||||
|
||||
@@ -95,24 +95,25 @@ decode :: decode_from
|
||||
|
||||
// Decodes the given string as CBOR.
|
||||
// See docs on the proc group `decode` for more information.
|
||||
decode_from_string :: proc(s: string, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
|
||||
decode_from_string :: proc(s: string, flags: Decoder_Flags = {}, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
||||
r: strings.Reader
|
||||
strings.reader_init(&r, s)
|
||||
return decode_from_reader(strings.reader_to_stream(&r), flags, allocator)
|
||||
return decode_from_reader(strings.reader_to_stream(&r), flags, allocator, loc)
|
||||
}
|
||||
|
||||
// Reads a CBOR value from the given reader.
|
||||
// See docs on the proc group `decode` for more information.
|
||||
decode_from_reader :: proc(r: io.Reader, flags: Decoder_Flags = {}, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
|
||||
decode_from_reader :: proc(r: io.Reader, flags: Decoder_Flags = {}, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
||||
return decode_from_decoder(
|
||||
Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r },
|
||||
allocator=allocator,
|
||||
loc = loc,
|
||||
)
|
||||
}
|
||||
|
||||
// Reads a CBOR value from the given decoder.
|
||||
// See docs on the proc group `decode` for more information.
|
||||
decode_from_decoder :: proc(d: Decoder, allocator := context.allocator) -> (v: Value, err: Decode_Error) {
|
||||
decode_from_decoder :: proc(d: Decoder, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
d := d
|
||||
@@ -121,13 +122,13 @@ decode_from_decoder :: proc(d: Decoder, allocator := context.allocator) -> (v: V
|
||||
d.max_pre_alloc = DEFAULT_MAX_PRE_ALLOC
|
||||
}
|
||||
|
||||
v, err = _decode_from_decoder(d)
|
||||
v, err = _decode_from_decoder(d, {}, allocator, loc)
|
||||
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||
if err == .EOF { err = .Unexpected_EOF }
|
||||
return
|
||||
}
|
||||
|
||||
_decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0)) -> (v: Value, err: Decode_Error) {
|
||||
_decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0), allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
||||
hdr := hdr
|
||||
r := d.reader
|
||||
if hdr == Header(0) { hdr = _decode_header(r) or_return }
|
||||
@@ -161,11 +162,11 @@ _decode_from_decoder :: proc(d: Decoder, hdr: Header = Header(0)) -> (v: Value,
|
||||
switch maj {
|
||||
case .Unsigned: return _decode_tiny_u8(add)
|
||||
case .Negative: return Negative_U8(_decode_tiny_u8(add) or_return), nil
|
||||
case .Bytes: return _decode_bytes_ptr(d, add)
|
||||
case .Text: return _decode_text_ptr(d, add)
|
||||
case .Array: return _decode_array_ptr(d, add)
|
||||
case .Map: return _decode_map_ptr(d, add)
|
||||
case .Tag: return _decode_tag_ptr(d, add)
|
||||
case .Bytes: return _decode_bytes_ptr(d, add, .Bytes, allocator, loc)
|
||||
case .Text: return _decode_text_ptr(d, add, allocator, loc)
|
||||
case .Array: return _decode_array_ptr(d, add, allocator, loc)
|
||||
case .Map: return _decode_map_ptr(d, add, allocator, loc)
|
||||
case .Tag: return _decode_tag_ptr(d, add, allocator, loc)
|
||||
case .Other: return _decode_tiny_simple(add)
|
||||
case: return nil, .Bad_Major
|
||||
}
|
||||
@@ -203,27 +204,27 @@ encode :: encode_into
|
||||
|
||||
// Encodes the CBOR value into binary CBOR allocated on the given allocator.
|
||||
// See the docs on the proc group `encode_into` for more info.
|
||||
encode_into_bytes :: proc(v: Value, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (data: []byte, err: Encode_Error) {
|
||||
b := strings.builder_make(allocator) or_return
|
||||
encode_into_bytes :: proc(v: Value, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (data: []byte, err: Encode_Error) {
|
||||
b := strings.builder_make(allocator, loc) or_return
|
||||
encode_into_builder(&b, v, flags, temp_allocator) or_return
|
||||
return b.buf[:], nil
|
||||
}
|
||||
|
||||
// Encodes the CBOR value into binary CBOR written to the given builder.
|
||||
// See the docs on the proc group `encode_into` for more info.
|
||||
encode_into_builder :: proc(b: ^strings.Builder, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error {
|
||||
return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator)
|
||||
encode_into_builder :: proc(b: ^strings.Builder, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Encode_Error {
|
||||
return encode_into_writer(strings.to_stream(b), v, flags, temp_allocator, loc=loc)
|
||||
}
|
||||
|
||||
// Encodes the CBOR value into binary CBOR written to the given writer.
|
||||
// See the docs on the proc group `encode_into` for more info.
|
||||
encode_into_writer :: proc(w: io.Writer, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Encode_Error {
|
||||
return encode_into_encoder(Encoder{flags, w, temp_allocator}, v)
|
||||
encode_into_writer :: proc(w: io.Writer, v: Value, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Encode_Error {
|
||||
return encode_into_encoder(Encoder{flags, w, temp_allocator}, v, loc=loc)
|
||||
}
|
||||
|
||||
// Encodes the CBOR value into binary CBOR written to the given encoder.
|
||||
// See the docs on the proc group `encode_into` for more info.
|
||||
encode_into_encoder :: proc(e: Encoder, v: Value) -> Encode_Error {
|
||||
encode_into_encoder :: proc(e: Encoder, v: Value, loc := #caller_location) -> Encode_Error {
|
||||
e := e
|
||||
|
||||
if e.temp_allocator.procedure == nil {
|
||||
@@ -366,21 +367,21 @@ _encode_u64_exact :: proc(w: io.Writer, v: u64, major: Major = .Unsigned) -> (er
|
||||
return
|
||||
}
|
||||
|
||||
_decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes) -> (v: ^Bytes, err: Decode_Error) {
|
||||
v = new(Bytes) or_return
|
||||
defer if err != nil { free(v) }
|
||||
_decode_bytes_ptr :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator, loc := #caller_location) -> (v: ^Bytes, err: Decode_Error) {
|
||||
v = new(Bytes, allocator, loc) or_return
|
||||
defer if err != nil { free(v, allocator, loc) }
|
||||
|
||||
v^ = _decode_bytes(d, add, type) or_return
|
||||
v^ = _decode_bytes(d, add, type, allocator, loc) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator) -> (v: Bytes, err: Decode_Error) {
|
||||
_decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := context.allocator, loc := #caller_location) -> (v: Bytes, err: Decode_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
add := add
|
||||
n, scap := _decode_len_str(d, add) or_return
|
||||
|
||||
buf := strings.builder_make(0, scap) or_return
|
||||
buf := strings.builder_make(0, scap, allocator, loc) or_return
|
||||
defer if err != nil { strings.builder_destroy(&buf) }
|
||||
buf_stream := strings.to_stream(&buf)
|
||||
|
||||
@@ -426,40 +427,40 @@ _encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: En
|
||||
return
|
||||
}
|
||||
|
||||
_decode_text_ptr :: proc(d: Decoder, add: Add) -> (v: ^Text, err: Decode_Error) {
|
||||
v = new(Text) or_return
|
||||
_decode_text_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Text, err: Decode_Error) {
|
||||
v = new(Text, allocator, loc) or_return
|
||||
defer if err != nil { free(v) }
|
||||
|
||||
v^ = _decode_text(d, add) or_return
|
||||
v^ = _decode_text(d, add, allocator, loc) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator) -> (v: Text, err: Decode_Error) {
|
||||
return (Text)(_decode_bytes(d, add, .Text, allocator) or_return), nil
|
||||
_decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Text, err: Decode_Error) {
|
||||
return (Text)(_decode_bytes(d, add, .Text, allocator, loc) or_return), nil
|
||||
}
|
||||
|
||||
_encode_text :: proc(e: Encoder, val: Text) -> Encode_Error {
|
||||
return _encode_bytes(e, transmute([]byte)val, .Text)
|
||||
}
|
||||
|
||||
_decode_array_ptr :: proc(d: Decoder, add: Add) -> (v: ^Array, err: Decode_Error) {
|
||||
v = new(Array) or_return
|
||||
_decode_array_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Array, err: Decode_Error) {
|
||||
v = new(Array, allocator, loc) or_return
|
||||
defer if err != nil { free(v) }
|
||||
|
||||
v^ = _decode_array(d, add) or_return
|
||||
v^ = _decode_array(d, add, allocator, loc) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_decode_array :: proc(d: Decoder, add: Add) -> (v: Array, err: Decode_Error) {
|
||||
_decode_array :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Array, err: Decode_Error) {
|
||||
n, scap := _decode_len_container(d, add) or_return
|
||||
array := make([dynamic]Value, 0, scap) or_return
|
||||
array := make([dynamic]Value, 0, scap, allocator, loc) or_return
|
||||
defer if err != nil {
|
||||
for entry in array { destroy(entry) }
|
||||
delete(array)
|
||||
for entry in array { destroy(entry, allocator) }
|
||||
delete(array, loc)
|
||||
}
|
||||
|
||||
for i := 0; n == -1 || i < n; i += 1 {
|
||||
val, verr := _decode_from_decoder(d)
|
||||
val, verr := _decode_from_decoder(d, {}, allocator, loc)
|
||||
if n == -1 && verr == .Break {
|
||||
break
|
||||
} else if verr != nil {
|
||||
@@ -485,39 +486,39 @@ _encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_decode_map_ptr :: proc(d: Decoder, add: Add) -> (v: ^Map, err: Decode_Error) {
|
||||
v = new(Map) or_return
|
||||
_decode_map_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Map, err: Decode_Error) {
|
||||
v = new(Map, allocator, loc) or_return
|
||||
defer if err != nil { free(v) }
|
||||
|
||||
v^ = _decode_map(d, add) or_return
|
||||
v^ = _decode_map(d, add, allocator, loc) or_return
|
||||
return
|
||||
}
|
||||
|
||||
_decode_map :: proc(d: Decoder, add: Add) -> (v: Map, err: Decode_Error) {
|
||||
_decode_map :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Map, err: Decode_Error) {
|
||||
n, scap := _decode_len_container(d, add) or_return
|
||||
items := make([dynamic]Map_Entry, 0, scap) or_return
|
||||
items := make([dynamic]Map_Entry, 0, scap, allocator, loc) or_return
|
||||
defer if err != nil {
|
||||
for entry in items {
|
||||
destroy(entry.key)
|
||||
destroy(entry.value)
|
||||
}
|
||||
delete(items)
|
||||
delete(items, loc)
|
||||
}
|
||||
|
||||
for i := 0; n == -1 || i < n; i += 1 {
|
||||
key, kerr := _decode_from_decoder(d)
|
||||
key, kerr := _decode_from_decoder(d, {}, allocator, loc)
|
||||
if n == -1 && kerr == .Break {
|
||||
break
|
||||
} else if kerr != nil {
|
||||
return nil, kerr
|
||||
}
|
||||
|
||||
value := _decode_from_decoder(d) or_return
|
||||
value := _decode_from_decoder(d, {}, allocator, loc) or_return
|
||||
|
||||
append(&items, Map_Entry{
|
||||
key = key,
|
||||
value = value,
|
||||
}) or_return
|
||||
}, loc) or_return
|
||||
}
|
||||
|
||||
if .Shrink_Excess in d.flags { shrink(&items) }
|
||||
@@ -578,20 +579,20 @@ _encode_map :: proc(e: Encoder, m: Map) -> (err: Encode_Error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
_decode_tag_ptr :: proc(d: Decoder, add: Add) -> (v: Value, err: Decode_Error) {
|
||||
tag := _decode_tag(d, add) or_return
|
||||
_decode_tag_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
||||
tag := _decode_tag(d, add, allocator, loc) or_return
|
||||
if t, ok := tag.?; ok {
|
||||
defer if err != nil { destroy(t.value) }
|
||||
tp := new(Tag) or_return
|
||||
tp := new(Tag, allocator, loc) or_return
|
||||
tp^ = t
|
||||
return tp, nil
|
||||
}
|
||||
|
||||
// no error, no tag, this was the self described CBOR tag, skip it.
|
||||
return _decode_from_decoder(d)
|
||||
return _decode_from_decoder(d, {}, allocator, loc)
|
||||
}
|
||||
|
||||
_decode_tag :: proc(d: Decoder, add: Add) -> (v: Maybe(Tag), err: Decode_Error) {
|
||||
_decode_tag :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Maybe(Tag), err: Decode_Error) {
|
||||
num := _decode_uint_as_u64(d.reader, add) or_return
|
||||
|
||||
// CBOR can be wrapped in a tag that decoders can use to see/check if the binary data is CBOR.
|
||||
@@ -602,7 +603,7 @@ _decode_tag :: proc(d: Decoder, add: Add) -> (v: Maybe(Tag), err: Decode_Error)
|
||||
|
||||
t := Tag{
|
||||
number = num,
|
||||
value = _decode_from_decoder(d) or_return,
|
||||
value = _decode_from_decoder(d, {}, allocator, loc) or_return,
|
||||
}
|
||||
|
||||
if nested, ok := t.value.(^Tag); ok {
|
||||
@@ -883,4 +884,4 @@ _encode_deterministic_f64 :: proc(w: io.Writer, v: f64) -> io.Error {
|
||||
}
|
||||
|
||||
return _encode_f64_exact(w, v)
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,8 @@ marshal :: marshal_into
|
||||
|
||||
// Marshals the given value into a CBOR byte stream (allocated using the given allocator).
|
||||
// See docs on the `marshal_into` proc group for more info.
|
||||
marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (bytes: []byte, err: Marshal_Error) {
|
||||
b, alloc_err := strings.builder_make(allocator)
|
||||
marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (bytes: []byte, err: Marshal_Error) {
|
||||
b, alloc_err := strings.builder_make(allocator, loc=loc)
|
||||
// The builder as a stream also returns .EOF if it ran out of memory so this is consistent.
|
||||
if alloc_err != nil {
|
||||
return nil, .EOF
|
||||
@@ -54,7 +54,7 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
|
||||
|
||||
defer if err != nil { strings.builder_destroy(&b) }
|
||||
|
||||
if err = marshal_into_builder(&b, v, flags, temp_allocator); err != nil {
|
||||
if err = marshal_into_builder(&b, v, flags, temp_allocator, loc=loc); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -63,20 +63,20 @@ marshal_into_bytes :: proc(v: any, flags := ENCODE_SMALL, allocator := context.a
|
||||
|
||||
// Marshals the given value into a CBOR byte stream written to the given builder.
|
||||
// See docs on the `marshal_into` proc group for more info.
|
||||
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator)
|
||||
marshal_into_builder :: proc(b: ^strings.Builder, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
|
||||
return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc)
|
||||
}
|
||||
|
||||
// Marshals the given value into a CBOR byte stream written to the given writer.
|
||||
// See docs on the `marshal_into` proc group for more info.
|
||||
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error {
|
||||
marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator, loc := #caller_location) -> Marshal_Error {
|
||||
encoder := Encoder{flags, w, temp_allocator}
|
||||
return marshal_into_encoder(encoder, v)
|
||||
return marshal_into_encoder(encoder, v, loc=loc)
|
||||
}
|
||||
|
||||
// Marshals the given value into a CBOR byte stream written to the given encoder.
|
||||
// See docs on the `marshal_into` proc group for more info.
|
||||
marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) {
|
||||
marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (err: Marshal_Error) {
|
||||
e := e
|
||||
|
||||
if e.temp_allocator.procedure == nil {
|
||||
|
||||
@@ -31,8 +31,8 @@ unmarshal :: proc {
|
||||
unmarshal_from_string,
|
||||
}
|
||||
|
||||
unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
|
||||
err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator)
|
||||
unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
err = unmarshal_from_decoder(Decoder{ DEFAULT_MAX_PRE_ALLOC, flags, r }, ptr, allocator, temp_allocator, loc)
|
||||
|
||||
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||
if err == .EOF { err = .Unexpected_EOF }
|
||||
@@ -40,21 +40,21 @@ unmarshal_from_reader :: proc(r: io.Reader, ptr: ^$T, flags := Decoder_Flags{},
|
||||
}
|
||||
|
||||
// Unmarshals from a string, see docs on the proc group `Unmarshal` for more info.
|
||||
unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
|
||||
unmarshal_from_string :: proc(s: string, ptr: ^$T, flags := Decoder_Flags{}, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
sr: strings.Reader
|
||||
r := strings.to_reader(&sr, s)
|
||||
|
||||
err = unmarshal_from_reader(r, ptr, flags, allocator, temp_allocator)
|
||||
err = unmarshal_from_reader(r, ptr, flags, allocator, temp_allocator, loc)
|
||||
|
||||
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||
if err == .EOF { err = .Unexpected_EOF }
|
||||
return
|
||||
}
|
||||
|
||||
unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator) -> (err: Unmarshal_Error) {
|
||||
unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
d := d
|
||||
|
||||
err = _unmarshal_any_ptr(d, ptr, nil, allocator, temp_allocator)
|
||||
err = _unmarshal_any_ptr(d, ptr, nil, allocator, temp_allocator, loc)
|
||||
|
||||
// Normal EOF does not exist here, we try to read the exact amount that is said to be provided.
|
||||
if err == .EOF { err = .Unexpected_EOF }
|
||||
@@ -62,7 +62,7 @@ unmarshal_from_decoder :: proc(d: Decoder, ptr: ^$T, allocator := context.alloca
|
||||
|
||||
}
|
||||
|
||||
_unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocator := context.allocator, temp_allocator := context.temp_allocator) -> Unmarshal_Error {
|
||||
_unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> Unmarshal_Error {
|
||||
context.allocator = allocator
|
||||
context.temp_allocator = temp_allocator
|
||||
v := v
|
||||
@@ -78,10 +78,10 @@ _unmarshal_any_ptr :: proc(d: Decoder, v: any, hdr: Maybe(Header) = nil, allocat
|
||||
}
|
||||
|
||||
data := any{(^rawptr)(v.data)^, ti.variant.(reflect.Type_Info_Pointer).elem.id}
|
||||
return _unmarshal_value(d, data, hdr.? or_else (_decode_header(d.reader) or_return))
|
||||
return _unmarshal_value(d, data, hdr.? or_else (_decode_header(d.reader) or_return), allocator, temp_allocator, loc)
|
||||
}
|
||||
|
||||
_unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Error) {
|
||||
_unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
v := v
|
||||
ti := reflect.type_info_base(type_info_of(v.id))
|
||||
r := d.reader
|
||||
@@ -104,7 +104,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
|
||||
// Allow generic unmarshal by doing it into a `Value`.
|
||||
switch &dst in v {
|
||||
case Value:
|
||||
dst = err_conv(_decode_from_decoder(d, hdr)) or_return
|
||||
dst = err_conv(_decode_from_decoder(d, hdr, allocator, loc)) or_return
|
||||
return
|
||||
}
|
||||
|
||||
@@ -308,7 +308,7 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
|
||||
if impl, ok := _tag_implementations_nr[nr]; ok {
|
||||
return impl->unmarshal(d, nr, v)
|
||||
} else if nr == TAG_OBJECT_TYPE {
|
||||
return _unmarshal_union(d, v, ti, hdr)
|
||||
return _unmarshal_union(d, v, ti, hdr, loc=loc)
|
||||
} else {
|
||||
// Discard the tag info and unmarshal as its value.
|
||||
return _unmarshal_value(d, v, _decode_header(r) or_return)
|
||||
@@ -316,19 +316,19 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header) -> (err: Unmarshal_Err
|
||||
|
||||
return _unsupported(v, hdr, add)
|
||||
|
||||
case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add)
|
||||
case .Text: return _unmarshal_string(d, v, ti, hdr, add)
|
||||
case .Array: return _unmarshal_array(d, v, ti, hdr, add)
|
||||
case .Map: return _unmarshal_map(d, v, ti, hdr, add)
|
||||
case .Bytes: return _unmarshal_bytes(d, v, ti, hdr, add, allocator=allocator, loc=loc)
|
||||
case .Text: return _unmarshal_string(d, v, ti, hdr, add, allocator=allocator, loc=loc)
|
||||
case .Array: return _unmarshal_array(d, v, ti, hdr, add, allocator=allocator, loc=loc)
|
||||
case .Map: return _unmarshal_map(d, v, ti, hdr, add, allocator=allocator, loc=loc)
|
||||
|
||||
case: return .Bad_Major
|
||||
}
|
||||
}
|
||||
|
||||
_unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||
_unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
#partial switch t in ti.variant {
|
||||
case reflect.Type_Info_String:
|
||||
bytes := err_conv(_decode_bytes(d, add)) or_return
|
||||
bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
|
||||
|
||||
if t.is_cstring {
|
||||
raw := (^cstring)(v.data)
|
||||
@@ -347,7 +347,7 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
|
||||
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||
|
||||
bytes := err_conv(_decode_bytes(d, add)) or_return
|
||||
bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
|
||||
raw := (^mem.Raw_Slice)(v.data)
|
||||
raw^ = transmute(mem.Raw_Slice)bytes
|
||||
return
|
||||
@@ -357,12 +357,12 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
|
||||
if elem_base.id != byte { return _unsupported(v, hdr) }
|
||||
|
||||
bytes := err_conv(_decode_bytes(d, add)) or_return
|
||||
bytes := err_conv(_decode_bytes(d, add, allocator=allocator, loc=loc)) or_return
|
||||
raw := (^mem.Raw_Dynamic_Array)(v.data)
|
||||
raw.data = raw_data(bytes)
|
||||
raw.len = len(bytes)
|
||||
raw.cap = len(bytes)
|
||||
raw.allocator = context.allocator
|
||||
raw.allocator = allocator
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Array:
|
||||
@@ -385,10 +385,10 @@ _unmarshal_bytes :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
_unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||
_unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, temp_allocator := context.temp_allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
#partial switch t in ti.variant {
|
||||
case reflect.Type_Info_String:
|
||||
text := err_conv(_decode_text(d, add)) or_return
|
||||
text := err_conv(_decode_text(d, add, allocator, loc)) or_return
|
||||
|
||||
if t.is_cstring {
|
||||
raw := (^cstring)(v.data)
|
||||
@@ -403,8 +403,8 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
|
||||
|
||||
// Enum by its variant name.
|
||||
case reflect.Type_Info_Enum:
|
||||
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return
|
||||
defer delete(text, context.temp_allocator)
|
||||
text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return
|
||||
defer delete(text, temp_allocator, loc)
|
||||
|
||||
for name, i in t.names {
|
||||
if name == text {
|
||||
@@ -414,8 +414,8 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
|
||||
}
|
||||
|
||||
case reflect.Type_Info_Rune:
|
||||
text := err_conv(_decode_text(d, add, allocator=context.temp_allocator)) or_return
|
||||
defer delete(text, context.temp_allocator)
|
||||
text := err_conv(_decode_text(d, add, allocator=temp_allocator, loc=loc)) or_return
|
||||
defer delete(text, temp_allocator, loc)
|
||||
|
||||
r := (^rune)(v.data)
|
||||
dr, n := utf8.decode_rune(text)
|
||||
@@ -430,13 +430,15 @@ _unmarshal_string :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Heade
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
_unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||
_unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
assign_array :: proc(
|
||||
d: Decoder,
|
||||
da: ^mem.Raw_Dynamic_Array,
|
||||
elemt: ^reflect.Type_Info,
|
||||
length: int,
|
||||
growable := true,
|
||||
allocator := context.allocator,
|
||||
loc := #caller_location,
|
||||
) -> (out_of_space: bool, err: Unmarshal_Error) {
|
||||
for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
|
||||
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
|
||||
@@ -450,13 +452,13 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
if !growable { return true, .Out_Of_Memory }
|
||||
|
||||
cap := 2 * da.cap
|
||||
ok := runtime.__dynamic_array_reserve(da, elemt.size, elemt.align, cap)
|
||||
ok := runtime.__dynamic_array_reserve(da, elemt.size, elemt.align, cap, loc)
|
||||
|
||||
// NOTE: Might be lying here, but it is at least an allocator error.
|
||||
if !ok { return false, .Out_Of_Memory }
|
||||
}
|
||||
|
||||
err = _unmarshal_value(d, elem, hdr)
|
||||
err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
|
||||
if length == -1 && err == .Break { break }
|
||||
if err != nil { return }
|
||||
|
||||
@@ -469,10 +471,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
// Allow generically storing the values array.
|
||||
switch &dst in v {
|
||||
case ^Array:
|
||||
dst = err_conv(_decode_array_ptr(d, add)) or_return
|
||||
dst = err_conv(_decode_array_ptr(d, add, allocator=allocator, loc=loc)) or_return
|
||||
return
|
||||
case Array:
|
||||
dst = err_conv(_decode_array(d, add)) or_return
|
||||
dst = err_conv(_decode_array(d, add, allocator=allocator, loc=loc)) or_return
|
||||
return
|
||||
}
|
||||
|
||||
@@ -480,8 +482,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
case reflect.Type_Info_Slice:
|
||||
length, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||
|
||||
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return
|
||||
defer if err != nil { mem.free_bytes(data) }
|
||||
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, allocator=allocator, loc=loc) or_return
|
||||
defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
|
||||
|
||||
da := mem.Raw_Dynamic_Array{raw_data(data), 0, length, context.allocator }
|
||||
|
||||
@@ -489,7 +491,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
|
||||
if .Shrink_Excess in d.flags {
|
||||
// Ignoring an error here, but this is not critical to succeed.
|
||||
_ = runtime.__dynamic_array_shrink(&da, t.elem.size, t.elem.align, da.len)
|
||||
_ = runtime.__dynamic_array_shrink(&da, t.elem.size, t.elem.align, da.len, loc=loc)
|
||||
}
|
||||
|
||||
raw := (^mem.Raw_Slice)(v.data)
|
||||
@@ -500,8 +502,8 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
case reflect.Type_Info_Dynamic_Array:
|
||||
length, scap := err_conv(_decode_len_container(d, add)) or_return
|
||||
|
||||
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align) or_return
|
||||
defer if err != nil { mem.free_bytes(data) }
|
||||
data := mem.alloc_bytes_non_zeroed(t.elem.size * scap, t.elem.align, loc=loc) or_return
|
||||
defer if err != nil { mem.free_bytes(data, allocator=allocator, loc=loc) }
|
||||
|
||||
raw := (^mem.Raw_Dynamic_Array)(v.data)
|
||||
raw.data = raw_data(data)
|
||||
@@ -513,7 +515,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
|
||||
if .Shrink_Excess in d.flags {
|
||||
// Ignoring an error here, but this is not critical to succeed.
|
||||
_ = runtime.__dynamic_array_shrink(raw, t.elem.size, t.elem.align, raw.len)
|
||||
_ = runtime.__dynamic_array_shrink(raw, t.elem.size, t.elem.align, raw.len, loc=loc)
|
||||
}
|
||||
return
|
||||
|
||||
@@ -525,7 +527,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator }
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
|
||||
|
||||
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
|
||||
if out_of_space { return _unsupported(v, hdr) }
|
||||
@@ -539,7 +541,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, context.allocator }
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
|
||||
|
||||
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
|
||||
if out_of_space { return _unsupported(v, hdr) }
|
||||
@@ -553,7 +555,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 2, context.allocator }
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 2, allocator }
|
||||
|
||||
info: ^runtime.Type_Info
|
||||
switch ti.id {
|
||||
@@ -575,7 +577,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
return _unsupported(v, hdr)
|
||||
}
|
||||
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 4, context.allocator }
|
||||
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, 4, allocator }
|
||||
|
||||
info: ^runtime.Type_Info
|
||||
switch ti.id {
|
||||
@@ -593,17 +595,17 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
}
|
||||
}
|
||||
|
||||
_unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add) -> (err: Unmarshal_Error) {
|
||||
_unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, add: Add, allocator := context.allocator, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
r := d.reader
|
||||
decode_key :: proc(d: Decoder, v: any, allocator := context.allocator) -> (k: string, err: Unmarshal_Error) {
|
||||
decode_key :: proc(d: Decoder, v: any, allocator := context.allocator, loc := #caller_location) -> (k: string, err: Unmarshal_Error) {
|
||||
entry_hdr := _decode_header(d.reader) or_return
|
||||
entry_maj, entry_add := _header_split(entry_hdr)
|
||||
#partial switch entry_maj {
|
||||
case .Text:
|
||||
k = err_conv(_decode_text(d, entry_add, allocator)) or_return
|
||||
k = err_conv(_decode_text(d, entry_add, allocator=allocator, loc=loc)) or_return
|
||||
return
|
||||
case .Bytes:
|
||||
bytes := err_conv(_decode_bytes(d, entry_add, allocator=allocator)) or_return
|
||||
bytes := err_conv(_decode_bytes(d, entry_add, allocator=allocator, loc=loc)) or_return
|
||||
k = string(bytes)
|
||||
return
|
||||
case:
|
||||
@@ -615,10 +617,10 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
// Allow generically storing the map array.
|
||||
switch &dst in v {
|
||||
case ^Map:
|
||||
dst = err_conv(_decode_map_ptr(d, add)) or_return
|
||||
dst = err_conv(_decode_map_ptr(d, add, allocator=allocator, loc=loc)) or_return
|
||||
return
|
||||
case Map:
|
||||
dst = err_conv(_decode_map(d, add)) or_return
|
||||
dst = err_conv(_decode_map(d, add, allocator=allocator, loc=loc)) or_return
|
||||
return
|
||||
}
|
||||
|
||||
@@ -754,7 +756,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
// Unmarshal into a union, based on the `TAG_OBJECT_TYPE` tag of the spec, it denotes a tag which
|
||||
// contains an array of exactly two elements, the first is a textual representation of the following
|
||||
// CBOR value's type.
|
||||
_unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header) -> (err: Unmarshal_Error) {
|
||||
_unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, loc := #caller_location) -> (err: Unmarshal_Error) {
|
||||
r := d.reader
|
||||
#partial switch t in ti.variant {
|
||||
case reflect.Type_Info_Union:
|
||||
@@ -792,7 +794,7 @@ _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
case reflect.Type_Info_Named:
|
||||
if vti.name == target_name {
|
||||
reflect.set_union_variant_raw_tag(v, tag)
|
||||
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return)
|
||||
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return, loc=loc)
|
||||
}
|
||||
|
||||
case:
|
||||
@@ -804,7 +806,7 @@ _unmarshal_union :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
|
||||
if variant_name == target_name {
|
||||
reflect.set_union_variant_raw_tag(v, tag)
|
||||
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return)
|
||||
return _unmarshal_value(d, any{v.data, variant.id}, _decode_header(r) or_return, loc=loc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,38 +56,27 @@ CDATA_END :: "]]>"
|
||||
COMMENT_START :: "<!--"
|
||||
COMMENT_END :: "-->"
|
||||
|
||||
/*
|
||||
Default: CDATA and comments are passed through unchanged.
|
||||
*/
|
||||
// Default: CDATA and comments are passed through unchanged.
|
||||
XML_Decode_Option :: enum u8 {
|
||||
/*
|
||||
Do not decode & entities. It decodes by default.
|
||||
If given, overrides `Decode_CDATA`.
|
||||
*/
|
||||
// Do not decode & entities. It decodes by default. If given, overrides `Decode_CDATA`.
|
||||
No_Entity_Decode,
|
||||
|
||||
/*
|
||||
CDATA is unboxed.
|
||||
*/
|
||||
// CDATA is unboxed.
|
||||
Unbox_CDATA,
|
||||
|
||||
/*
|
||||
Unboxed CDATA is decoded as well.
|
||||
Ignored if `.Unbox_CDATA` is not given.
|
||||
*/
|
||||
// Unboxed CDATA is decoded as well. Ignored if `.Unbox_CDATA` is not given.
|
||||
Decode_CDATA,
|
||||
|
||||
/*
|
||||
Comments are stripped.
|
||||
*/
|
||||
// Comments are stripped.
|
||||
Comment_Strip,
|
||||
|
||||
// Normalize whitespace
|
||||
Normalize_Whitespace,
|
||||
}
|
||||
XML_Decode_Options :: bit_set[XML_Decode_Option; u8]
|
||||
|
||||
/*
|
||||
Decode a string that may include SGML/XML/HTML entities.
|
||||
The caller has to free the result.
|
||||
*/
|
||||
// Decode a string that may include SGML/XML/HTML entities.
|
||||
// The caller has to free the result.
|
||||
decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator := context.allocator) -> (decoded: string, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -100,14 +89,14 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
t := Tokenizer{src=input}
|
||||
in_data := false
|
||||
|
||||
prev: rune = ' '
|
||||
|
||||
loop: for {
|
||||
advance(&t) or_return
|
||||
if t.r < 0 { break loop }
|
||||
|
||||
/*
|
||||
Below here we're never inside a CDATA tag.
|
||||
At most we'll see the start of one, but that doesn't affect the logic.
|
||||
*/
|
||||
// Below here we're never inside a CDATA tag. At most we'll see the start of one,
|
||||
// but that doesn't affect the logic.
|
||||
switch t.r {
|
||||
case '<':
|
||||
/*
|
||||
@@ -126,9 +115,7 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
in_data = _handle_xml_special(&t, &builder, options) or_return
|
||||
|
||||
case ']':
|
||||
/*
|
||||
If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
|
||||
*/
|
||||
// If we're unboxing _and_ decoding CDATA, we'll have to check for the end tag.
|
||||
if in_data {
|
||||
if t.read_offset + len(CDATA_END) < len(t.src) {
|
||||
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
|
||||
@@ -143,22 +130,16 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
|
||||
case:
|
||||
if in_data && .Decode_CDATA not_in options {
|
||||
/*
|
||||
Unboxed, but undecoded.
|
||||
*/
|
||||
// Unboxed, but undecoded.
|
||||
write_rune(&builder, t.r)
|
||||
continue
|
||||
}
|
||||
|
||||
if t.r == '&' {
|
||||
if entity, entity_err := _extract_xml_entity(&t); entity_err != .None {
|
||||
/*
|
||||
We read to the end of the string without closing the entity.
|
||||
Pass through as-is.
|
||||
*/
|
||||
// We read to the end of the string without closing the entity. Pass through as-is.
|
||||
write_string(&builder, entity)
|
||||
} else {
|
||||
|
||||
if .No_Entity_Decode not_in options {
|
||||
if decoded, ok := xml_decode_entity(entity); ok {
|
||||
write_rune(&builder, decoded)
|
||||
@@ -166,19 +147,41 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Literal passthrough because the decode failed or we want entities not decoded.
|
||||
*/
|
||||
// Literal passthrough because the decode failed or we want entities not decoded.
|
||||
write_string(&builder, "&")
|
||||
write_string(&builder, entity)
|
||||
write_string(&builder, ";")
|
||||
}
|
||||
} else {
|
||||
write_rune(&builder, t.r)
|
||||
// Handle AV Normalization: https://www.w3.org/TR/2006/REC-xml11-20060816/#AVNormalize
|
||||
if .Normalize_Whitespace in options {
|
||||
switch t.r {
|
||||
case ' ', '\r', '\n', '\t':
|
||||
if prev != ' ' {
|
||||
write_rune(&builder, ' ')
|
||||
prev = ' '
|
||||
}
|
||||
case:
|
||||
write_rune(&builder, t.r)
|
||||
prev = t.r
|
||||
}
|
||||
} else {
|
||||
// https://www.w3.org/TR/2006/REC-xml11-20060816/#sec-line-ends
|
||||
switch t.r {
|
||||
case '\n', 0x85, 0x2028:
|
||||
write_rune(&builder, '\n')
|
||||
case '\r': // Do nothing until next character
|
||||
case:
|
||||
if prev == '\r' { // Turn a single carriage return into a \n
|
||||
write_rune(&builder, '\n')
|
||||
}
|
||||
write_rune(&builder, t.r)
|
||||
}
|
||||
prev = t.r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return strings.clone(strings.to_string(builder), allocator), err
|
||||
}
|
||||
|
||||
@@ -253,24 +256,18 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
|
||||
return rune(val), true
|
||||
|
||||
case:
|
||||
/*
|
||||
Named entity.
|
||||
*/
|
||||
// Named entity.
|
||||
return named_xml_entity_to_rune(entity)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Private XML helper to extract `&<stuff>;` entity.
|
||||
*/
|
||||
// Private XML helper to extract `&<stuff>;` entity.
|
||||
@(private="file")
|
||||
_extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
|
||||
assert(t != nil && t.r == '&')
|
||||
|
||||
/*
|
||||
All of these would be in the ASCII range.
|
||||
Even if one is not, it doesn't matter. All characters we need to compare to extract are.
|
||||
*/
|
||||
// All of these would be in the ASCII range.
|
||||
// Even if one is not, it doesn't matter. All characters we need to compare to extract are.
|
||||
|
||||
length := len(t.src)
|
||||
found := false
|
||||
@@ -292,9 +289,7 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
|
||||
return string(t.src[t.offset : t.read_offset]), .Invalid_Entity_Encoding
|
||||
}
|
||||
|
||||
/*
|
||||
Private XML helper for CDATA and comments.
|
||||
*/
|
||||
// Private XML helper for CDATA and comments.
|
||||
@(private="file")
|
||||
_handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: XML_Decode_Options) -> (in_data: bool, err: Error) {
|
||||
assert(t != nil && t.r == '<')
|
||||
@@ -304,20 +299,14 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X
|
||||
t.read_offset += len(CDATA_START) - 1
|
||||
|
||||
if .Unbox_CDATA in options && .Decode_CDATA in options {
|
||||
/*
|
||||
We're unboxing _and_ decoding CDATA
|
||||
*/
|
||||
// We're unboxing _and_ decoding CDATA
|
||||
return true, .None
|
||||
}
|
||||
|
||||
/*
|
||||
CDATA is passed through.
|
||||
*/
|
||||
// CDATA is passed through.
|
||||
offset := t.offset
|
||||
|
||||
/*
|
||||
Scan until end of CDATA.
|
||||
*/
|
||||
// Scan until end of CDATA.
|
||||
for {
|
||||
advance(t) or_return
|
||||
if t.r < 0 { return true, .CDATA_Not_Terminated }
|
||||
@@ -341,14 +330,10 @@ _handle_xml_special :: proc(t: ^Tokenizer, builder: ^strings.Builder, options: X
|
||||
|
||||
} else if string(t.src[t.offset:][:len(COMMENT_START)]) == COMMENT_START {
|
||||
t.read_offset += len(COMMENT_START)
|
||||
/*
|
||||
Comment is passed through by default.
|
||||
*/
|
||||
// Comment is passed through by default.
|
||||
offset := t.offset
|
||||
|
||||
/*
|
||||
Scan until end of Comment.
|
||||
*/
|
||||
// Scan until end of Comment.
|
||||
for {
|
||||
advance(t) or_return
|
||||
if t.r < 0 { return true, .Comment_Not_Terminated }
|
||||
|
||||
@@ -2,8 +2,8 @@ package encoding_hex
|
||||
|
||||
import "core:strings"
|
||||
|
||||
encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds_check {
|
||||
dst := make([]byte, len(src) * 2, allocator)
|
||||
encode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> []byte #no_bounds_check {
|
||||
dst := make([]byte, len(src) * 2, allocator, loc)
|
||||
for i, j := 0, 0; i < len(src); i += 1 {
|
||||
v := src[i]
|
||||
dst[j] = HEXTABLE[v>>4]
|
||||
@@ -15,12 +15,12 @@ encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds
|
||||
}
|
||||
|
||||
|
||||
decode :: proc(src: []byte, allocator := context.allocator) -> (dst: []byte, ok: bool) #no_bounds_check {
|
||||
decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (dst: []byte, ok: bool) #no_bounds_check {
|
||||
if len(src) % 2 == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
dst = make([]byte, len(src) / 2, allocator)
|
||||
dst = make([]byte, len(src) / 2, allocator, loc)
|
||||
for i, j := 0, 1; j < len(src); j += 2 {
|
||||
p := src[j-1]
|
||||
q := src[j]
|
||||
@@ -69,5 +69,4 @@ hex_digit :: proc(char: byte) -> (u8, bool) {
|
||||
case 'A' ..= 'F': return char - 'A' + 10, true
|
||||
case: return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -160,34 +160,35 @@ CONVENTION_SOFT_TRANSFORM :: "transform"
|
||||
|
||||
/* destroy procedures */
|
||||
|
||||
meta_destroy :: proc(meta: Meta, allocator := context.allocator) {
|
||||
meta_destroy :: proc(meta: Meta, allocator := context.allocator, loc := #caller_location) {
|
||||
if nested, ok := meta.value.([]Meta); ok {
|
||||
for m in nested {
|
||||
meta_destroy(m)
|
||||
meta_destroy(m, loc=loc)
|
||||
}
|
||||
delete(nested, allocator)
|
||||
delete(nested, allocator, loc=loc)
|
||||
}
|
||||
}
|
||||
nodes_destroy :: proc(nodes: []Node, allocator := context.allocator) {
|
||||
nodes_destroy :: proc(nodes: []Node, allocator := context.allocator, loc := #caller_location) {
|
||||
for node in nodes {
|
||||
for meta in node.meta_data {
|
||||
meta_destroy(meta)
|
||||
meta_destroy(meta, loc=loc)
|
||||
}
|
||||
delete(node.meta_data, allocator)
|
||||
delete(node.meta_data, allocator, loc=loc)
|
||||
|
||||
switch n in node.content {
|
||||
case Node_Geometry:
|
||||
delete(n.corner_stack, allocator)
|
||||
delete(n.edge_stack, allocator)
|
||||
delete(n.face_stack, allocator)
|
||||
delete(n.corner_stack, allocator, loc=loc)
|
||||
delete(n.vertex_stack, allocator, loc=loc)
|
||||
delete(n.edge_stack, allocator, loc=loc)
|
||||
delete(n.face_stack, allocator, loc=loc)
|
||||
case Node_Image:
|
||||
delete(n.image_stack, allocator)
|
||||
delete(n.image_stack, allocator, loc=loc)
|
||||
}
|
||||
}
|
||||
delete(nodes, allocator)
|
||||
delete(nodes, allocator, loc=loc)
|
||||
}
|
||||
|
||||
file_destroy :: proc(file: File) {
|
||||
nodes_destroy(file.nodes, file.allocator)
|
||||
delete(file.backing, file.allocator)
|
||||
}
|
||||
file_destroy :: proc(file: File, loc := #caller_location) {
|
||||
nodes_destroy(file.nodes, file.allocator, loc=loc)
|
||||
delete(file.backing, file.allocator, loc=loc)
|
||||
}
|
||||
@@ -11,24 +11,21 @@ Read_Error :: enum {
|
||||
Unable_To_Read_File,
|
||||
}
|
||||
|
||||
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
|
||||
read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
data, ok := os.read_entire_file(filename, allocator, loc)
|
||||
if !ok {
|
||||
err = .Unable_To_Read_File
|
||||
delete(data, allocator, loc)
|
||||
return
|
||||
}
|
||||
defer if !ok {
|
||||
delete(data)
|
||||
} else {
|
||||
file.backing = data
|
||||
}
|
||||
file, err = read(data, filename, print_error, allocator)
|
||||
file, err = read(data, filename, print_error, allocator, loc)
|
||||
file.backing = data
|
||||
return
|
||||
}
|
||||
|
||||
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator) -> (file: File, err: Read_Error) {
|
||||
read :: proc(data: []byte, filename := "<input>", print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) {
|
||||
Reader :: struct {
|
||||
filename: string,
|
||||
data: []byte,
|
||||
@@ -79,8 +76,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
return string(data[:len]), nil
|
||||
}
|
||||
|
||||
read_meta :: proc(r: ^Reader, capacity: u32le) -> (meta_data: []Meta, err: Read_Error) {
|
||||
meta_data = make([]Meta, int(capacity))
|
||||
read_meta :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (meta_data: []Meta, err: Read_Error) {
|
||||
meta_data = make([]Meta, int(capacity), allocator=allocator)
|
||||
count := 0
|
||||
defer meta_data = meta_data[:count]
|
||||
for &m in meta_data {
|
||||
@@ -111,10 +108,10 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
return
|
||||
}
|
||||
|
||||
read_layer_stack :: proc(r: ^Reader, capacity: u32le) -> (layers: Layer_Stack, err: Read_Error) {
|
||||
read_layer_stack :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (layers: Layer_Stack, err: Read_Error) {
|
||||
stack_count := read_value(r, u32le) or_return
|
||||
layer_count := 0
|
||||
layers = make(Layer_Stack, stack_count)
|
||||
layers = make(Layer_Stack, stack_count, allocator=allocator, loc=loc)
|
||||
defer layers = layers[:layer_count]
|
||||
for &layer in layers {
|
||||
layer.name = read_name(r) or_return
|
||||
@@ -170,7 +167,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
|
||||
node_count := 0
|
||||
file.header = header^
|
||||
file.nodes = make([]Node, header.internal_node_count)
|
||||
file.nodes = make([]Node, header.internal_node_count, allocator=allocator, loc=loc)
|
||||
file.allocator = allocator
|
||||
defer if err != nil {
|
||||
nodes_destroy(file.nodes)
|
||||
file.nodes = nil
|
||||
@@ -198,15 +196,15 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
case .Geometry:
|
||||
g: Node_Geometry
|
||||
|
||||
g.vertex_count = read_value(r, u32le) or_return
|
||||
g.vertex_stack = read_layer_stack(r, g.vertex_count) or_return
|
||||
g.edge_corner_count = read_value(r, u32le) or_return
|
||||
g.corner_stack = read_layer_stack(r, g.edge_corner_count) or_return
|
||||
g.vertex_count = read_value(r, u32le) or_return
|
||||
g.vertex_stack = read_layer_stack(r, g.vertex_count, loc=loc) or_return
|
||||
g.edge_corner_count = read_value(r, u32le) or_return
|
||||
g.corner_stack = read_layer_stack(r, g.edge_corner_count, loc=loc) or_return
|
||||
if header.version > 2 {
|
||||
g.edge_stack = read_layer_stack(r, g.edge_corner_count) or_return
|
||||
g.edge_stack = read_layer_stack(r, g.edge_corner_count, loc=loc) or_return
|
||||
}
|
||||
g.face_count = read_value(r, u32le) or_return
|
||||
g.face_stack = read_layer_stack(r, g.face_count) or_return
|
||||
g.face_count = read_value(r, u32le) or_return
|
||||
g.face_stack = read_layer_stack(r, g.face_count, loc=loc) or_return
|
||||
|
||||
node.content = g
|
||||
|
||||
@@ -233,4 +231,4 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
189
core/encoding/ini/ini.odin
Normal file
189
core/encoding/ini/ini.odin
Normal file
@@ -0,0 +1,189 @@
|
||||
package encoding_ini
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:io"
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
_ :: fmt
|
||||
|
||||
Options :: struct {
|
||||
comment: string,
|
||||
key_lower_case: bool,
|
||||
}
|
||||
|
||||
DEFAULT_OPTIONS :: Options {
|
||||
comment = ";",
|
||||
key_lower_case = false,
|
||||
}
|
||||
|
||||
Iterator :: struct {
|
||||
section: string,
|
||||
_src: string,
|
||||
options: Options,
|
||||
}
|
||||
|
||||
iterator_from_string :: proc(src: string, options := DEFAULT_OPTIONS) -> Iterator {
|
||||
return {
|
||||
section = "",
|
||||
options = options,
|
||||
_src = src,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Returns the raw `key` and `value`. `ok` will be false if no more key=value pairs cannot be found.
|
||||
// They key and value may be quoted, which may require the use of `strconv.unquote_string`.
|
||||
iterate :: proc(it: ^Iterator) -> (key, value: string, ok: bool) {
|
||||
for line_ in strings.split_lines_iterator(&it._src) {
|
||||
line := strings.trim_space(line_)
|
||||
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if line[0] == '[' {
|
||||
end_idx := strings.index_byte(line, ']')
|
||||
if end_idx < 0 {
|
||||
end_idx = len(line)
|
||||
}
|
||||
it.section = line[1:end_idx]
|
||||
continue
|
||||
}
|
||||
|
||||
if it.options.comment != "" && strings.has_prefix(line, it.options.comment) {
|
||||
continue
|
||||
}
|
||||
|
||||
equal := strings.index(line, " =") // check for things keys that `ctrl+= = zoom_in`
|
||||
quote := strings.index_byte(line, '"')
|
||||
if equal < 0 || quote > 0 && quote < equal {
|
||||
equal = strings.index_byte(line, '=')
|
||||
if equal < 0 {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
equal += 1
|
||||
}
|
||||
|
||||
key = strings.trim_space(line[:equal])
|
||||
value = strings.trim_space(line[equal+1:])
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
it.section = ""
|
||||
return
|
||||
}
|
||||
|
||||
Map :: distinct map[string]map[string]string
|
||||
|
||||
load_map_from_string :: proc(src: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error) {
|
||||
unquote :: proc(val: string) -> (string, runtime.Allocator_Error) {
|
||||
v, allocated, ok := strconv.unquote_string(val)
|
||||
if !ok {
|
||||
return strings.clone(val)
|
||||
}
|
||||
if allocated {
|
||||
return v, nil
|
||||
}
|
||||
return strings.clone(v)
|
||||
|
||||
}
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
it := iterator_from_string(src, options)
|
||||
|
||||
for key, value in iterate(&it) {
|
||||
section := it.section
|
||||
if section not_in m {
|
||||
section = strings.clone(section) or_return
|
||||
m[section] = {}
|
||||
}
|
||||
|
||||
// store key-value pair
|
||||
pairs := &m[section]
|
||||
new_key := unquote(key) or_return
|
||||
if options.key_lower_case {
|
||||
old_key := new_key
|
||||
new_key = strings.to_lower(key) or_return
|
||||
delete(old_key) or_return
|
||||
}
|
||||
pairs[new_key] = unquote(value) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error, ok: bool) {
|
||||
data := os.read_entire_file(path, allocator) or_return
|
||||
defer delete(data, allocator)
|
||||
m, err = load_map_from_string(string(data), allocator, options)
|
||||
ok = err != nil
|
||||
defer if !ok {
|
||||
delete_map(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
save_map_to_string :: proc(m: Map, allocator: runtime.Allocator) -> (data: string) {
|
||||
b := strings.builder_make(allocator)
|
||||
_, _ = write_map(strings.to_writer(&b), m)
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
delete_map :: proc(m: Map) {
|
||||
allocator := m.allocator
|
||||
for section, pairs in m {
|
||||
for key, value in pairs {
|
||||
delete(key, allocator)
|
||||
delete(value, allocator)
|
||||
}
|
||||
delete(section)
|
||||
}
|
||||
delete(m)
|
||||
}
|
||||
|
||||
write_section :: proc(w: io.Writer, name: string, n_written: ^int = nil) -> (n: int, err: io.Error) {
|
||||
defer if n_written != nil { n_written^ += n }
|
||||
io.write_byte (w, '[', &n) or_return
|
||||
io.write_string(w, name, &n) or_return
|
||||
io.write_byte (w, ']', &n) or_return
|
||||
return
|
||||
}
|
||||
|
||||
write_pair :: proc(w: io.Writer, key: string, value: $T, n_written: ^int = nil) -> (n: int, err: io.Error) {
|
||||
defer if n_written != nil { n_written^ += n }
|
||||
io.write_string(w, key, &n) or_return
|
||||
io.write_string(w, " = ", &n) or_return
|
||||
when intrinsics.type_is_string(T) {
|
||||
val := string(value)
|
||||
if len(val) > 0 && (val[0] == ' ' || val[len(val)-1] == ' ') {
|
||||
io.write_quoted_string(w, val, n_written=&n) or_return
|
||||
} else {
|
||||
io.write_string(w, val, &n) or_return
|
||||
}
|
||||
} else {
|
||||
n += fmt.wprint(w, value)
|
||||
}
|
||||
io.write_byte(w, '\n', &n) or_return
|
||||
return
|
||||
}
|
||||
|
||||
write_map :: proc(w: io.Writer, m: Map) -> (n: int, err: io.Error) {
|
||||
section_index := 0
|
||||
for section, pairs in m {
|
||||
if section_index == 0 && section == "" {
|
||||
// ignore section
|
||||
} else {
|
||||
write_section(w, section, &n) or_return
|
||||
}
|
||||
for key, value in pairs {
|
||||
write_pair(w, key, value, &n) or_return
|
||||
}
|
||||
section_index += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -62,8 +62,8 @@ Marshal_Options :: struct {
|
||||
mjson_skipped_first_braces_end: bool,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator)
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator, loc)
|
||||
defer if err != nil {
|
||||
strings.builder_destroy(&b)
|
||||
}
|
||||
|
||||
@@ -28,27 +28,27 @@ make_parser_from_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, par
|
||||
}
|
||||
|
||||
|
||||
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
|
||||
return parse_string(string(data), spec, parse_integers, allocator)
|
||||
parse :: proc(data: []byte, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
|
||||
return parse_string(string(data), spec, parse_integers, allocator, loc)
|
||||
}
|
||||
|
||||
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator) -> (Value, Error) {
|
||||
parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers := false, allocator := context.allocator, loc := #caller_location) -> (Value, Error) {
|
||||
context.allocator = allocator
|
||||
p := make_parser_from_string(data, spec, parse_integers, allocator)
|
||||
|
||||
switch p.spec {
|
||||
case .JSON:
|
||||
return parse_object(&p)
|
||||
return parse_object(&p, loc)
|
||||
case .JSON5:
|
||||
return parse_value(&p)
|
||||
return parse_value(&p, loc)
|
||||
case .SJSON:
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Ident, .String:
|
||||
return parse_object_body(&p, .EOF)
|
||||
return parse_object_body(&p, .EOF, loc)
|
||||
}
|
||||
return parse_value(&p)
|
||||
return parse_value(&p, loc)
|
||||
}
|
||||
return parse_object(&p)
|
||||
return parse_object(&p, loc)
|
||||
}
|
||||
|
||||
token_end_pos :: proc(tok: Token) -> Pos {
|
||||
@@ -106,7 +106,7 @@ parse_comma :: proc(p: ^Parser) -> (do_break: bool) {
|
||||
return false
|
||||
}
|
||||
|
||||
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
parse_value :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
|
||||
err = .None
|
||||
token := p.curr_token
|
||||
#partial switch token.kind {
|
||||
@@ -142,13 +142,13 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
|
||||
case .String:
|
||||
advance_token(p)
|
||||
return unquote_string(token, p.spec, p.allocator)
|
||||
return unquote_string(token, p.spec, p.allocator, loc)
|
||||
|
||||
case .Open_Brace:
|
||||
return parse_object(p)
|
||||
return parse_object(p, loc)
|
||||
|
||||
case .Open_Bracket:
|
||||
return parse_array(p)
|
||||
return parse_array(p, loc)
|
||||
|
||||
case:
|
||||
if p.spec != .JSON {
|
||||
@@ -176,7 +176,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
parse_array :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
|
||||
err = .None
|
||||
expect_token(p, .Open_Bracket) or_return
|
||||
|
||||
@@ -184,14 +184,14 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
array.allocator = p.allocator
|
||||
defer if err != nil {
|
||||
for elem in array {
|
||||
destroy_value(elem)
|
||||
destroy_value(elem, loc=loc)
|
||||
}
|
||||
delete(array)
|
||||
delete(array, loc)
|
||||
}
|
||||
|
||||
for p.curr_token.kind != .Close_Bracket {
|
||||
elem := parse_value(p) or_return
|
||||
append(&array, elem)
|
||||
elem := parse_value(p, loc) or_return
|
||||
append(&array, elem, loc)
|
||||
|
||||
if parse_comma(p) {
|
||||
break
|
||||
@@ -228,38 +228,39 @@ clone_string :: proc(s: string, allocator: mem.Allocator, loc := #caller_locatio
|
||||
return
|
||||
}
|
||||
|
||||
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator) -> (key: string, err: Error) {
|
||||
parse_object_key :: proc(p: ^Parser, key_allocator: mem.Allocator, loc := #caller_location) -> (key: string, err: Error) {
|
||||
tok := p.curr_token
|
||||
if p.spec != .JSON {
|
||||
if allow_token(p, .Ident) {
|
||||
return clone_string(tok.text, key_allocator)
|
||||
return clone_string(tok.text, key_allocator, loc)
|
||||
}
|
||||
}
|
||||
if tok_err := expect_token(p, .String); tok_err != nil {
|
||||
err = .Expected_String_For_Object_Key
|
||||
return
|
||||
}
|
||||
return unquote_string(tok, p.spec, key_allocator)
|
||||
return unquote_string(tok, p.spec, key_allocator, loc)
|
||||
}
|
||||
|
||||
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, err: Error) {
|
||||
obj.allocator = p.allocator
|
||||
parse_object_body :: proc(p: ^Parser, end_token: Token_Kind, loc := #caller_location) -> (obj: Object, err: Error) {
|
||||
obj = make(Object, allocator=p.allocator, loc=loc)
|
||||
|
||||
defer if err != nil {
|
||||
for key, elem in obj {
|
||||
delete(key, p.allocator)
|
||||
destroy_value(elem)
|
||||
delete(key, p.allocator, loc)
|
||||
destroy_value(elem, loc=loc)
|
||||
}
|
||||
delete(obj)
|
||||
delete(obj, loc)
|
||||
}
|
||||
|
||||
for p.curr_token.kind != end_token {
|
||||
key := parse_object_key(p, p.allocator) or_return
|
||||
key := parse_object_key(p, p.allocator, loc) or_return
|
||||
parse_colon(p) or_return
|
||||
elem := parse_value(p) or_return
|
||||
elem := parse_value(p, loc) or_return
|
||||
|
||||
if key in obj {
|
||||
err = .Duplicate_Object_Key
|
||||
delete(key, p.allocator)
|
||||
delete(key, p.allocator, loc)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -267,7 +268,7 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
|
||||
// inserting empty key/values into the object and for those we do not
|
||||
// want to allocate anything
|
||||
if key != "" {
|
||||
reserve_error := reserve(&obj, len(obj) + 1)
|
||||
reserve_error := reserve(&obj, len(obj) + 1, loc)
|
||||
if reserve_error == mem.Allocator_Error.Out_Of_Memory {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
@@ -281,9 +282,9 @@ parse_object_body :: proc(p: ^Parser, end_token: Token_Kind) -> (obj: Object, er
|
||||
return obj, .None
|
||||
}
|
||||
|
||||
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
parse_object :: proc(p: ^Parser, loc := #caller_location) -> (value: Value, err: Error) {
|
||||
expect_token(p, .Open_Brace) or_return
|
||||
obj := parse_object_body(p, .Close_Brace) or_return
|
||||
obj := parse_object_body(p, .Close_Brace, loc) or_return
|
||||
expect_token(p, .Close_Brace) or_return
|
||||
return obj, .None
|
||||
}
|
||||
@@ -480,4 +481,4 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
}
|
||||
|
||||
return string(b[:w]), nil
|
||||
}
|
||||
}
|
||||
@@ -89,22 +89,22 @@ Error :: enum {
|
||||
|
||||
|
||||
|
||||
destroy_value :: proc(value: Value, allocator := context.allocator) {
|
||||
destroy_value :: proc(value: Value, allocator := context.allocator, loc := #caller_location) {
|
||||
context.allocator = allocator
|
||||
#partial switch v in value {
|
||||
case Object:
|
||||
for key, elem in v {
|
||||
delete(key)
|
||||
destroy_value(elem)
|
||||
delete(key, loc=loc)
|
||||
destroy_value(elem, loc=loc)
|
||||
}
|
||||
delete(v)
|
||||
delete(v, loc=loc)
|
||||
case Array:
|
||||
for elem in v {
|
||||
destroy_value(elem)
|
||||
destroy_value(elem, loc=loc)
|
||||
}
|
||||
delete(v)
|
||||
delete(v, loc=loc)
|
||||
case String:
|
||||
delete(v)
|
||||
delete(v, loc=loc)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -218,9 +218,7 @@ scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
for is_valid_identifier_rune(t.ch) {
|
||||
advance_rune(t)
|
||||
if t.ch == ':' {
|
||||
/*
|
||||
A namespaced attr can have at most two parts, `namespace:ident`.
|
||||
*/
|
||||
// A namespaced attr can have at most two parts, `namespace:ident`.
|
||||
if namespaced {
|
||||
break
|
||||
}
|
||||
@@ -268,14 +266,10 @@ scan_comment :: proc(t: ^Tokenizer) -> (comment: string, err: Error) {
|
||||
return string(t.src[offset : t.offset - 1]), .None
|
||||
}
|
||||
|
||||
/*
|
||||
Skip CDATA
|
||||
*/
|
||||
// Skip CDATA
|
||||
skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||
if t.read_offset + len(CDATA_START) >= len(t.src) {
|
||||
/*
|
||||
Can't be the start of a CDATA tag.
|
||||
*/
|
||||
// Can't be the start of a CDATA tag.
|
||||
return .None
|
||||
}
|
||||
|
||||
@@ -290,9 +284,7 @@ skip_cdata :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||
return .Premature_EOF
|
||||
}
|
||||
|
||||
/*
|
||||
Scan until the end of a CDATA tag.
|
||||
*/
|
||||
// Scan until the end of a CDATA tag.
|
||||
if t.read_offset + len(CDATA_END) < len(t.src) {
|
||||
if string(t.src[t.offset:][:len(CDATA_END)]) == CDATA_END {
|
||||
t.read_offset += len(CDATA_END)
|
||||
@@ -319,14 +311,10 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
|
||||
case '<':
|
||||
if peek_byte(t) == '!' {
|
||||
if peek_byte(t, 1) == '[' {
|
||||
/*
|
||||
Might be the start of a CDATA tag.
|
||||
*/
|
||||
// Might be the start of a CDATA tag.
|
||||
skip_cdata(t) or_return
|
||||
} else if peek_byte(t, 1) == '-' && peek_byte(t, 2) == '-' {
|
||||
/*
|
||||
Comment start. Eat comment.
|
||||
*/
|
||||
// Comment start. Eat comment.
|
||||
t.read_offset += 3
|
||||
_ = scan_comment(t) or_return
|
||||
}
|
||||
@@ -342,17 +330,13 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
|
||||
}
|
||||
|
||||
if t.ch == close {
|
||||
/*
|
||||
If it's not a CDATA or comment, it's the end of this body.
|
||||
*/
|
||||
// If it's not a CDATA or comment, it's the end of this body.
|
||||
break loop
|
||||
}
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
/*
|
||||
Strip trailing whitespace.
|
||||
*/
|
||||
// Strip trailing whitespace.
|
||||
lit := string(t.src[offset : t.offset])
|
||||
|
||||
end := len(lit)
|
||||
@@ -369,11 +353,6 @@ scan_string :: proc(t: ^Tokenizer, offset: int, close: rune = '<', consume_close
|
||||
if consume_close {
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Handle decoding escape characters and unboxing CDATA.
|
||||
*/
|
||||
|
||||
return lit, err
|
||||
}
|
||||
|
||||
@@ -384,7 +363,7 @@ peek :: proc(t: ^Tokenizer) -> (token: Token) {
|
||||
return token
|
||||
}
|
||||
|
||||
scan :: proc(t: ^Tokenizer) -> Token {
|
||||
scan :: proc(t: ^Tokenizer, multiline_string := false) -> Token {
|
||||
skip_whitespace(t)
|
||||
|
||||
offset := t.offset
|
||||
@@ -418,7 +397,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
case '"', '\'':
|
||||
kind = .Invalid
|
||||
|
||||
lit, err = scan_string(t, t.offset, ch, true, false)
|
||||
lit, err = scan_string(t, t.offset, ch, true, multiline_string)
|
||||
if err == .None {
|
||||
kind = .String
|
||||
}
|
||||
@@ -435,4 +414,4 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
lit = string(t.src[offset : t.offset])
|
||||
}
|
||||
return Token{kind, lit, pos}
|
||||
}
|
||||
}
|
||||
@@ -203,9 +203,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
|
||||
doc.elements = make([dynamic]Element, 1024, 1024, allocator)
|
||||
|
||||
// strings.intern_init(&doc.intern, allocator, allocator)
|
||||
|
||||
err = .Unexpected_Token
|
||||
err = .Unexpected_Token
|
||||
element, parent: Element_ID
|
||||
open: Token
|
||||
|
||||
@@ -259,8 +257,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
case .Slash:
|
||||
// Empty tag. Close it.
|
||||
expect(t, .Gt) or_return
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
|
||||
case:
|
||||
error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
|
||||
@@ -276,8 +274,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
error(t, t.offset, "Mismatched Closing Tag. Expected %v, got %v\n", doc.elements[element].ident, ident.text)
|
||||
return doc, .Mismatched_Closing_Tag
|
||||
}
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
|
||||
} else if open.kind == .Exclaim {
|
||||
// <!
|
||||
@@ -463,8 +461,8 @@ validate_options :: proc(options: Options) -> (validated: Options, err: Error) {
|
||||
return validated, .None
|
||||
}
|
||||
|
||||
expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) {
|
||||
tok = scan(t)
|
||||
expect :: proc(t: ^Tokenizer, kind: Token_Kind, multiline_string := false) -> (tok: Token, err: Error) {
|
||||
tok = scan(t, multiline_string=multiline_string)
|
||||
if tok.kind == kind { return tok, .None }
|
||||
|
||||
error(t, t.offset, "Expected \"%v\", got \"%v\".", kind, tok.kind)
|
||||
@@ -480,7 +478,13 @@ parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: E
|
||||
offset = t.offset - len(key.text)
|
||||
|
||||
_ = expect(t, .Eq) or_return
|
||||
value := expect(t, .String) or_return
|
||||
value := expect(t, .String, multiline_string=true) or_return
|
||||
|
||||
normalized, normalize_err := entity.decode_xml(value.text, {.Normalize_Whitespace}, doc.allocator)
|
||||
if normalize_err == .None {
|
||||
append(&doc.strings_to_free, normalized)
|
||||
value.text = normalized
|
||||
}
|
||||
|
||||
attr.key = key.text
|
||||
attr.val = value.text
|
||||
|
||||
Reference in New Issue
Block a user