diff --git a/core/encoding/cbor/doc.odin b/core/encoding/cbor/doc.odin index 937b1b61b..b3fa36130 100644 --- a/core/encoding/cbor/doc.odin +++ b/core/encoding/cbor/doc.odin @@ -77,8 +77,11 @@ You can look at the default tags provided for pointers on how these implementati Example: package main + import "base:intrinsics" + import "core:encoding/cbor" import "core:fmt" + import "core:reflect" import "core:time" Possibilities :: union { @@ -93,9 +96,32 @@ Example: ignore_this: ^Data `cbor:"-"`, // Ignored by implementation. renamed: f32 `cbor:"renamed :)"`, // Renamed when encoded. my_union: Possibilities, // Union support. + + my_raw: [8]u32 `cbor_tag:"raw"`, // Custom tag that just writes the value as bytes. } main :: proc() { + // Example custom tag implementation that instead of breaking down all parts, + // just writes the value as a big byte blob. This is an advanced feature but very powerful. + RAW_TAG_NR :: 200 + cbor.tag_register_number({ + marshal = proc(_: ^cbor.Tag_Implementation, e: cbor.Encoder, v: any) -> cbor.Marshal_Error { + cbor._encode_u8(e.writer, RAW_TAG_NR, .Tag) or_return + return cbor.err_conv(cbor._encode_bytes(e, reflect.as_bytes(v))) + }, + unmarshal = proc(_: ^cbor.Tag_Implementation, d: cbor.Decoder, _: cbor.Tag_Number, v: any) -> (cbor.Unmarshal_Error) { + hdr := cbor._decode_header(d.reader) or_return + maj, add := cbor._header_split(hdr) + if maj != .Bytes { + return .Bad_Tag_Value + } + + bytes := cbor.err_conv(cbor._decode_bytes(d, add, maj)) or_return + intrinsics.mem_copy_non_overlapping(v.data, raw_data(bytes), len(bytes)) + return nil + }, + }, RAW_TAG_NR, "raw") + now := time.Time{_nsec = 1701117968 * 1e9} data := Data{ @@ -105,21 +131,22 @@ Example: ignore_this = &Data{}, renamed = 123123.125, my_union = 3, + my_raw = {1=1, 2=2, 3=3}, } - + // Marshal the struct into binary CBOR. binary, err := cbor.marshal(data, cbor.ENCODE_FULLY_DETERMINISTIC) - assert(err == nil) + fmt.assertf(err == nil, "marshal error: %v", err) defer delete(binary) - + // Decode the binary data into a `cbor.Value`. decoded, derr := cbor.decode(string(binary)) - assert(derr == nil) + fmt.assertf(derr == nil, "decode error: %v", derr) defer cbor.destroy(decoded) // Turn the CBOR into a human readable representation defined as the diagnostic format in [[RFC 8949 Section 8;https://www.rfc-editor.org/rfc/rfc8949.html#name-diagnostic-notation]]. diagnosis, eerr := cbor.to_diagnostic_format(decoded) - assert(eerr == nil) + fmt.assertf(eerr == nil, "to diagnostic error: %v", eerr) defer delete(diagnosis) fmt.println(diagnosis) @@ -127,6 +154,7 @@ Example: Output: { + "my_raw": 200(h'00001000200030000000000000000000'), "my_union": 1010([ "int", 3 diff --git a/core/encoding/cbor/marshal.odin b/core/encoding/cbor/marshal.odin index d64d22d9c..2cdf384c3 100644 --- a/core/encoding/cbor/marshal.odin +++ b/core/encoding/cbor/marshal.odin @@ -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, loc=loc); err != nil { + if err = marshal_into_builder(&b, v, flags, temp_allocator); 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, loc := #caller_location) -> Marshal_Error { - return marshal_into_writer(strings.to_writer(b), v, flags, temp_allocator, loc=loc) +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) } // 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, loc := #caller_location) -> Marshal_Error { +marshal_into_writer :: proc(w: io.Writer, v: any, flags := ENCODE_SMALL, temp_allocator := context.temp_allocator) -> Marshal_Error { encoder := Encoder{flags, w, temp_allocator} - return marshal_into_encoder(encoder, v, loc=loc) + return marshal_into_encoder(encoder, v) } // 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, loc := #caller_location) -> (err: Marshal_Error) { +marshal_into_encoder :: proc(e: Encoder, v: any) -> (err: Marshal_Error) { e := e if e.temp_allocator.procedure == nil { @@ -97,11 +97,14 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e return impl->marshal(e, v) } - ti := runtime.type_info_base(type_info_of(v.id)) - a := any{v.data, ti.id} + ti := runtime.type_info_core(type_info_of(v.id)) + return _marshal_into_encoder(e, v, ti) +} +_marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (err: Marshal_Error) { + a := any{v.data, ti.id} #partial switch info in ti.variant { - case runtime.Type_Info_Named: + case runtime.Type_Info_Named, runtime.Type_Info_Enum, runtime.Type_Info_Bit_Field: unreachable() case runtime.Type_Info_Pointer: @@ -223,18 +226,38 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e } err_conv(_encode_u64(e, u64(info.count), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) for i in 0.. (e array := (^mem.Raw_Dynamic_Array)(v.data) err_conv(_encode_u64(e, u64(array.len), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) for i in 0.. (e array := (^mem.Raw_Slice)(v.data) err_conv(_encode_u64(e, u64(array.len), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) for i in 0.. (e return marshal_into(e, any{v.data, vti.id}) - case runtime.Type_Info_Enum: - return marshal_into(e, any{v.data, info.base.id}) - case runtime.Type_Info_Bit_Set: // Store bit_set as big endian just like the protocol. do_byte_swap := !reflect.bit_set_is_big_endian(v) diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index 289895e98..13350bb85 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -520,9 +520,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header return case reflect.Type_Info_Array: - _, scap := err_conv(_decode_len_container(d, add)) or_return - length := min(scap, t.count) - + length, _ := err_conv(_decode_len_container(d, add)) or_return if length > t.count { return _unsupported(v, hdr) } @@ -534,9 +532,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header return case reflect.Type_Info_Enumerated_Array: - _, scap := err_conv(_decode_len_container(d, add)) or_return - length := min(scap, t.count) - + length, _ := err_conv(_decode_len_container(d, add)) or_return if length > t.count { return _unsupported(v, hdr) } @@ -548,9 +544,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header return case reflect.Type_Info_Complex: - _, scap := err_conv(_decode_len_container(d, add)) or_return - length := min(scap, 2) - + length, _ := err_conv(_decode_len_container(d, add)) or_return if length > 2 { return _unsupported(v, hdr) } @@ -570,9 +564,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header return case reflect.Type_Info_Quaternion: - _, scap := err_conv(_decode_len_container(d, add)) or_return - length := min(scap, 4) - + length, _ := err_conv(_decode_len_container(d, add)) or_return if length > 4 { return _unsupported(v, hdr) } @@ -633,7 +625,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, length, _ := err_conv(_decode_len_container(d, add)) or_return unknown := length == -1 fields := reflect.struct_fields_zipped(ti.id) - + for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 { // Decode key, keys can only be strings. key: string @@ -646,7 +638,7 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, key = keyv } defer delete(key, context.temp_allocator) - + // Find matching field. use_field_idx := -1 { diff --git a/core/io/io.odin b/core/io/io.odin index 961dbe43e..6072aec6d 100644 --- a/core/io/io.odin +++ b/core/io/io.odin @@ -375,10 +375,6 @@ write_at_least :: proc(w: Writer, buf: []byte, min: int) -> (n: int, err: Error) nn, err = write(w, buf[n:]) n += nn } - - if err == nil && n < min { - err = .Short_Write - } return }