diff --git a/vlib/v/tests/bench/bench_json_vs_json2.v b/vlib/v/tests/bench/bench_json_vs_json2.v index 28d4ac5b51..bc390cad5f 100644 --- a/vlib/v/tests/bench/bench_json_vs_json2.v +++ b/vlib/v/tests/bench/bench_json_vs_json2.v @@ -7,6 +7,8 @@ import benchmark // recommendations: // ./v wipe-cache && MAX_ITERATIONS=100_000 ./v run vlib/v/tests/bench/bench_json_vs_json2.v // ./v wipe-cache && MAX_ITERATIONS=100_000 ./v -prod crun vlib/v/tests/bench/bench_json_vs_json2.v +// ./v wipe-cache && ./v -gc boehm_leak -o testcase_leak -prod vlib/v/tests/bench/bench_json_vs_json2.v && ./testcase_leak 2>leaks.txt +// ./v wipe-cache && ./v -prod vlib/v/tests/bench/bench_json_vs_json2.v -o b_out && valgrind --track-origins=yes ./b_out const max_iterations = os.getenv_opt('MAX_ITERATIONS') or { '1000' }.int() diff --git a/vlib/x/json2/count.v b/vlib/x/json2/count.v new file mode 100644 index 0000000000..0984fb4c3b --- /dev/null +++ b/vlib/x/json2/count.v @@ -0,0 +1,98 @@ +module json2 + +import time + +struct Count { +mut: + total int +} + +// get_total +fn (mut count Count) get_total() int { + return count.total +} + +// reset_total +fn (mut count Count) reset_total() { + count.total = 0 +} + +// count_chars count json sizen whithout new encode +fn (mut count Count) count_chars[T](val T) { + $if val is $option { + workaround := val + if workaround != none { + count.count_chars(val) + } + } $else $if T is string { + count.chars_in_string(val) + } $else $if T is $sumtype { + $for v in val.variants { + if val is v { + count.count_chars(val) + } + } + } $else $if T is $alias { + // TODO + } $else $if T is time.Time { + count.total += 26 // "YYYY-MM-DDTHH:mm:ss.123Z" + } $else $if T is $map { + count.total++ // { + for k, v in val { + count.count_chars(k) + count.total++ // : + count.count_chars(v) + } + count.total++ // } + } $else $if T is $array { + count.total += 2 // [] + if val.len > 0 { + for element in val { + count.count_chars(element) + } + count.total += val.len - 1 // , + } + } $else $if T is $struct { + count.chars_in_struct(val) + } $else $if T is $enum { + count.count_chars(int(val)) + } $else $if T is $int { + // TODO benchmark + mut abs_val := val + if val < 0 { + count.total++ // - + abs_val = -val + } + for number_value := abs_val; number_value >= 1; number_value /= 10 { + count.total++ + } + if val == 0 { + count.total++ + } + } $else $if T is $float { + // TODO + } $else $if T is bool { + if val { + count.total += 4 // true + } else { + count.total += 5 // false + } + } $else { + } +} + +// chars_in_struct +fn (mut count Count) chars_in_struct[T](val T) { + count.total += 2 // {} + $for field in T.fields { + // TODO handle attributes + count.total += field.name.len + 3 // "": + workaround := val.$(field.name) + count.count_chars(workaround) + } +} + +// chars_in_string +fn (mut count Count) chars_in_string(val string) { + count.total += val.len + 2 // "" +} diff --git a/vlib/x/json2/count_test.v b/vlib/x/json2/count_test.v new file mode 100644 index 0000000000..06f955420b --- /dev/null +++ b/vlib/x/json2/count_test.v @@ -0,0 +1,100 @@ +module json2 + +import time + +const fixed_time = time.Time{ + year: 2022 + month: 3 + day: 11 + hour: 13 + minute: 54 + second: 25 + unix: 1647006865 +} + +type StringAlias = string +type BoolAlias = bool +type IntAlias = int +type TimeAlias = time.Time +type StructAlias = StructType[int] +type EnumAlias = Enumerates + +type SumTypes = StructType[string] | []SumTypes | []string | bool | int | string | time.Time + +enum Enumerates { + a + b + c + d + e = 99 + f +} + +struct StructType[T] { +mut: + val T +} + +struct StructTypeOption[T] { +mut: + val ?T +} + +struct StructTypePointer[T] { +mut: + val &T +} + +fn count_test[T](value T) { + mut count := Count{0} + + count.count_chars(value) + assert encode(value).len == count.get_total() +} + +fn test_empty() { + count_test(map[string]string{}) + + count_test([]string{}) + + count_test(StructType[bool]{}) + + count_test(map[string]string{}) +} + +fn test_types() { + count_test(StructType[string]{}) + + count_test(StructType[string]{ val: '' }) + + count_test(StructType[string]{ val: 'abcd' }) + + count_test(StructType[bool]{ val: false }) + + count_test(StructType[bool]{ val: true }) + + count_test(StructType[int]{ val: 26 }) + + count_test(StructType[int]{ val: 1 }) + + count_test(StructType[int]{ val: -125 }) + + count_test(StructType[u64]{ val: u64(-1) }) + + count_test(StructType[time.Time]{}) + + count_test(StructType[time.Time]{ val: json2.fixed_time }) + + count_test(StructType[StructType[int]]{ + val: StructType[int]{ + val: 1 + } + }) + + count_test(StructType[Enumerates]{}) + count_test(StructType[Enumerates]{}) + count_test(StructType[Enumerates]{ val: Enumerates.f }) + count_test(StructType[[]int]{}) + count_test(StructType[[]int]{ val: [0] }) + count_test(StructType[[]int]{ val: [0, 1, 0, 2, 3, 2, 5, 1] }) +} diff --git a/vlib/x/json2/encoder.v b/vlib/x/json2/encoder.v index 3eb04abda4..5cd43f5cc3 100644 --- a/vlib/x/json2/encoder.v +++ b/vlib/x/json2/encoder.v @@ -53,7 +53,10 @@ pub fn encode[T](val T) string { $if T is $array { return encode_array(val) } $else { - mut buf := []u8{} + mut count := Count{0} + count.count_chars(val) + + mut buf := []u8{cap: count.total} defer { unsafe { buf.free() }