Ctrl K
ComunidadGuías…

vlib/json2/json5_allocations_test.c.v

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// vtest vflags: -gc none -d track_heap -nocache
module json2

import os

#include "@VMODROOT/vlib/json2/testdata/json5_heap_tracking.h"

fn C.json5_heap_live_bytes() u64

struct Json5PortOnly {
	port int
}

struct Json5KeyEncodingRecord {
	enabled bool
	unicode bool @[json: 'clé']
	quoted  bool @[json: 'with space']
}

@[manualfree]
fn assert_json5_unquoted_encoding_releases_temporaries[T](value T, expected string) {
	// Reuse caller-owned storage so the measurement isolates per-call scratch.
	mut output := []u8{cap: 4096}
	encode_append(value, mut output, json5: true, unquoted_keys: true)
	first := output.bytestr()
	assert first == expected
	unsafe { first.free() }
	before := C.json5_heap_live_bytes()
	for _ in 0 .. 100 {
		output.clear()
		encode_append(value, mut output, json5: true, unquoted_keys: true)
		result := output.bytestr()
		assert result == expected
		unsafe { result.free() }
	}
	assert C.json5_heap_live_bytes() == before
	unsafe { output.free() }
}

@[manualfree]
fn test_json5_unquoted_map_keys_release_encoding_temporaries() {
	value := {
		'$key0':      true
		'a\u0903':    true
		'a\u203f':    false
		'a\u00b2':    false
		'clé':        true
		'with space': false
		'\U00010341': true
	}
	assert_json5_unquoted_encoding_releases_temporaries(value,
		'{$key0:true,"a\u00b2":false,a\u0903:true,a\u203f:false,clé:true,"with space":false,\U00010341:true}')
}

@[manualfree]
fn test_json5_unquoted_struct_keys_release_encoding_temporaries() {
	value := Json5KeyEncodingRecord{
		enabled: true
		quoted:  true
	}
	assert_json5_unquoted_encoding_releases_temporaries(value,
		'{enabled:true,clé:false,"with space":true}')
}

@[manualfree]
fn test_json5_integer_decoding_releases_temporary_storage() {
	for input in ['123', "'123'", '0x7b', '+123', '-123'] {
		expected := if input == '-123' { -123 } else { 123 }
		assert decode[int](input, json5: true)! == expected
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 1000 {
			assert decode[int](input, json5: true)! == expected
		}
		assert C.json5_heap_live_bytes() == before
	}
}

@[manualfree]
fn test_json5_large_hexadecimal_float_decoding_releases_temporary_storage() {
	for input in ['0x10000000000000000', '-0x10000000000000000',
		'0x' + '0'.repeat(1024) + '10000000000000000'] {
		expected := if input.starts_with('-') {
			-18446744073709551616.0
		} else {
			18446744073709551616.0
		}
		assert decode[f64](input, json5: true)! == expected
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 100 {
			assert decode[f64](input, json5: true)! == expected
		}
		assert C.json5_heap_live_bytes() == before
	}
}

@[manualfree]
fn test_json5_reuse_releases_normalized_text_of_ignored_fields() {
	for prefix in ["{port:123,ignored:'", r"{\u0070ort:123,ig\u006eored:'"] {
		input := prefix + 'x'.repeat(8192) + "'}"
		mut buffer := DecodeBuffer{}
		assert decode_reuse[Json5PortOnly](input, mut buffer, json5: true)!.port == 123
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 100 {
			assert decode_reuse[Json5PortOnly](input, mut buffer, json5: true)!.port == 123
		}
		assert C.json5_heap_live_bytes() == before
		unsafe { buffer.values_info.free() }
	}
}

@[manualfree]
fn test_json5_string_results_own_only_their_returned_text() {
	input := "'" + 'x'.repeat(8192) + "'"
	first := decode[string](input, json5: true)!
	unsafe { first.free() }
	before := C.json5_heap_live_bytes()
	for _ in 0 .. 100 {
		decoded := decode[string](input, json5: true)!
		assert decoded.len == 8192
		unsafe { decoded.free() }
	}
	assert C.json5_heap_live_bytes() == before
}

@[manualfree]
fn test_decode_any_releases_serialization_scratch_for_numbers() {
	for json5 in [false, true] {
		for number in [0, 1, 123] {
			value := Any(number)
			input := number.str()
			assert decode_any[int](value, json5: json5)! == number
			// Plain JSON validation allocates EOF error sentinels even on success.
			// Measure direct decoding so the assertion isolates wrapper scratch storage.
			baseline := C.json5_heap_live_bytes()
			for _ in 0 .. 100 {
				assert decode[int](input, json5: json5)! == number
			}
			decoder_allocations := C.json5_heap_live_bytes() - baseline
			before := C.json5_heap_live_bytes()
			for _ in 0 .. 100 {
				assert decode_any[int](value, json5: json5)! == number
			}
			assert C.json5_heap_live_bytes() == before + decoder_allocations
		}
	}
}

@[manualfree]
fn test_decode_any_string_results_own_only_their_returned_text() {
	text := 'x'.repeat(8192)
	value := Any(text)
	for json5 in [false, true] {
		first := decode_any[string](value, json5: json5)!
		unsafe { first.free() }
		input := '"${text}"'
		baseline := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			decoded := decode[string](input, json5: json5)!
			unsafe { decoded.free() }
		}
		decoder_allocations := C.json5_heap_live_bytes() - baseline
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			decoded := decode_any[string](value, json5: json5)!
			assert decoded == text
			unsafe { decoded.free() }
		}
		assert C.json5_heap_live_bytes() == before + decoder_allocations
	}
}

@[manualfree]
fn test_decode_any_escaped_strings_release_encoding_temporaries() {
	text := '\x00\x01\n\r\t\b\f\\"' + rune(0x2028).str() + rune(0x2029).str() + 'é😀'
	value := Any(text)
	for json5 in [false, true] {
		input := encode(text, json5: json5)
		first := decode_any[string](value, json5: json5)!
		unsafe { first.free() }
		baseline := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			decoded := decode[string](input, json5: json5)!
			unsafe { decoded.free() }
		}
		decoder_allocations := C.json5_heap_live_bytes() - baseline
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			decoded := decode_any[string](value, json5: json5)!
			assert decoded == text
			unsafe { decoded.free() }
		}
		assert C.json5_heap_live_bytes() == before + decoder_allocations
	}
}

@[manualfree]
fn test_decode_file_releases_file_contents_for_numeric_results() {
	path := os.join_path(os.temp_dir(), 'json2_file_allocations_${os.getpid()}.json')
	defer { os.rm(path) or {} }
	input := ' '.repeat(8192) + '123'
	os.write_file(path, input)!
	for json5 in [false, true] {
		assert decode_file[int](path, json5: json5)! == 123
		baseline := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			assert decode[int](input, json5: json5)! == 123
		}
		decoder_allocations := C.json5_heap_live_bytes() - baseline
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			assert decode_file[int](path, json5: json5)! == 123
		}
		assert C.json5_heap_live_bytes() == before + decoder_allocations
	}
}

@[manualfree]
fn test_doc_decode_and_reflect_release_serialization_scratch() {
	for json5 in [false, true] {
		doc := new_doc(Any(123), json5: json5)
		assert doc.decode[int]()! == 123
		assert doc.reflect[int]() == 123
		baseline := C.json5_heap_live_bytes()
		for _ in 0 .. 200 {
			assert decode[int]('123', json5: json5)! == 123
		}
		decoder_allocations := C.json5_heap_live_bytes() - baseline
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 100 {
			assert doc.decode[int]()! == 123
			assert doc.reflect[int]() == 123
		}
		assert C.json5_heap_live_bytes() == before + decoder_allocations
	}
}

@[manualfree]
fn test_doc_decode_releases_scratch_for_large_ignored_fields() {
	value := Any({
		'port':    Any(123)
		'ignored': Any('x'.repeat(8192))
	})
	for json5 in [false, true] {
		doc := new_doc(value, json5: json5)
		input := encode(value, json5: json5)
		assert doc.decode[Json5PortOnly]()!.port == 123
		baseline := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			assert decode[Json5PortOnly](input, json5: json5)!.port == 123
		}
		decoder_allocations := C.json5_heap_live_bytes() - baseline
		before := C.json5_heap_live_bytes()
		for _ in 0 .. 10 {
			assert doc.decode[Json5PortOnly]()!.port == 123
		}
		assert C.json5_heap_live_bytes() == before + decoder_allocations
	}
}