Skip to content

Commit

Permalink
x.json2.decoder2: decode bool and numbers (#22550)
Browse files Browse the repository at this point in the history
  • Loading branch information
enghitalo authored Oct 17, 2024
1 parent 7da79fd commit 7c1cde0
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 6 deletions.
94 changes: 89 additions & 5 deletions vlib/x/json2/decoder2/decode.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
}
35 changes: 34 additions & 1 deletion vlib/x/json2/decoder2/tests/bench.v
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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)!
Expand All @@ -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')!")
}
94 changes: 94 additions & 0 deletions vlib/x/json2/decoder2/tests/decode_number_and_boolean_test.v
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit 7c1cde0

Please sign in to comment.