diff options
| author | gingerBill <bill@gingerbill.org> | 2019-01-06 23:32:50 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2019-01-06 23:32:50 +0000 |
| commit | 6c21e99832da22e7e89a5eac12f5f52d9486f7f0 (patch) | |
| tree | c254384562d8600673e3285d7e6135c32f181d8b /core/encoding | |
| parent | 08598b9425ebaf06805cbf9b2fb09acbc29a3a64 (diff) | |
json.marshal
Diffstat (limited to 'core/encoding')
| -rw-r--r-- | core/encoding/json/marshal.odin | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin new file mode 100644 index 000000000..34ac71fca --- /dev/null +++ b/core/encoding/json/marshal.odin @@ -0,0 +1,283 @@ +package json + +import "core:mem" +import "core:runtime" +import "core:strconv" +import "core:strings" +import "core:types" + +Marshal_Error :: enum { + None, + Unsupported_Type, +} + +marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) { + b := strings.make_builder(allocator); + + err := marshal_arg(&b, v); + + if err != Marshal_Error.None { + strings.destroy_builder(&b); + return nil, err; + } + + return b.buf[:], err; +} + + +marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { + using strings; + using runtime; + if v == nil { + write_string(b, "null"); + return Marshal_Error.None; + } + + ti := type_info_base(type_info_of(v.id)); + a := any{v.data, ti.id}; + + switch info in ti.variant { + case Type_Info_Named: + panic("Unreachable"); + + case Type_Info_Integer: + buf: [21]byte; + u: u64; + switch i in a { + case i8: u = u64(i); + case i16: u = u64(i); + case i32: u = u64(i); + case i64: u = u64(i); + case int: u = u64(i); + case u8: u = u64(i); + case u16: u = u64(i); + case u32: u = u64(i); + case u64: u = u64(i); + case uint: u = u64(i); + case uintptr: u = u64(i); + + case i16le: u = u64(i); + case i32le: u = u64(i); + case i64le: u = u64(i); + case u16le: u = u64(i); + case u32le: u = u64(i); + case u64le: u = u64(i); + + case i16be: u = u64(i); + case i32be: u = u64(i); + case i64be: u = u64(i); + case u16be: u = u64(i); + case u32be: u = u64(i); + case u64be: u = u64(i); + } + + s := strconv.append_bits(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil); + write_string(b, s); + + + case Type_Info_Rune: + r := a.(rune); + write_byte(b, '"'); + write_escaped_rune(b, r, '"', true); + write_byte(b, '"'); + + case Type_Info_Float: + val: f64; + switch f in a { + case f32: val = f64(f); + case f64: val = f64(f); + } + + buf: [386]byte; + + str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size); + str = string(buf[:len(str)+1]); + if str[1] == '+' || str[1] == '-' { + str = str[1:]; + } else { + str[0] = '+'; + } + if str[0] == '+' { + str = str[1:]; + } + + write_string(b, str); + + case Type_Info_Complex: + return Marshal_Error.Unsupported_Type; + + case Type_Info_String: + switch s in a { + case string: write_quoted_string(b, s); + case cstring: write_quoted_string(b, string(s)); + } + + case Type_Info_Boolean: + val: bool; + switch b in a { + case bool: val = bool(b); + case b8: val = bool(b); + case b16: val = bool(b); + case b32: val = bool(b); + case b64: val = bool(b); + } + write_string(b, val ? "true" : "false"); + + case Type_Info_Any: + return Marshal_Error.Unsupported_Type; + + case Type_Info_Type_Id: + return Marshal_Error.Unsupported_Type; + + case Type_Info_Pointer: + return Marshal_Error.Unsupported_Type; + + case Type_Info_Procedure: + return Marshal_Error.Unsupported_Type; + + case Type_Info_Tuple: + return Marshal_Error.Unsupported_Type; + + case Type_Info_Array: + write_byte(b, '['); + for i in 0..info.count-1 { + if i > 0 do write_string(b, ", "); + + data := uintptr(v.data) + uintptr(i*info.elem_size); + marshal_arg(b, any{rawptr(data), info.elem.id}); + } + write_byte(b, ']'); + + case Type_Info_Dynamic_Array: + write_byte(b, '['); + array := cast(^mem.Raw_Dynamic_Array)v.data; + for i in 0..array.len-1 { + if i > 0 do write_string(b, ", "); + + data := uintptr(array.data) + uintptr(i*info.elem_size); + marshal_arg(b, any{rawptr(data), info.elem.id}); + } + write_byte(b, ']'); + + case Type_Info_Slice: + write_byte(b, '['); + slice := cast(^mem.Raw_Slice)v.data; + for i in 0..slice.len-1 { + if i > 0 do write_string(b, ", "); + + data := uintptr(slice.data) + uintptr(i*info.elem_size); + marshal_arg(b, any{rawptr(data), info.elem.id}); + } + write_byte(b, ']'); + + case Type_Info_Map: + m := (^mem.Raw_Map)(v.data); + + write_byte(b, '{'); + if m != nil { + if info.generated_struct == nil { + return Marshal_Error.Unsupported_Type; + } + entries := &m.entries; + gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct); + ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array); + entry_type := ed.elem.variant.(Type_Info_Struct); + entry_size := ed.elem_size; + + for i in 0..entries.len-1 { + if i > 0 do write_string(b, ", "); + + data := uintptr(entries.data) + uintptr(i*entry_size); + header := cast(^Map_Entry_Header)data; + + if types.is_string(info.key) { + marshal_arg(b, header.key.str); + } else { + marshal_arg(b, any{rawptr(&header.key.hash), info.key.id}); + } + + write_string(b, ": "); + + value := data + entry_type.offsets[2]; + marshal_arg(b, any{rawptr(value), info.value.id}); + } + } + write_byte(b, '}'); + + case Type_Info_Struct: + write_byte(b, '{'); + for name, i in info.names { + if i > 0 do write_string(b, ", "); + write_quoted_string(b, name); + write_string(b, ": "); + + id := info.types[i].id; + data := rawptr(uintptr(v.data) + info.offsets[i]); + marshal_arg(b, any{data, id}); + } + write_byte(b, '}'); + + case Type_Info_Union: + tag_ptr := uintptr(v.data) + info.tag_offset; + tag_any := any{rawptr(tag_ptr), info.tag_type.id}; + + tag: i64 = -1; + switch i in tag_any { + case u8: tag = i64(i); + case i8: tag = i64(i); + case u16: tag = i64(i); + case i16: tag = i64(i); + case u32: tag = i64(i); + case i32: tag = i64(i); + case u64: tag = i64(i); + case i64: tag = i64(i); + case: panic("Invalid union tag type"); + } + + if v.data == nil || tag == 0 { + write_string(b, "null"); + } else { + id := info.variants[tag-1].id; + marshal_arg(b, any{v.data, id}); + } + + case Type_Info_Enum: + return Marshal_Error.Unsupported_Type; + + case Type_Info_Bit_Field: + data: u64 = 0; + switch ti.size { + case 1: data = cast(u64) (^u8)(v.data)^; + case 2: data = cast(u64)(^u16)(v.data)^; + case 4: data = cast(u64)(^u32)(v.data)^; + case 8: data = cast(u64)(^u64)(v.data)^; + } + + write_byte(b, '{'); + for name, i in info.names { + if i > 0 do write_string(b, ", "); + + bits := u64(info.bits[i]); + offset := u64(info.offsets[i]); + marshal_arg(b, name); + write_string(b, ": "); + + n := 8*u64(size_of(u64)); + sa := n - bits; + u := data>>offset; + u <<= sa; + u >>= sa; + + write_u64(b, u, 10); + } + write_byte(b, '}'); + + case Type_Info_Bit_Set: + return Marshal_Error.Unsupported_Type; + + case Type_Info_Opaque: + return Marshal_Error.Unsupported_Type; + } + + return Marshal_Error.None; +} |