From 7c1cde0396e97bb7f598fe0be0e5e38e267fb951 Mon Sep 17 00:00:00 2001 From: Hitalo Souza Date: Thu, 17 Oct 2024 13:53:04 -0400 Subject: [PATCH] x.json2.decoder2: decode bool and numbers (#22550) --- vlib/x/json2/decoder2/decode.v | 94 ++++++++++++++++++- vlib/x/json2/decoder2/tests/bench.v | 35 ++++++- .../tests/decode_number_and_boolean_test.v | 94 +++++++++++++++++++ 3 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 vlib/x/json2/decoder2/tests/decode_number_and_boolean_test.v diff --git a/vlib/x/json2/decoder2/decode.v b/vlib/x/json2/decoder2/decode.v index 77851a7bad..4d7236226b 100644 --- a/vlib/x/json2/decoder2/decode.v +++ b/vlib/x/json2/decoder2/decode.v @@ -494,12 +494,12 @@ pub fn decode[T](val string) !T { // decode_value decodes a value from the JSON nodes. fn (mut decoder Decoder) decode_value[T](mut val T) ! { - $if val is $option { + $if T is $option { } $else $if T is string { } $else $if T is $sumtype { $for v in val.variants { if val is v { - decoder.decode_value(nodes, val) + decoder.decode_value(val) } } } $else $if T is $alias { @@ -512,13 +512,26 @@ fn (mut decoder Decoder) decode_value[T](mut val T) ! { decoder.fulfill_nodes(mut nodes) decoder.decode_struct(nodes, val) - } $else $if T is $enum { - } $else $if T is $int { - } $else $if T is $float { } $else $if T is bool { + value_info := decoder.values_info[decoder.value_info_idx] + + unsafe { + val = vmemcmp(decoder.json.str + value_info.position, 'true'.str, 4) == 0 + } + } $else $if T in [$int, $float, $enum] { + value_info := decoder.values_info[decoder.value_info_idx] + + if value_info.value_kind == .number { + bytes := unsafe { (decoder.json.str + value_info.position).vbytes(value_info.length) } + + unsafe { + string_buffer_to_generic_number(val, bytes) + } + } } $else { return error('cannot encode value with ${typeof(val).name} type') } + decoder.value_info_idx++ } // get_value_kind returns the kind of a JSON value. @@ -866,3 +879,74 @@ fn (mut decoder Decoder) fulfill_nodes(mut nodes []Node) { decoder.idx++ } } + +// string_buffer_to_generic_number converts a buffer of bytes (data) into a generic type T and +// stores the result in the provided result pointer. +// The function supports conversion to the following types: +// - Signed integers: i8, i16, int, i64 +// - Unsigned integers: u8, u16, u32, u64 +// - Floating-point numbers: f32, f64 +// +// For signed integers, the function handles negative numbers by checking for a '-' character. +// For floating-point numbers, the function handles decimal points and adjusts the result +// accordingly. +// +// If the type T is not supported, the function will panic with an appropriate error message. +// +// Parameters: +// - data []u8: The buffer of bytes to be converted. +// - result &T: A pointer to the variable where the converted result will be stored. +// +// NOTE: This aims works with not new memory allocated data, to more efficient use `vbytes` before +@[direct_array_access; unsafe] +pub fn string_buffer_to_generic_number[T](result &T, data []u8) { + mut is_negative := false + + $if T is $int { + for ch in data { + if ch == `-` { + is_negative = true + continue + } + digit := T(ch - `0`) + *result = T(*result * 10 + digit) + } + } $else $if T is $float { + mut decimal_seen := false + mut decimal_divider := int(1) + + for ch in data { + if ch == `-` { + is_negative = true + continue + } + if ch == `.` { + decimal_seen = true + continue + } + + digit := T(ch - `0`) + + if decimal_seen { + decimal_divider *= 10 + *result += T(digit / decimal_divider) + } else { + *result = *result * 10 + digit + } + } + } $else $if T is $enum { + // Convert the string to an integer + enumeration := 0 + for ch in data { + digit := int(ch - `0`) + enumeration = enumeration * 10 + digit + } + *result = T(enumeration) + } $else { + panic('unsupported type ${typeof[T]().name}') + } + + if is_negative { + *result = -*result + } +} diff --git a/vlib/x/json2/decoder2/tests/bench.v b/vlib/x/json2/decoder2/tests/bench.v index 62669a44a8..8b29006028 100644 --- a/vlib/x/json2/decoder2/tests/bench.v +++ b/vlib/x/json2/decoder2/tests/bench.v @@ -47,7 +47,11 @@ mut: fn main() { json_data := '{"val": 1, "val2": "lala", "val3": {"a": 2, "churrasco": "leleu"}}' json_data1 := '{"val": "2"}' - // json_data2 := '{"val": 2}' + json_data2 := '{"val": 2}' + + println('Starting benchmark...') + println('max_iterations: ${max_iterations}') + println('\n***Structure and maps***') mut b := benchmark.start() @@ -85,6 +89,19 @@ fn main() { b.measure('old_json.decode(StructTypeOption[string], json_data1)!\n') + // StructType[int] ********************************************************** + for i := 0; i < max_iterations; i++ { + _ := decoder2.decode[StructType[int]](json_data2)! + } + + b.measure('decoder2.decode[StructType[int]](json_data2)!') + + for i := 0; i < max_iterations; i++ { + _ := old_json.decode(StructType[int], json_data2)! + } + + b.measure('old_json.decode(StructType[int], json_data2)!\n') + // map[string]string ********************************************************** for i := 0; i < max_iterations; i++ { _ := decoder2.decode[map[string]string](json_data1)! @@ -97,4 +114,20 @@ fn main() { } b.measure('old_json.decode(map[string]string, json_data1)!\n') + + println('\n***simple types***') + + // int ********************************************************** + for i := 0; i < max_iterations; i++ { + _ := decoder2.decode[int]('2')! + } + + b.measure("decoder2.decode[int]('2')!") + + // bool ********************************************************** + for i := 0; i < max_iterations; i++ { + _ := decoder2.decode[bool]('true')! + } + + b.measure("decoder2.decode[bool]('true')!") } diff --git a/vlib/x/json2/decoder2/tests/decode_number_and_boolean_test.v b/vlib/x/json2/decoder2/tests/decode_number_and_boolean_test.v new file mode 100644 index 0000000000..fb5cae40c1 --- /dev/null +++ b/vlib/x/json2/decoder2/tests/decode_number_and_boolean_test.v @@ -0,0 +1,94 @@ +import x.json2.decoder2 as json + +fn test_number() { + // Test u8 + assert json.decode[u8]('0')! == 0 + assert json.decode[u8]('1')! == 1 + assert json.decode[u8]('201')! == 201 + + assert json.decode[u8]('-1')! == u8(-1) + assert json.decode[u8]('-127')! == u8(-127) + + // Test u16 + assert json.decode[u16]('0')! == 0 + assert json.decode[u16]('1')! == 1 + assert json.decode[u16]('201')! == 201 + + assert json.decode[u16]('-1')! == u16(-1) + assert json.decode[u16]('-201')! == u16(-201) + + // Test u32 + assert json.decode[u32]('0')! == 0 + assert json.decode[u32]('1')! == 1 + assert json.decode[u32]('201')! == 201 + + // Test u64 + assert json.decode[u64]('0')! == 0 + assert json.decode[u64]('1')! == 1 + assert json.decode[u64]('201')! == 201 + + // Test i8 + assert json.decode[i8]('0')! == 0 + assert json.decode[i8]('1')! == 1 + assert json.decode[i8]('127')! == 127 + + assert json.decode[i8]('-1')! == -1 + assert json.decode[i8]('-127')! == -127 + + // Test i16 + assert json.decode[i16]('0')! == 0 + assert json.decode[i16]('1')! == 1 + assert json.decode[i16]('201')! == 201 + + assert json.decode[i16]('-1')! == -1 + assert json.decode[i16]('-201')! == -201 + + // Test int + assert json.decode[int]('0')! == 0 + assert json.decode[int]('1')! == 1 + assert json.decode[int]('201')! == 201 + + assert json.decode[int]('-1')! == -1 + assert json.decode[int]('-201')! == -201 + + assert json.decode[int]('1234567890')! == 1234567890 + assert json.decode[int]('-1234567890')! == -1234567890 + + // Test i64 + assert json.decode[i64]('0')! == 0 + assert json.decode[i64]('1')! == 1 + assert json.decode[i64]('201')! == 201 + + assert json.decode[i64]('-1')! == -1 + assert json.decode[i64]('-201')! == -201 + + assert json.decode[i64]('1234567890')! == 1234567890 + assert json.decode[i64]('-1234567890')! == -1234567890 + + // Test f32 + assert json.decode[f32]('0')! == 0.0 + assert json.decode[f32]('1')! == 1.0 + assert json.decode[f32]('201')! == 201.0 + + assert json.decode[f32]('-1')! == -1.0 + assert json.decode[f32]('-201')! == -201.0 + + assert json.decode[f32]('1234567890')! == 1234567890.0 + assert json.decode[f32]('-1234567890')! == -1234567890.0 + + // Test f64 + assert json.decode[f64]('0')! == 0.0 + assert json.decode[f64]('1')! == 1.0 + assert json.decode[f64]('201')! == 201.0 + + assert json.decode[f64]('-1')! == -1.0 + assert json.decode[f64]('-201')! == -201.0 + + assert json.decode[f64]('1234567890')! == 1234567890.0 + assert json.decode[f64]('-1234567890')! == -1234567890.0 +} + +fn test_boolean() { + assert json.decode[bool]('false')! == false + assert json.decode[bool]('true')! == true +}