package json import "core:mem" import "core:math/bits" import "core:runtime" import "core:strconv" import "core:strings" Marshal_Error :: enum { None, Unsupported_Type, Invalid_Data, } marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) { b: strings.Builder; strings.init_builder(&b, allocator); err := marshal_arg(&b, v); if err != .None { strings.destroy_builder(&b); return nil, err; } if len(b.buf) == 0 { 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 .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: 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 f16: val = f64(f); 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); s := buf[:len(str)+1]; if s[1] == '+' || s[1] == '-' { s = s[1:]; } else { s[0] = '+'; } if s[0] == '+' { s = s[1:]; } write_string(b, string(s)); case Type_Info_Complex: return .Unsupported_Type; case Type_Info_Quaternion: return .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_builder(b, val ? "true" : "false"); case Type_Info_Any: return .Unsupported_Type; case Type_Info_Type_Id: return .Unsupported_Type; case Type_Info_Pointer: return .Unsupported_Type; case Type_Info_Procedure: return .Unsupported_Type; case Type_Info_Tuple: return .Unsupported_Type; case Type_Info_Enumerated_Array: return .Unsupported_Type; case Type_Info_Simd_Vector: return .Unsupported_Type; case Type_Info_Relative_Pointer: return .Unsupported_Type; case Type_Info_Relative_Slice: return .Unsupported_Type; case Type_Info_Array: write_byte(b, '['); for i in 0.. 0 { 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.. 0 { 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.. 0 { 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 .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.. 0 { write_string(b, ", "); } data := uintptr(entries.data) + uintptr(i*entry_size); key := rawptr(data + entry_type.offsets[2]); value := rawptr(data + entry_type.offsets[3]); marshal_arg(b, any{key, info.key.id}); write_string(b, ": "); marshal_arg(b, any{value, info.value.id}); } } write_byte(b, '}'); case Type_Info_Struct: write_byte(b, '{'); for name, i in info.names { if i > 0 { 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_arg(b, any{v.data, info.base.id}); case Type_Info_Bit_Set: is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { if ti == nil { return false; } t := runtime.type_info_base(ti); #partial switch info in t.variant { case runtime.Type_Info_Integer: switch info.endianness { case .Platform: return false; case .Little: return ODIN_ENDIAN != "little"; case .Big: return ODIN_ENDIAN != "big"; } } return false; } bit_data: u64; bit_size := u64(8*ti.size); do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying); switch bit_size { case 0: bit_data = 0; case 8: x := (^u8)(v.data)^; bit_data = u64(x); case 16: x := (^u16)(v.data)^; if do_byte_swap { x = bits.byte_swap(x); } bit_data = u64(x); case 32: x := (^u32)(v.data)^; if do_byte_swap { x = bits.byte_swap(x); } bit_data = u64(x); case 64: x := (^u64)(v.data)^; if do_byte_swap { x = bits.byte_swap(x); } bit_data = u64(x); case: panic("unknown bit_size size"); } write_u64(b, bit_data); return .Unsupported_Type; } return .None; }