Skip to content
Merged
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
54 changes: 46 additions & 8 deletions decode.v
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub fn (mut d Decoder) decode_to_json[T](src []u8) !string {
result << `}`
}
mp_nil {
unsafe { result.push_many('null'.str, 'null'.len) }
unsafe { result.push_many(c'null', 'null'.len) }
}
mp_true, mp_false {
mut bool_val := false
Expand Down Expand Up @@ -124,6 +124,7 @@ pub fn (mut d Decoder) decode_to_json[T](src []u8) !string {
return error('unsupported descriptor byte for conversion to JSON')
}
}

return result.bytestr()
}

Expand All @@ -143,7 +144,14 @@ pub fn (mut d Decoder) decode_from_string[T](data string) ! {
pub fn (mut d Decoder) decode[T](data []u8, mut val T) ! {
d.buffer = data
d.next()!
d.decode_value[T](mut val)!
}

// decode_value dispatches to the type-specific decoder for the descriptor byte
// already loaded into d.bd by a prior next() call. It does NOT reset d.buffer
// or d.pos, so it is safe to use from inside container decoders (struct, map,
// array) that must preserve their position within the buffer.
fn (mut d Decoder) decode_value[T](mut val T) ! {
$if T is $int {
d.decode_integer[T](mut val) or { return error('error decoding integer: ${err}') }
} $else $if T is $float {
Expand Down Expand Up @@ -300,14 +308,44 @@ pub fn (mut d Decoder) decode_map[T](mut val T) ! {
}

pub fn (mut d Decoder) decode_struct[T](mut val T) ! {
data := d.buffer
$for field in T.fields {
// Decode each field using its type
mut field_val := val.$(field.name)
d.decode(data[d.pos..], mut field_val) or {
return error('error decoding struct field: ${field.name}')
map_len := d.read_map_len(d.buffer) or { return error('error reading map length') }
// read_map_len leaves d.pos at the position after the map header byte;
// for map_16 / map_32 we must also skip the explicit length bytes.
match d.bd {
mp_map_16 { d.pos += 2 }
mp_map_32 { d.pos += 4 }
else {}
}

for _ in 0 .. map_len {
// Read key (msgpack-encoded string).
d.next()!
mut key := ''
d.decode_string(mut key) or { return error('error decoding struct field key') }
// Read the value's descriptor byte and dispatch by matching field name
// (honoring `@[codec: 'alias']` attrs the encoder writes).
d.next()!
mut found := false
$for field in T.fields {
mut codec_attr := ''
for attr in field.attrs {
if attr.starts_with('codec:') {
codec_attr = attr.all_after('codec:').trim_space()
break
}
}
if !found && (key == field.name || (codec_attr.len > 0 && key == codec_attr)) {
mut field_val := val.$(field.name)
d.decode_value(mut field_val) or {
return error('error decoding struct field: ${field.name}: ${err}')
}
val.$(field.name) = field_val
found = true
}
}
if !found {
return error('unknown struct field: ${key}')
}
val[field.name] = field_val
}
}

Expand Down
7 changes: 5 additions & 2 deletions decode_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ fn test_decoding() {
// }
// assert msgpack.decode[map[string]string](hex.decode('80')!)! == {} // Test empty map

// // Test decoding structs
// assert msgpack.decode[Struct](hex.decode('82a161a44a6f686ea162d20000001e')!)! == Struct{'John', 30}
// Test decoding structs
assert msgpack.decode[Struct](hex.decode('82a161a44a6f686ea162d20000001e')!)! == Struct{'John', 30}
// Round-trip (works regardless of which integer width the encoder picks)
assert msgpack.decode[Struct](msgpack.encode(Struct{'John', 30}))! == Struct{'John', 30}
assert msgpack.decode[Struct](msgpack.encode(Struct{'', 0}))! == Struct{'', 0}

// Test decoding booleans
assert msgpack.decode[bool](hex.decode('c3')!)! == true
Expand Down
1 change: 1 addition & 0 deletions encode.v
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ pub fn (mut e Encoder) encode_string(s string) {
container_raw_legacy
}
}

e.write_container_len(ct, s.len)
if s.len > 0 {
e.write_string(s)
Expand Down
Loading