123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589 |
- package json
- import "core:mem"
- import "core:math"
- import "core:reflect"
- import "core:strconv"
- import "core:strings"
- import "core:runtime"
- Unmarshal_Data_Error :: enum {
- Invalid_Data,
- Invalid_Parameter,
- Non_Pointer_Parameter,
- Multiple_Use_Field,
- }
- Unsupported_Type_Error :: struct {
- id: typeid,
- token: Token,
- }
- Unmarshal_Error :: union {
- Error,
- Unmarshal_Data_Error,
- Unsupported_Type_Error,
- }
- unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, allocator := context.allocator) -> Unmarshal_Error {
- v := v
- if v == nil || v.id == nil {
- return .Invalid_Parameter
- }
- v = reflect.any_base(v)
- ti := type_info_of(v.id)
- if !reflect.is_pointer(ti) || ti.id == rawptr {
- return .Non_Pointer_Parameter
- }
- PARSE_INTEGERS :: true
-
- if !is_valid(data, spec, PARSE_INTEGERS) {
- return .Invalid_Data
- }
- p := make_parser(data, spec, PARSE_INTEGERS, allocator)
-
- data := any{(^rawptr)(v.data)^, ti.variant.(reflect.Type_Info_Pointer).elem.id}
- if v.data == nil {
- return .Invalid_Parameter
- }
-
- context.allocator = p.allocator
-
- if p.spec == .MJSON {
- #partial switch p.curr_token.kind {
- case .Ident, .String:
- return unmarshal_object(&p, data, .EOF)
- }
- }
- return unmarshal_value(&p, data)
- }
- unmarshal :: proc(data: []byte, ptr: ^$T, spec := DEFAULT_SPECIFICATION, allocator := context.allocator) -> Unmarshal_Error {
- return unmarshal_any(data, ptr, spec, allocator)
- }
- unmarshal_string :: proc(data: string, ptr: ^$T, spec := DEFAULT_SPECIFICATION, allocator := context.allocator) -> Unmarshal_Error {
- return unmarshal_any(transmute([]byte)data, ptr, spec, allocator)
- }
- @(private)
- assign_bool :: proc(val: any, b: bool) -> bool {
- v := reflect.any_core(val)
- switch &dst in v {
- case bool: dst = bool(b)
- case b8: dst = b8 (b)
- case b16: dst = b16 (b)
- case b32: dst = b32 (b)
- case b64: dst = b64 (b)
- case: return false
- }
- return true
- }
- @(private)
- assign_int :: proc(val: any, i: $T) -> bool {
- v := reflect.any_core(val)
- switch &dst in v {
- case i8: dst = i8 (i)
- case i16: dst = i16 (i)
- case i16le: dst = i16le (i)
- case i16be: dst = i16be (i)
- case i32: dst = i32 (i)
- case i32le: dst = i32le (i)
- case i32be: dst = i32be (i)
- case i64: dst = i64 (i)
- case i64le: dst = i64le (i)
- case i64be: dst = i64be (i)
- case i128: dst = i128 (i)
- case i128le: dst = i128le (i)
- case i128be: dst = i128be (i)
- case u8: dst = u8 (i)
- case u16: dst = u16 (i)
- case u16le: dst = u16le (i)
- case u16be: dst = u16be (i)
- case u32: dst = u32 (i)
- case u32le: dst = u32le (i)
- case u32be: dst = u32be (i)
- case u64: dst = u64 (i)
- case u64le: dst = u64le (i)
- case u64be: dst = u64be (i)
- case u128: dst = u128 (i)
- case u128le: dst = u128le (i)
- case u128be: dst = u128be (i)
- case int: dst = int (i)
- case uint: dst = uint (i)
- case uintptr: dst = uintptr(i)
- case: return false
- }
- return true
- }
- @(private)
- assign_float :: proc(val: any, f: $T) -> bool {
- v := reflect.any_core(val)
- switch &dst in v {
- case f16: dst = f16 (f)
- case f16le: dst = f16le(f)
- case f16be: dst = f16be(f)
- case f32: dst = f32 (f)
- case f32le: dst = f32le(f)
- case f32be: dst = f32be(f)
- case f64: dst = f64 (f)
- case f64le: dst = f64le(f)
- case f64be: dst = f64be(f)
-
- case complex32: dst = complex(f16(f), 0)
- case complex64: dst = complex(f32(f), 0)
- case complex128: dst = complex(f64(f), 0)
-
- case quaternion64: dst = quaternion(f16(f), 0, 0, 0)
- case quaternion128: dst = quaternion(f32(f), 0, 0, 0)
- case quaternion256: dst = quaternion(f64(f), 0, 0, 0)
-
- case: return false
- }
- return true
- }
- @(private)
- unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
- val := val
- switch &dst in val {
- case string:
- dst = str
- return true
- case cstring:
- if str == "" {
- dst = strings.clone_to_cstring("", p.allocator)
- } else {
- // NOTE: This is valid because 'clone_string' appends a NUL terminator
- dst = cstring(raw_data(str))
- }
- return true
- }
-
- #partial switch variant in ti.variant {
- case reflect.Type_Info_Enum:
- for name, i in variant.names {
- if name == str {
- assign_int(val, variant.values[i])
- return true
- }
- }
- // TODO(bill): should this be an error or not?
- return true
-
- case reflect.Type_Info_Integer:
- i := strconv.parse_i128(str) or_return
- if assign_int(val, i) {
- return true
- }
- if assign_float(val, i) {
- return true
- }
- case reflect.Type_Info_Float:
- f := strconv.parse_f64(str) or_return
- if assign_int(val, f) {
- return true
- }
- if assign_float(val, f) {
- return true
- }
- }
-
- return false
- }
- @(private)
- unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
- UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
- token := p.curr_token
-
- v := v
- ti := reflect.type_info_base(type_info_of(v.id))
- // NOTE: If it's a union with only one variant, then treat it as that variant
- if u, ok := ti.variant.(reflect.Type_Info_Union); ok && len(u.variants) == 1 && token.kind != .Null {
- variant := u.variants[0]
- v.id = variant.id
- ti = reflect.type_info_base(variant)
- if !reflect.is_pointer_internally(variant) {
- tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
- assign_int(tag, 1)
- }
- }
-
- switch &dst in v {
- // Handle json.Value as an unknown type
- case Value:
- dst = parse_value(p) or_return
- return
- }
-
- #partial switch token.kind {
- case .Null:
- mem.zero(v.data, ti.size)
- advance_token(p)
- return
- case .False, .True:
- advance_token(p)
- if assign_bool(v, token.kind == .True) {
- return
- }
- return UNSUPPORTED_TYPE
- case .Integer:
- advance_token(p)
- i, _ := strconv.parse_i128(token.text)
- if assign_int(v, i) {
- return
- }
- if assign_float(v, i) {
- return
- }
- return UNSUPPORTED_TYPE
- case .Float:
- advance_token(p)
- f, _ := strconv.parse_f64(token.text)
- if assign_float(v, f) {
- return
- }
- if i, fract := math.modf(f); fract == 0 {
- if assign_int(v, i) {
- return
- }
- if assign_float(v, i) {
- return
- }
- }
- return UNSUPPORTED_TYPE
-
- case .Ident:
- advance_token(p)
- if p.spec == .MJSON {
- if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
- return nil
- }
- }
- return UNSUPPORTED_TYPE
-
- case .String:
- advance_token(p)
- str := unquote_string(token, p.spec, p.allocator) or_return
- if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
- return nil
- }
- delete(str, p.allocator)
- return UNSUPPORTED_TYPE
- case .Open_Brace:
- return unmarshal_object(p, v, .Close_Brace)
- case .Open_Bracket:
- return unmarshal_array(p, v)
- case:
- if p.spec != .JSON {
- #partial switch token.kind {
- case .Infinity:
- advance_token(p)
- f: f64 = 0h7ff0000000000000
- if token.text[0] == '-' {
- f = 0hfff0000000000000
- }
- if assign_float(v, f) {
- return
- }
- return UNSUPPORTED_TYPE
- case .NaN:
- advance_token(p)
- f: f64 = 0h7ff7ffffffffffff
- if token.text[0] == '-' {
- f = 0hfff7ffffffffffff
- }
- if assign_float(v, f) {
- return
- }
- return UNSUPPORTED_TYPE
- }
- }
- }
- advance_token(p)
- return UNSUPPORTED_TYPE
- }
- @(private)
- unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
- prev := p.curr_token
- err := expect_token(p, kind)
- assert(err == nil, "unmarshal_expect_token")
- return prev
- }
- @(private)
- unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
- UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
-
- if end_token == .Close_Brace {
- unmarshal_expect_token(p, .Open_Brace)
- }
- v := v
- v = reflect.any_base(v)
- ti := type_info_of(v.id)
-
- #partial switch t in ti.variant {
- case reflect.Type_Info_Struct:
- if t.is_raw_union {
- return UNSUPPORTED_TYPE
- }
-
- struct_loop: for p.curr_token.kind != end_token {
- key, _ := parse_object_key(p, p.allocator)
- defer delete(key, p.allocator)
-
- unmarshal_expect_token(p, .Colon)
-
- fields := reflect.struct_fields_zipped(ti.id)
-
- runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
- field_used := make([]bool, len(fields), context.temp_allocator)
-
- use_field_idx := -1
-
- for field, field_idx in fields {
- tag_value := string(reflect.struct_tag_get(field.tag, "json"))
- if key == tag_value {
- use_field_idx = field_idx
- break
- }
- }
-
- if use_field_idx < 0 {
- for field, field_idx in fields {
- if key == field.name {
- use_field_idx = field_idx
- break
- }
- }
- }
-
- if use_field_idx >= 0 {
- if field_used[use_field_idx] {
- return .Multiple_Use_Field
- }
- field_used[use_field_idx] = true
- offset := fields[use_field_idx].offset
- type := fields[use_field_idx].type
- name := fields[use_field_idx].name
-
- field_ptr := rawptr(uintptr(v.data) + offset)
- field := any{field_ptr, type.id}
- unmarshal_value(p, field) or_return
-
- if parse_comma(p) {
- break struct_loop
- }
- continue struct_loop
- } else {
- // allows skipping unused struct fields
- parse_value(p) or_return
- if parse_comma(p) {
- break struct_loop
- }
- continue struct_loop
- }
- }
-
- case reflect.Type_Info_Map:
- if !reflect.is_string(t.key) {
- return UNSUPPORTED_TYPE
- }
- raw_map := (^mem.Raw_Map)(v.data)
- if raw_map.allocator.procedure == nil {
- raw_map.allocator = p.allocator
- }
-
- elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
- defer delete(elem_backing, p.allocator)
-
- map_backing_value := any{raw_data(elem_backing), t.value.id}
-
- map_loop: for p.curr_token.kind != end_token {
- key, _ := parse_object_key(p, p.allocator)
- unmarshal_expect_token(p, .Colon)
-
-
- mem.zero_slice(elem_backing)
- if err := unmarshal_value(p, map_backing_value); err != nil {
- delete(key, p.allocator)
- return err
- }
- key_ptr := rawptr(&key)
- key_cstr: cstring
- if reflect.is_cstring(t.key) {
- key_cstr = cstring(raw_data(key))
- key_ptr = &key_cstr
- }
-
- set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
- if set_ptr == nil {
- delete(key, p.allocator)
- }
-
- if parse_comma(p) {
- break map_loop
- }
- }
-
- case reflect.Type_Info_Enumerated_Array:
- index_type := reflect.type_info_base(t.index)
- enum_type := index_type.variant.(reflect.Type_Info_Enum)
-
- enumerated_array_loop: for p.curr_token.kind != end_token {
- key, _ := parse_object_key(p, p.allocator)
- unmarshal_expect_token(p, .Colon)
- defer delete(key, p.allocator)
- index := -1
- for name, i in enum_type.names {
- if key == name {
- index = int(enum_type.values[i] - t.min_value)
- break
- }
- }
- if index < 0 || index >= t.count {
- return UNSUPPORTED_TYPE
- }
-
- index_ptr := rawptr(uintptr(v.data) + uintptr(index*t.elem_size))
- index_any := any{index_ptr, t.elem.id}
-
- unmarshal_value(p, index_any) or_return
-
- if parse_comma(p) {
- break enumerated_array_loop
- }
- }
- return nil
- case:
- return UNSUPPORTED_TYPE
- }
-
- if end_token == .Close_Brace {
- unmarshal_expect_token(p, .Close_Brace)
- }
- return
- }
- @(private)
- unmarshal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
- p_backup := p^
- p.allocator = mem.nil_allocator()
- unmarshal_expect_token(p, .Open_Bracket)
- array_length_loop: for p.curr_token.kind != .Close_Bracket {
- _, _ = parse_value(p)
- length += 1
-
- if parse_comma(p) {
- break
- }
- }
- p^ = p_backup
- return
- }
- @(private)
- unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
- assign_array :: proc(p: ^Parser, base: rawptr, elem: ^reflect.Type_Info, length: uintptr) -> Unmarshal_Error {
- unmarshal_expect_token(p, .Open_Bracket)
-
- for idx: uintptr = 0; p.curr_token.kind != .Close_Bracket; idx += 1 {
- assert(idx < length)
-
- elem_ptr := rawptr(uintptr(base) + idx*uintptr(elem.size))
- elem := any{elem_ptr, elem.id}
-
- unmarshal_value(p, elem) or_return
-
- if parse_comma(p) {
- break
- }
- }
-
- unmarshal_expect_token(p, .Close_Bracket)
-
-
- return nil
- }
- UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
-
- ti := reflect.type_info_base(type_info_of(v.id))
-
- length := unmarshal_count_array(p)
-
- #partial switch t in ti.variant {
- case reflect.Type_Info_Slice:
- raw := (^mem.Raw_Slice)(v.data)
- data := bytes_make(t.elem.size * int(length), t.elem.align, p.allocator) or_return
- raw.data = raw_data(data)
- raw.len = int(length)
-
- return assign_array(p, raw.data, t.elem, length)
-
- case reflect.Type_Info_Dynamic_Array:
- raw := (^mem.Raw_Dynamic_Array)(v.data)
- data := bytes_make(t.elem.size * int(length), t.elem.align, p.allocator) or_return
- raw.data = raw_data(data)
- raw.len = int(length)
- raw.cap = int(length)
- raw.allocator = p.allocator
-
- return assign_array(p, raw.data, t.elem, length)
-
- case reflect.Type_Info_Array:
- // NOTE(bill): Allow lengths which are less than the dst array
- if int(length) > t.count {
- return UNSUPPORTED_TYPE
- }
-
- return assign_array(p, v.data, t.elem, length)
-
- case reflect.Type_Info_Enumerated_Array:
- // NOTE(bill): Allow lengths which are less than the dst array
- if int(length) > t.count {
- return UNSUPPORTED_TYPE
- }
-
- return assign_array(p, v.data, t.elem, length)
-
- case reflect.Type_Info_Complex:
- // NOTE(bill): Allow lengths which are less than the dst array
- if int(length) > 2 {
- return UNSUPPORTED_TYPE
- }
-
- switch ti.id {
- case complex32: return assign_array(p, v.data, type_info_of(f16), 2)
- case complex64: return assign_array(p, v.data, type_info_of(f32), 2)
- case complex128: return assign_array(p, v.data, type_info_of(f64), 2)
- }
-
- return UNSUPPORTED_TYPE
-
- }
-
- return UNSUPPORTED_TYPE
- }
|