Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 30 additions & 19 deletions core/encoding/json/marshal.odin
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ Marshal_Options :: struct {
mjson_skipped_first_braces_end: bool,
}

marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
User_Marshaller :: #type proc(w: io.Writer, v: any) -> Marshal_Error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that User_Marshaller needs ^Marshaller_Options too. If marshaller produces a json object, then there should be a way to respect all those options.
Also makes it possible to just call json.marshal_to_writer(w, transformed_data, opt)

User_Marshaller_Map :: map[typeid]User_Marshaller

marshal :: proc(v: any, opt: Marshal_Options = {}, user_marshallers: User_Marshaller_Map = nil, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Marshal_Error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably better to place user_marshallers inside Marshal_Options.

b := strings.builder_make(allocator, loc)
defer if err != nil {
strings.builder_destroy(&b)
Expand All @@ -72,7 +75,7 @@ marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocato
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)

opt := opt
marshal_to_builder(&b, v, &opt) or_return
marshal_to_builder(&b, v, &opt, user_marshallers) or_return

if len(b.buf) != 0 {
data = b.buf[:]
Expand All @@ -81,16 +84,24 @@ marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocato
return data, nil
}

marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
return marshal_to_writer(strings.to_writer(b), v, opt)
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options, user_marshallers: User_Marshaller_Map = nil) -> Marshal_Error {
return marshal_to_writer(strings.to_writer(b), v, opt, user_marshallers)
}

marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options, user_marshallers: User_Marshaller_Map = nil) -> (err: Marshal_Error) {
if v == nil {
io.write_string(w, "null") or_return
return
}

if user_marshallers != nil {
marshaller := user_marshallers[v.id]
if marshaller != nil {
marshaller(w, v) or_return
return
}
}

ti := runtime.type_info_base(type_info_of(v.id))
a := any{v.data, ti.id}

Expand Down Expand Up @@ -204,7 +215,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
for i in 0..<info.count {
opt_write_iteration(w, opt, i == 0) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt, user_marshallers) or_return
}
opt_write_end(w, opt, ']') or_return

Expand All @@ -223,7 +234,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_iteration(w, opt, i == 0) or_return
opt_write_key(w, opt, enum_type.names[index]) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt, user_marshallers) or_return
}
opt_write_end(w, opt, '}') or_return

Expand All @@ -233,7 +244,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
for i in 0..<array.len {
opt_write_iteration(w, opt, i == 0) or_return
data := uintptr(array.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt, user_marshallers) or_return
}
opt_write_end(w, opt, ']') or_return

Expand All @@ -243,7 +254,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
for i in 0..<slice.len {
opt_write_iteration(w, opt, i == 0) or_return
data := uintptr(slice.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt, user_marshallers) or_return
}
opt_write_end(w, opt, ']') or_return

Expand Down Expand Up @@ -293,7 +304,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
}
}

marshal_to_writer(w, any{value, info.value.id}, opt) or_return
marshal_to_writer(w, any{value, info.value.id}, opt, user_marshallers) or_return
}
} else {
Entry :: struct {
Expand Down Expand Up @@ -336,7 +347,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
for s, i in sorted {
opt_write_iteration(w, opt, i == 0) or_return
opt_write_key(w, opt, s.key) or_return
marshal_to_writer(w, s.value, opt) or_return
marshal_to_writer(w, s.value, opt, user_marshallers) or_return
}
}
}
Expand Down Expand Up @@ -380,7 +391,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
return false
}

marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options, user_marshallers: User_Marshaller_Map) -> (err: Marshal_Error) {
ti := runtime.type_info_base(type_info_of(v.id))
info := ti.variant.(runtime.Type_Info_Struct)
first_iteration := true
Expand Down Expand Up @@ -415,21 +426,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
} else {
// Marshal the fields of 'using _: T' fields directly into the parent struct
if info.usings[i] && name == "_" {
marshal_struct_fields(w, the_value, opt) or_return
marshal_struct_fields(w, the_value, opt, user_marshallers) or_return
continue
} else {
opt_write_key(w, opt, name) or_return
}
}


marshal_to_writer(w, the_value, opt) or_return
marshal_to_writer(w, the_value, opt, user_marshallers) or_return
}
return
}

opt_write_start(w, opt, '{') or_return
marshal_struct_fields(w, v, opt) or_return
marshal_struct_fields(w, v, opt, user_marshallers) or_return
opt_write_end(w, opt, '}') or_return

case runtime.Type_Info_Union:
Expand Down Expand Up @@ -462,17 +473,17 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
tag -= 1
}
id := info.variants[tag].id
return marshal_to_writer(w, any{v.data, id}, opt)
return marshal_to_writer(w, any{v.data, id}, opt, user_marshallers)

case runtime.Type_Info_Enum:
if !opt.use_enum_names || len(info.names) == 0 {
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
return marshal_to_writer(w, any{v.data, info.base.id}, opt, user_marshallers)
} else {
name, found := reflect.enum_name_from_value_any(v)
if found {
return marshal_to_writer(w, name, opt)
return marshal_to_writer(w, name, opt, user_marshallers)
} else {
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
return marshal_to_writer(w, any{v.data, info.base.id}, opt, user_marshallers)
}
}

Expand Down
61 changes: 36 additions & 25 deletions core/encoding/json/unmarshal.odin
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ Unmarshal_Error :: union {
Unsupported_Type_Error,
}

unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, allocator := context.allocator) -> Unmarshal_Error {
User_Unmarshaller :: #type proc(p: ^Parser, v: any) -> Unmarshal_Error
User_Unmarshaller_Map :: map[typeid]User_Unmarshaller

unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, user_unmarshallers: User_Unmarshaller_Map, allocator := context.allocator) -> Unmarshal_Error {
v := v
if v == nil || v.id == nil {
return .Invalid_Parameter
Expand All @@ -53,20 +56,20 @@ unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, alloc
if p.spec == .MJSON {
#partial switch p.curr_token.kind {
case .Ident, .String:
return unmarshal_object(&p, data, .EOF)
return unmarshal_object(&p, data, .EOF, user_unmarshallers)
}
}

return unmarshal_value(&p, data)
return unmarshal_value(&p, data, user_unmarshallers)
}


unmarshal :: proc(data: []byte, ptr: ^$T, spec := DEFAULT_SPECIFICATION, allocator := context.allocator) -> Unmarshal_Error {
return unmarshal_any(data, ptr, spec, allocator)
unmarshal :: proc(data: []byte, ptr: ^$T, spec := DEFAULT_SPECIFICATION, user_unmarshallers: User_Unmarshaller_Map = nil, allocator := context.allocator) -> Unmarshal_Error {
return unmarshal_any(data, ptr, spec, user_unmarshallers, 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)
unmarshal_string :: proc(data: string, ptr: ^$T, spec := DEFAULT_SPECIFICATION, user_unmarshallers: User_Unmarshaller_Map = nil, allocator := context.allocator) -> Unmarshal_Error {
return unmarshal_any(transmute([]byte)data, ptr, spec, user_unmarshallers, allocator)
}


Expand Down Expand Up @@ -257,10 +260,18 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.T


@(private)
unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
unmarshal_value :: proc(p: ^Parser, v: any, user_unmarshallers: User_Unmarshaller_Map = nil) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
token := p.curr_token

if user_unmarshallers != nil {
unmarshaller := user_unmarshallers[v.id]
if unmarshaller != nil {
unmarshaller(p, v) or_return
return
}
}

v := v
ti := reflect.type_info_base(type_info_of(v.id))
if u, ok := ti.variant.(reflect.Type_Info_Union); ok && token.kind != .Null {
Expand All @@ -277,7 +288,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
for variant, i in u.variants {
variant_any := any{v.data, variant.id}
variant_p := p^
if err = unmarshal_value(&variant_p, variant_any); err == nil {
if err = unmarshal_value(&variant_p, variant_any, user_unmarshallers); err == nil {
p^ = variant_p

raw_tag := i
Expand Down Expand Up @@ -361,10 +372,10 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
return nil

case .Open_Brace:
return unmarshal_object(p, v, .Close_Brace)
return unmarshal_object(p, v, .Close_Brace, user_unmarshallers)

case .Open_Bracket:
return unmarshal_array(p, v)
return unmarshal_array(p, v, user_unmarshallers)

case:
if p.spec != .JSON {
Expand Down Expand Up @@ -421,7 +432,7 @@ json_name_from_tag_value :: proc(value: string) -> (json_name, extra: string) {


@(private)
unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind, user_unmarshallers: User_Unmarshaller_Map = nil) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}

if end_token == .Close_Brace {
Expand Down Expand Up @@ -520,7 +531,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm

field_ptr := rawptr(uintptr(v.data) + offset)
field := any{field_ptr, type.id}
unmarshal_value(p, field) or_return
unmarshal_value(p, field, user_unmarshallers) or_return

if parse_comma(p) {
break struct_loop
Expand Down Expand Up @@ -562,7 +573,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm


mem.zero_slice(elem_backing)
if uerr := unmarshal_value(p, map_backing_value); uerr != nil {
if uerr := unmarshal_value(p, map_backing_value, user_unmarshallers); uerr != nil {
delete(key, p.allocator)
return uerr
}
Expand Down Expand Up @@ -624,7 +635,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
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
unmarshal_value(p, index_any, user_unmarshallers) or_return

if parse_comma(p) {
break enumerated_array_loop
Expand Down Expand Up @@ -660,8 +671,8 @@ unmarshal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
}

@(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_array :: proc(p: ^Parser, v: any, user_unmarshallers: User_Unmarshaller_Map = nil) -> (err: Unmarshal_Error) {
assign_array :: proc(p: ^Parser, base: rawptr, elem: ^reflect.Type_Info, length: uintptr, user_unmarshallers: User_Unmarshaller_Map = nil) -> Unmarshal_Error {
unmarshal_expect_token(p, .Open_Bracket)

for idx: uintptr = 0; p.curr_token.kind != .Close_Bracket; idx += 1 {
Expand All @@ -670,7 +681,7 @@ unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
elem_ptr := rawptr(uintptr(base) + idx*uintptr(elem.size))
elem := any{elem_ptr, elem.id}

unmarshal_value(p, elem) or_return
unmarshal_value(p, elem, user_unmarshallers) or_return

if parse_comma(p) {
break
Expand All @@ -696,7 +707,7 @@ unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
raw.data = raw_data(data)
raw.len = int(length)

return assign_array(p, raw.data, t.elem, length)
return assign_array(p, raw.data, t.elem, length, user_unmarshallers)

case reflect.Type_Info_Dynamic_Array:
raw := (^mem.Raw_Dynamic_Array)(v.data)
Expand All @@ -706,23 +717,23 @@ unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
raw.cap = int(length)
raw.allocator = p.allocator

return assign_array(p, raw.data, t.elem, length)
return assign_array(p, raw.data, t.elem, length, user_unmarshallers)

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)
return assign_array(p, v.data, t.elem, length, user_unmarshallers)

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)
return assign_array(p, v.data, t.elem, length, user_unmarshallers)

case reflect.Type_Info_Complex:
// NOTE(bill): Allow lengths which are less than the dst array
Expand All @@ -731,9 +742,9 @@ unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
}

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)
case complex32: return assign_array(p, v.data, type_info_of(f16), 2, user_unmarshallers)
case complex64: return assign_array(p, v.data, type_info_of(f32), 2, user_unmarshallers)
case complex128: return assign_array(p, v.data, type_info_of(f64), 2, user_unmarshallers)
}

return UNSUPPORTED_TYPE
Expand Down