diff options
Diffstat (limited to 'research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64')
15 files changed, 37382 insertions, 0 deletions
diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/complete.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/complete.json new file mode 100644 index 0000000..5bde4c3 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/complete.json @@ -0,0 +1,5 @@ +{ + "samples": 210, + "all_branch_outputs_match": true, + "finished_utc": "2026-09-22T13:11:31Z" +} diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/environment.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/environment.json new file mode 100644 index 0000000..3aa2455 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/environment.json @@ -0,0 +1,96 @@ +{ + "platform": "Linux-6.18.39+rpt-rpi-2712-aarch64-with-glibc2.41", + "machine": "aarch64", + "python": "3.14.7", + "compiler": "cc (Debian 14.2.0-19) 14.2.0\nCopyright (C) 2024 Free Software Foundation, Inc.\nThis is free software; see the source for copying conditions. There is NO\nwarranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n", + "cpu_affinity": 0, + "builds": [ + { + "command": [ + "cc", + "-std=c17", + "-O2", + "-g", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Werror", + "-DYYJSON_DISABLE_WRITER=1", + "-DYYJSON_DISABLE_UTILS=1", + "-DYYJSON_DISABLE_INCR_READER=1", + "/home/eunhhu/work/pure/research/experiments/003-json-fanout/benchmark.c", + "/home/eunhhu/work/pure/research/experiments/003-json-fanout/vendor/yyjson/yyjson.c", + "-o", + "/tmp/pure-fanout-i4xs4fhg/benchmark" + ], + "stderr": "", + "sha256": "adfd209a990abe11bfd93337c56eb2c9266d6529899fc91cd2f3844c93ef67db", + "size_bytes": 842056, + "build_wall_ns": 15110774980 + }, + { + "command": [ + "cc", + "-std=c17", + "-O2", + "-g", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Werror", + "-DYYJSON_DISABLE_WRITER=1", + "-DYYJSON_DISABLE_UTILS=1", + "-DYYJSON_DISABLE_INCR_READER=1", + "-O1", + "-fsanitize=address,undefined", + "-fno-omit-frame-pointer", + "/home/eunhhu/work/pure/research/experiments/003-json-fanout/benchmark.c", + "/home/eunhhu/work/pure/research/experiments/003-json-fanout/vendor/yyjson/yyjson.c", + "-o", + "/tmp/pure-fanout-i4xs4fhg/benchmark-sanitized" + ], + "stderr": "", + "sha256": "c7516cf3c1c18463c72b96a75d70cc326a0ff43873fa278e0b13fb94d6e180d2", + "size_bytes": 5441872, + "build_wall_ns": 160671462690 + } + ], + "source_sha256": { + "benchmark.c": "c9ccc4544ef296eb59a8d071ad7fb25afc4b3fe5a94a8978c415c557b8ea8d0d", + "run.py": "7ab64e454f3ce0a48c20a477d8924b06ece6ad29a442d0bb49f48b5a6ecffe49", + "plan.json": "69f1258b3afd616404c6853526a5fbb6523d406eb43db479fd4be43905bc9f5d", + "vendor/yyjson/UPSTREAM.json": "eab60a505ff59ca1f3271792c16aa57d61f596cb5c57ff04dcff15d22ba29cee", + "vendor/yyjson/yyjson.c": "ac2e9bbb2e2d9149d90878d40506a1d624fa0b33c979a11b61075c54782c6d6a", + "vendor/yyjson/yyjson.h": "175867c5493a5df648cec566717fa1c29aa2f6096f5f0cf1efad0b65e1f6d7b3", + "vendor/yyjson/LICENSE": "45e384d3d52c73cba3a64d6e6c25d47cd738cd8a55c30629e3201046eda62947" + }, + "upstream": { + "project": "yyjson", + "version": "0.12.0", + "commit": "8b4a38dc994a110abaec8a400615567bd996105f", + "license": "MIT", + "files": { + "yyjson.c": { + "url": "https://raw.githubusercontent.com/ibireme/yyjson/8b4a38dc994a110abaec8a400615567bd996105f/src/yyjson.c", + "sha256": "ac2e9bbb2e2d9149d90878d40506a1d624fa0b33c979a11b61075c54782c6d6a", + "bytes": 410813 + }, + "yyjson.h": { + "url": "https://raw.githubusercontent.com/ibireme/yyjson/8b4a38dc994a110abaec8a400615567bd996105f/src/yyjson.h", + "sha256": "175867c5493a5df648cec566717fa1c29aa2f6096f5f0cf1efad0b65e1f6d7b3", + "bytes": 322407 + }, + "LICENSE": { + "url": "https://raw.githubusercontent.com/ibireme/yyjson/8b4a38dc994a110abaec8a400615567bd996105f/LICENSE", + "sha256": "45e384d3d52c73cba3a64d6e6c25d47cd738cd8a55c30629e3201046eda62947", + "bytes": 1084 + } + } + }, + "timing": "I/O, parsing, selection, consumers, instrumentation, cleanup; excludes setup/build/verification", + "parse_call_ns": "wall duration inside yyjson_read_opts including allocator backpressure and serviced consumers", + "rss": "Linux current-image VmHWM approximate; zero means unavailable on other OSes", + "limits": "synthetic closed-loop arrivals/delays; one thread; uncontrolled thermals/power; cached file I/O; no fsync", + "started_utc": "2026-09-22T13:10:45Z", + "cpuinfo": "processor\t: 0\nBogoMIPS\t: 108.00\nFeatures\t: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp\nCPU implementer\t: 0x41\nCPU architecture: 8\nCPU variant\t: 0x4\nCPU part\t: 0xd0b\nCPU revision\t: 1\n\nprocessor\t: 1\nBogoMIPS\t: 108.00\nFeatures\t: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp\nCPU implementer\t: 0x41\nCPU architecture: 8\nCPU variant\t: 0x4\nCPU part\t: 0xd0b\nCPU revision\t: 1\n\nprocessor\t: 2\nBogoMIPS\t: 108.00\nFeatures\t: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp\nCPU implementer\t: 0x41\nCPU architecture: 8\nCPU variant\t: 0x4\nCPU part\t: 0xd0b\nCPU revision\t: 1\n\nprocessor\t: 3\nBogoMIPS\t: 108.00\nFeatures\t: fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm lrcpc dcpop asimddp\nCPU implementer\t: 0x41\nCPU architecture: 8\nCPU variant\t: 0x4\nCPU part\t: 0xd0b\nCPU revision\t: 1\n\nRevision\t: d04171\nSerial\t\t: d3ba8e625dbe42ac\nModel\t\t: Raspberry Pi 5 Model B Rev 1.1\n" +} diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/fixtures.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/fixtures.json new file mode 100644 index 0000000..dbd23e2 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/fixtures.json @@ -0,0 +1,128 @@ +[ + { + "case": "one_slow", + "seed": 20260923, + "records": 1024, + "input_bytes": 42083156, + "input_sha256": "ebd22d4a12c1369ce27459e454df0a93545ca816fe13a82401160130772a3bfd", + "keys": [ + "tag" + ], + "output_bytes_by_branch": [ + 57344 + ], + "output_sha256_by_branch": [ + "1acead91fea700e5a94dc4285a37251f71876a0b505d2638cae3b9abf99915dd" + ] + }, + { + "case": "three_distinct_speeds", + "seed": 20260923, + "records": 1024, + "input_bytes": 42083156, + "input_sha256": "ebd22d4a12c1369ce27459e454df0a93545ca816fe13a82401160130772a3bfd", + "keys": [ + "body", + "tag", + "tag" + ], + "output_bytes_by_branch": [ + 33554432, + 57344, + 57344 + ], + "output_sha256_by_branch": [ + "a48ec20b430e737841289f18d08572d3630f92bac4778c7b0aa5d23acf5361bd", + "1acead91fea700e5a94dc4285a37251f71876a0b505d2638cae3b9abf99915dd", + "1acead91fea700e5a94dc4285a37251f71876a0b505d2638cae3b9abf99915dd" + ] + }, + { + "case": "same_large_field", + "seed": 20260923, + "records": 1024, + "input_bytes": 42083156, + "input_sha256": "ebd22d4a12c1369ce27459e454df0a93545ca816fe13a82401160130772a3bfd", + "keys": [ + "body", + "body", + "body" + ], + "output_bytes_by_branch": [ + 33554432, + 33554432, + 33554432 + ], + "output_sha256_by_branch": [ + "a48ec20b430e737841289f18d08572d3630f92bac4778c7b0aa5d23acf5361bd", + "a48ec20b430e737841289f18d08572d3630f92bac4778c7b0aa5d23acf5361bd", + "a48ec20b430e737841289f18d08572d3630f92bac4778c7b0aa5d23acf5361bd" + ] + }, + { + "case": "slow_large_field", + "seed": 20260923, + "records": 1024, + "input_bytes": 42083156, + "input_sha256": "ebd22d4a12c1369ce27459e454df0a93545ca816fe13a82401160130772a3bfd", + "keys": [ + "tag", + "tag", + "body" + ], + "output_bytes_by_branch": [ + 57344, + 57344, + 33554432 + ], + "output_sha256_by_branch": [ + "1acead91fea700e5a94dc4285a37251f71876a0b505d2638cae3b9abf99915dd", + "1acead91fea700e5a94dc4285a37251f71876a0b505d2638cae3b9abf99915dd", + "a48ec20b430e737841289f18d08572d3630f92bac4778c7b0aa5d23acf5361bd" + ] + }, + { + "case": "mixed_tight", + "seed": 20260923, + "records": 1024, + "input_bytes": 49877076, + "input_sha256": "3bba97318415a0c32a6eb61b04b7d73214ffcca90ae00469b45c2db6b28f600c", + "keys": [ + "body", + "tag", + "tag" + ], + "output_bytes_by_branch": [ + 26757376, + 57344, + 57344 + ], + "output_sha256_by_branch": [ + "89a5a0467e4473dd4afb7307366793ba4840463a6e9d44cd1f13090f2775bac9", + "f9bdd5932d1e9201f9dfeaf5e85d14572740d5a13dd31491654f75dd4999b5cf", + "f9bdd5932d1e9201f9dfeaf5e85d14572740d5a13dd31491654f75dd4999b5cf" + ] + }, + { + "case": "metadata_heavy", + "seed": 20260923, + "records": 512, + "input_bytes": 5259666, + "input_sha256": "b3ddc9f18b946c466f3b5819538de80e062f734fcba70d2ed578445b0bd5392f", + "keys": [ + "body", + "tag", + "tag" + ], + "output_bytes_by_branch": [ + 262144, + 28672, + 28672 + ], + "output_sha256_by_branch": [ + "9aa5e0f4c17a62255942c9d23be621e437630e5fa34ef908d7c88033e132d700", + "a65807b99ca84b43058312cfdd6892e252bb47585f37f257f7dae2dcd7ee4be2", + "a65807b99ca84b43058312cfdd6892e252bb47585f37f257f7dae2dcd7ee4be2" + ] + } +] diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/plan.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/plan.json new file mode 100644 index 0000000..3ee7b4a --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/plan.json @@ -0,0 +1,177 @@ +{ + "version": 1, + "written_before_measurement": true, + "seed": 20260923, + "repeats": 5, + "policies": [ + { + "name": "borrow", + "policy": "borrow" + }, + { + "name": "copy", + "policy": "copy" + }, + { + "name": "shared_copy", + "policy": "shared_copy" + }, + { + "name": "threshold2", + "policy": "threshold", + "ratio": 2 + }, + { + "name": "threshold8", + "policy": "threshold", + "ratio": 8 + }, + { + "name": "compact", + "policy": "compact", + "ratio": 2 + }, + { + "name": "copy_no_pool", + "policy": "copy", + "pool": 0 + } + ], + "cases": [ + { + "name": "one_slow", + "shape": "large", + "records": 1024, + "keys": [ + "tag" + ], + "hold_us": [ + 8000 + ], + "service_us": [ + 0 + ], + "budget": 8388608, + "slots": 64 + }, + { + "name": "three_distinct_speeds", + "shape": "large", + "records": 1024, + "keys": [ + "body", + "tag", + "tag" + ], + "hold_us": [ + 0, + 5000, + 8000 + ], + "service_us": [ + 0, + 0, + 0 + ], + "budget": 8388608, + "slots": 64 + }, + { + "name": "same_large_field", + "shape": "large", + "records": 1024, + "keys": [ + "body", + "body", + "body" + ], + "hold_us": [ + 0, + 5000, + 8000 + ], + "service_us": [ + 0, + 0, + 0 + ], + "budget": 8388608, + "slots": 64 + }, + { + "name": "slow_large_field", + "shape": "large", + "records": 1024, + "keys": [ + "tag", + "tag", + "body" + ], + "hold_us": [ + 0, + 5000, + 8000 + ], + "service_us": [ + 0, + 0, + 0 + ], + "budget": 8388608, + "slots": 64 + }, + { + "name": "mixed_tight", + "shape": "mixed", + "records": 1024, + "keys": [ + "body", + "tag", + "tag" + ], + "hold_us": [ + 0, + 5000, + 8000 + ], + "service_us": [ + 0, + 0, + 0 + ], + "budget": 2097152, + "slots": 64 + }, + { + "name": "metadata_heavy", + "shape": "metadata", + "records": 512, + "keys": [ + "body", + "tag", + "tag" + ], + "hold_us": [ + 0, + 5000, + 8000 + ], + "service_us": [ + 0, + 0, + 0 + ], + "budget": 2097152, + "slots": 64 + } + ], + "expectations": [ + "A slow small-field consumer can retain the input after a fast large-field consumer finishes.", + "Sharing identical compact copies can remove duplicate per-consumer copies without a new runtime.", + "Late compaction may save copied bytes or fail to beat early size-based copying; all remaining owners matter.", + "Parser temporary memory and reader growth must be charged, even when retained selections are small.", + "No novel-policy advantage or production service SLO is presumed." + ], + "comparison_rule": "Report all fixed presets; exploratory measurements, no trained/held-out policy or significance claim.", + "limits": "Real yyjson parser and actual bytes, but synthetic NDJSON corpus and sink delays, three synchronous consumers in one event loop, no open-loop arrivals, thread races, durable output, or independent parser comparison." +} diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/raw.csv b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/raw.csv new file mode 100644 index 0000000..d2b9a02 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/raw.csv @@ -0,0 +1,211 @@ +case,variant,repeat,status,policy,parser_version,budget_bytes,fixed_bytes,peak_reserved_bytes,source_peak_bytes,output_peak_bytes,parser_peak_bytes,rss_peak_bytes,elapsed_ns,cpu_ns,parse_call_ns,accepted,emitted,input_bytes,selection_copy_bytes,reader_copy_bytes,parser_realloc_copy_bytes,source_byte_seconds,output_byte_seconds,parser_byte_seconds,source_allocations,output_allocations,parser_allocations,reuses,budget_waits,slot_waits,compacted,compact_skipped,dedup_hits,sleep_ns,live_at_stop_bytes,unreleased_buffer_bytes,queues
+one_slow,threshold2,0,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1835008,131703634,34750648,21989760,1024,1024,42083156,57344,75711296,0,1842.615404,919.75921,2883.244752,9,64,1,6056,0,16,0,0,0,97097912,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8051720.034, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,compact,0,ok,compact,0.12.0,8388608,33536,4427696,4230192,0,131120,5537792,131996119,38004572,22434299,1024,1024,42083156,0,75711296,0,540791.270719,0.0,2928.256675,72,0,1,5033,0,26,0,0,0,94192606,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8057605.331, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,borrow,0,ok,borrow,0.12.0,8388608,33536,4427696,4230192,0,131120,5521408,132124751,37612015,22444229,1024,1024,42083156,0,75711296,0,540845.471315,0.0,2930.090388,72,0,1,5033,0,53,0,0,0,94791711,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8058299.849, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,threshold8,0,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131620844,34446035,21905479,1024,1024,42083156,57344,75711296,0,1832.609103,920.078119,2872.388672,9,64,1,6056,0,16,0,0,0,97293567,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054286.008, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy_no_pool,0,ok,copy,0.12.0,8388608,33536,237296,98400,7168,131120,1835008,132233158,41767998,24756316,1024,1024,42083156,57344,75711296,0,2069.543688,920.140424,2901.308237,4082,1024,1024,0,0,19,0,0,0,90619067,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8059175.662, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy,0,ok,copy,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131600270,34455850,21964978,1024,1024,42083156,57344,75711296,0,1832.537919,920.025416,2880.380829,9,64,1,6056,0,16,0,0,0,97270417,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8053796.131, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,shared_copy,0,ok,shared_copy,0.12.0,8388608,33536,303072,98400,7168,131120,1802240,131715584,34456703,21940218,1024,1024,42083156,57344,75711296,0,1833.007769,920.07894,2877.016945,9,64,1,6056,0,16,0,0,0,97393619,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054276.953, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,compact,1,ok,compact,0.12.0,8388608,33536,4427696,4230192,0,131120,5537792,132171843,38070312,22523797,1024,1024,42083156,0,75711296,0,540815.06589,0.0,2937.269732,72,0,1,5033,0,40,0,0,0,94338529,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8057936.641, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy_no_pool,1,ok,copy,0.12.0,8388608,33536,237296,98400,7168,131120,1835008,132199048,42202499,24805054,1024,1024,42083156,57344,75711296,0,2084.967873,920.132302,2905.942804,4082,1024,1024,0,0,29,0,0,0,90146770,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8059478.906, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,shared_copy,1,ok,shared_copy,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131708165,34804961,22044118,1024,1024,42083156,57344,75711296,0,1844.634105,919.759432,2890.857973,9,64,1,6056,0,16,0,0,0,97053198,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8051751.524, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,threshold8,1,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131798127,34803856,22044512,1024,1024,42083156,57344,75711296,0,1846.36305,919.612987,2891.382584,9,64,1,6056,0,16,0,0,0,97156768,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050483.774, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,borrow,1,ok,borrow,0.12.0,8388608,33536,4427696,4230192,0,131120,5521408,131976979,38591495,22573964,1024,1024,42083156,0,75711296,0,540860.949553,0.0,2940.513903,72,0,1,5033,0,28,0,0,0,93626665,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8058679.423, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy,1,ok,copy,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131752053,34888537,21974890,1024,1024,42083156,57344,75711296,0,1845.194241,919.599974,2880.87266,9,64,1,6056,0,17,0,0,0,97030621,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050419.036, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,threshold2,1,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131720609,34747849,21999728,1024,1024,42083156,57344,75711296,0,1843.805295,919.628727,2884.923744,9,64,1,6056,0,16,0,0,0,97113750,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050578.244, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy,2,ok,copy,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131735071,34877739,22030534,1024,1024,42083156,57344,75711296,0,1847.409016,919.636562,2888.89668,9,64,1,6056,0,16,0,0,0,96987832,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050770.82, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,compact,2,ok,compact,0.12.0,8388608,33536,4427696,4230192,0,131120,5537792,132186905,38709650,22486562,1024,1024,42083156,0,75711296,0,540826.901328,0.0,2931.839266,72,0,1,5033,0,36,0,0,0,93736446,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8058254.455, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,shared_copy,2,ok,shared_copy,0.12.0,8388608,33536,303072,98400,7168,131120,1835008,134003964,36820353,22141327,1024,1024,42083156,57344,75711296,0,1907.646695,924.396419,2903.071145,9,116,1,6004,0,25,0,0,0,97345374,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8093864.112, ""latency_p99_upper_ns"": 9437184}]"
+one_slow,threshold2,2,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,132093280,37140317,22338927,1024,1024,42083156,57344,75711296,0,1928.638533,919.864631,2930.310014,9,68,1,6052,0,20,0,0,0,95013211,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054865.09, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,borrow,2,ok,borrow,0.12.0,8388608,33536,4427696,4230192,0,131120,5554176,132469040,41862764,22964342,1024,1024,42083156,0,75711296,0,541099.43903,0.0,2986.938382,73,0,1,5032,0,49,0,0,0,90878204,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8063020.332, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy_no_pool,2,ok,copy,0.12.0,8388608,33536,237296,98400,7168,131120,1802240,137450461,46306762,25720817,1024,1024,42083156,57344,75711296,0,2217.245474,934.97033,2945.941354,4082,1024,1024,0,0,23,0,0,0,91429169,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8191749.104, ""latency_p99_upper_ns"": 11534336}]"
+one_slow,threshold8,2,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131976855,36997295,22056693,1024,1024,42083156,57344,75711296,0,1904.528214,920.059714,2891.295258,9,65,1,6055,0,21,0,0,0,95230128,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8056258.001, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,threshold2,3,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,132144262,36810738,22394390,1024,1024,42083156,57344,75711296,0,1914.610493,920.080986,2937.462741,9,71,1,6049,0,39,0,0,0,95437850,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8056316.891, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,threshold8,3,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131627799,34748983,22050963,1024,1024,42083156,57344,75711296,0,1845.118437,920.020651,2891.313221,9,64,1,6056,0,19,0,0,0,96996391,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054019.278, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,compact,3,ok,compact,0.12.0,8388608,33536,4427696,4230192,0,131120,5537792,132093080,38378509,22460168,1024,1024,42083156,0,75711296,0,540907.798619,0.0,2928.186919,72,0,1,5033,0,43,0,0,0,93965206,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8059384.654, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy_no_pool,3,ok,copy,0.12.0,8388608,33536,237296,98400,7168,131120,1802240,132246841,41768202,24673720,1024,1024,42083156,57344,75711296,0,2066.312998,920.19798,2896.434244,4082,1024,1024,0,0,16,0,0,0,90608121,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8059680.607, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy,3,ok,copy,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131689453,35243759,22012604,1024,1024,42083156,57344,75711296,0,1857.757504,919.947028,2887.51415,9,68,1,6052,0,27,0,0,0,96622803,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8053765.222, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,shared_copy,3,ok,shared_copy,0.12.0,8388608,33536,303072,98400,7168,131120,1802240,132103712,35248408,22004780,1024,1024,42083156,57344,75711296,0,1858.44117,919.973445,2885.070598,9,73,1,6047,0,22,0,0,0,97018510,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054006.321, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,borrow,3,ok,borrow,0.12.0,8388608,33536,4427696,4230192,0,131120,5537792,132436008,40869521,22656213,1024,1024,42083156,0,75711296,0,540941.338269,0.0,2953.318689,75,0,1,5030,0,52,0,0,0,91954404,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8060462.982, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,compact,4,ok,compact,0.12.0,8388608,33536,4427696,4230192,0,131120,5554176,132110379,38257181,22557256,1024,1024,42083156,0,75711296,0,540811.125254,0.0,2944.323595,72,0,1,5033,0,56,0,0,0,94112952,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8057929.717, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,threshold8,4,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1802240,131804916,34977352,22094634,1024,1024,42083156,57344,75711296,0,1853.831647,920.149467,2897.713188,9,67,1,6053,0,31,0,0,0,96937037,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8055361.395, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy_no_pool,4,ok,copy,0.12.0,8388608,33536,237296,98400,7168,131120,1818624,135161451,44330024,25503982,1024,1024,42083156,57344,75711296,0,2157.809223,924.543547,2946.975235,4082,1024,1024,0,0,35,0,0,0,91118882,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8099348.558, ""latency_p99_upper_ns"": 10485760}]"
+one_slow,threshold2,4,ok,threshold,0.12.0,8388608,33536,303072,98400,7168,131120,1802240,132588310,37673671,22518430,1024,1024,42083156,57344,75711296,0,1949.11739,920.155569,2954.042078,9,78,1,6042,0,36,0,0,0,94895218,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8057756.78, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,copy,4,ok,copy,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,131620718,34580946,21993246,1024,1024,42083156,57344,75711296,0,1837.245494,919.925321,2884.001314,9,64,1,6056,0,16,0,0,0,97154068,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8053071.22, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,borrow,4,ok,borrow,0.12.0,8388608,33536,4427696,4230192,0,131120,5537792,132000791,37927424,22507290,1024,1024,42083156,0,75711296,0,540831.163899,0.0,2938.768696,72,0,1,5033,0,30,0,0,0,94241105,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8058140.749, ""latency_p99_upper_ns"": 8388608}]"
+one_slow,shared_copy,4,ok,shared_copy,0.12.0,8388608,33536,303072,98400,7168,131120,1818624,132283829,37157688,22116989,1024,1024,42083156,57344,75711296,0,1917.711679,920.053817,2900.603467,9,67,1,6053,0,38,0,0,0,95415784,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8056291.708, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,borrow,0,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,5373952,132804710,49883960,22784485,1024,3072,42083156,0,75711296,0,540593.356702,0.0,2990.235132,72,0,1,5033,0,578,0,0,0,84130488,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 45505.588, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070252.164, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8055489.045, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,compact,0,ok,compact,0.12.0,8388608,41728,4436784,4230192,6944,131120,5537792,132699783,50261222,22546544,1024,3072,42083156,57344,75711296,0,340105.36302,342.277671,2958.801865,897,913,1,4319,0,550,1024,0,0,83652241,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 45463.711, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069982.729, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054576.42, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,shared_copy,0,ok,shared_copy,0.12.0,8388608,41728,311264,98400,39984,131120,1835008,132438636,46508730,22104890,1024,3072,42083156,33611776,75711296,0,1994.687521,1188.73405,2903.952009,9,64,1,7080,0,538,0,0,1024,87037859,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42492.519, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069205.345, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8052022.729, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy,0,ok,copy,0.12.0,8388608,41728,318432,98400,47152,131120,1851392,132301692,46274024,22012014,1024,3072,42083156,33669120,75711296,0,1991.776071,1766.57547,2891.89841,9,1025,1,7143,0,563,0,0,0,87083196,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42221.575, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068352.743, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8049671.308, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy_no_pool,0,ok,copy,0.12.0,8388608,41728,252544,98400,47152,131120,1933312,132221655,46432790,22094470,1024,3072,42083156,33669120,75711296,0,2002.66499,1764.18462,2896.537304,4082,3072,1024,0,0,534,0,0,0,86822233,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42461.52, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068300.921, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054065.407, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold2,0,ok,threshold,0.12.0,8388608,41728,311264,98400,39984,131120,1851392,132423215,46202689,22137673,1024,3072,42083156,33611776,75711296,0,1992.776523,1183.803646,2909.259091,9,64,1,7080,0,559,0,0,1024,87298460,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42209.067, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068504.791, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8052025.9, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold8,0,ok,threshold,0.12.0,8388608,41728,311264,98400,7168,131120,1851392,132072752,44818714,22007505,1024,3072,42083156,57344,75711296,0,2372.402719,919.579066,2891.111165,9,64,1,6056,0,531,0,0,1024,88271004,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 41081.91, ""latency_p99_upper_ns"": 49152}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067409.945, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8052229.961, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy,1,ok,copy,0.12.0,8388608,41728,318432,98400,47152,131120,1851392,132122049,45960158,22062914,1024,3072,42083156,33669120,75711296,0,1985.913742,1766.681864,2899.647733,9,1025,1,7143,0,530,0,0,0,87159643,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42080.341, ""latency_p99_upper_ns"": 53248}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068084.019, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8053834.564, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy_no_pool,1,ok,copy,0.12.0,8388608,41728,252544,98400,47152,131120,1884160,132225178,46345346,22106440,1024,3072,42083156,33669120,75711296,0,1998.568774,1764.360213,2898.609,4082,3072,1024,0,0,531,0,0,0,86929226,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42412.925, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068204.178, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8052346.592, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold2,1,ok,threshold,0.12.0,8388608,41728,311264,98400,39984,131120,1851392,132119197,45785542,22119160,1024,3072,42083156,33611776,75711296,0,1985.975023,1182.61037,2906.107753,9,64,1,7080,0,535,0,0,1024,87289352,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42063.476, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068193.573, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8051854.179, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,compact,1,ok,compact,0.12.0,8388608,41728,4436784,4230192,6944,131120,5603328,132689456,50225388,22679397,1024,3072,42083156,57344,75711296,0,340115.562513,342.313483,2976.797692,896,908,1,4325,0,548,1024,0,0,83654110,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 45534.298, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070135.917, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8055038.37, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold8,1,ok,threshold,0.12.0,8388608,41728,311264,98400,7168,131120,1851392,132687220,45610919,22143479,1024,3072,42083156,57344,75711296,0,2398.303122,919.310014,2908.551305,9,73,1,6047,0,557,0,0,1024,88180228,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 41592.506, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067924.312, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050249.576, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,shared_copy,1,ok,shared_copy,0.12.0,8388608,41728,311264,98400,39984,131120,1851392,133179387,47009567,22053622,1024,3072,42083156,33611776,75711296,0,2001.098596,1195.094634,2898.481421,9,64,1,7080,0,619,0,0,1024,87400987,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42811.697, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068344.258, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054983.926, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,borrow,1,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,5373952,132590517,49691116,22667622,1024,3072,42083156,0,75711296,0,540625.036225,0.0,2974.002082,72,0,1,5033,0,555,0,0,0,84069041,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 45356.652, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069262.154, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8055874.806, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,borrow,2,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,5373952,132595906,48936945,22622066,1024,3072,42083156,0,75711296,0,540538.226737,0.0,2969.006017,72,0,1,5033,0,550,0,0,0,84820014,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 44590.665, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069713.765, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054486.477, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold2,2,ok,threshold,0.12.0,8388608,41728,311264,98400,39984,131120,1851392,132243573,45674856,22029775,1024,3072,42083156,33611776,75711296,0,1978.146096,1181.588579,2894.51268,9,64,1,7080,0,547,0,0,1024,87607899,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 41881.888, ""latency_p99_upper_ns"": 53248}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067912.655, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8049931.179, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,compact,2,ok,compact,0.12.0,8388608,41728,4436784,4230192,6944,131120,5603328,132732463,49739534,22671992,1024,3072,42083156,57344,75711296,0,340105.112239,342.468555,2974.922545,893,887,1,4349,0,562,1024,0,0,84142850,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 44979.318, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069891.561, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8056164.197, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold8,2,ok,threshold,0.12.0,8388608,41728,311264,98400,7168,131120,1851392,132272059,45320979,22141356,1024,3072,42083156,57344,75711296,0,2389.486333,919.390876,2908.882515,9,64,1,6056,0,563,0,0,1024,88007671,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 41406.16, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068143.112, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050851.553, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy,2,ok,copy,0.12.0,8388608,41728,318432,98400,47152,131120,1867776,132351078,46122525,22082400,1024,3072,42083156,33669120,75711296,0,1991.903399,1762.771138,2900.645032,9,1026,1,7142,0,558,0,0,0,87297075,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42091.413, ""latency_p99_upper_ns"": 53248}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068444.485, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8048834.24, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,shared_copy,2,ok,shared_copy,0.12.0,8388608,41728,311264,98400,39984,131120,1851392,132284819,45823432,22066434,1024,3072,42083156,33611776,75711296,0,1983.569581,1182.323172,2898.432644,9,64,1,7080,0,542,0,0,1024,87424368,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42064.699, ""latency_p99_upper_ns"": 53248}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068229.227, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050900.843, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy_no_pool,2,ok,copy,0.12.0,8388608,41728,252544,98400,47152,131120,1900544,132459707,47692750,22170762,1024,3072,42083156,33669120,75711296,0,2023.677274,1777.322506,2907.132194,4082,3072,1024,0,0,540,0,0,0,85990890,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 43327.485, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070694.294, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8055086.674, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,shared_copy,3,ok,shared_copy,0.12.0,8388608,41728,311264,98400,39984,131120,1851392,132140503,45825210,22125197,1024,3072,42083156,33611776,75711296,0,1985.69773,1183.215978,2906.687041,9,64,1,7080,0,533,0,0,1024,87310088,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42062.03, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068036.782, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050191.375, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy_no_pool,3,ok,copy,0.12.0,8388608,41728,252544,98400,47152,131120,1916928,132297747,46669804,22175323,1024,3072,42083156,33669120,75711296,0,2009.648765,1766.146335,2907.094824,4082,3072,1024,0,0,542,0,0,0,86663675,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42678.905, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068637.263, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054142.04, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy,3,ok,copy,0.12.0,8388608,41728,318432,98400,47152,131120,1851392,132282510,46415608,22079359,1024,3072,42083156,33669120,75711296,0,1996.444688,1765.761752,2901.17633,9,1025,1,7143,0,554,0,0,0,86986908,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42277.028, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070038.574, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050279.443, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,borrow,3,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,5373952,132489898,48808681,22654163,1024,3072,42083156,0,75711296,0,540484.370521,0.0,2972.753951,72,0,1,5033,0,556,0,0,0,84863248,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 44578.165, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069410.422, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8053620.063, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold2,3,ok,threshold,0.12.0,8388608,41728,311264,98400,39984,131120,1835008,132283398,45911917,22085581,1024,3072,42083156,33611776,75711296,0,1984.871517,1183.416247,2903.347153,9,64,1,7080,0,561,0,0,1024,87433143,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42043.254, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068356.958, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8050581.195, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold8,3,ok,threshold,0.12.0,8388608,41728,311264,98400,7168,131120,1851392,132082805,44815017,22070822,1024,3072,42083156,57344,75711296,0,2374.331913,919.214871,2899.672121,9,64,1,6056,0,534,0,0,1024,88275454,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 41070.548, ""latency_p99_upper_ns"": 53248}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067369.015, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8049008.122, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,compact,3,ok,compact,0.12.0,8388608,41728,4436784,4230192,7056,131120,5488640,132597323,49981497,22749218,1024,3072,42083156,57344,75711296,0,340108.105991,342.554096,2986.081381,912,907,1,4310,0,563,1024,0,0,83819563,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 45265.223, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069991.558, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8056976.805, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy_no_pool,4,ok,copy,0.12.0,8388608,41728,252544,98400,47152,131120,1900544,132985160,49981054,23442854,1024,3072,42083156,33669120,75711296,0,2129.142526,1795.211172,3074.346252,4082,3072,1024,0,0,542,0,0,0,84089494,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 45510.754, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5098662.604, ""latency_p99_upper_ns"": 6291456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8056999.864, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,borrow,4,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,5373952,133433884,51462618,23747967,1024,3072,42083156,0,75711296,0,540814.68462,0.0,3118.422109,72,0,1,5033,0,596,0,0,0,83315856,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 46811.327, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070925.9, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8058845.673, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold2,4,ok,threshold,0.12.0,8388608,41728,311264,98400,39984,131120,1867776,132186422,45811373,22083462,1024,3072,42083156,33611776,75711296,0,1983.12097,1182.647891,2900.448352,9,64,1,7080,0,532,0,0,1024,87421696,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42006.171, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068025.061, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8049545.414, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,shared_copy,4,ok,shared_copy,0.12.0,8388608,41728,311264,98400,39984,131120,1851392,132188663,45991210,22145612,1024,3072,42083156,33611776,75711296,0,1991.371251,1184.961172,2909.800223,9,64,1,7080,0,543,0,0,1024,87186189,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42139.616, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068490.238, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8052428.242, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,compact,4,ok,compact,0.12.0,8388608,41728,4436784,4230192,7056,131120,5537792,132746292,50870250,22660664,1024,3072,42083156,57344,75711296,0,340246.674827,342.038296,2973.982939,893,895,1,4341,0,545,1024,0,0,83141858,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 45809.599, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5072116.599, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8054634.315, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,threshold8,4,ok,threshold,0.12.0,8388608,41728,311264,98400,7168,131120,1851392,132177385,44977578,22128368,1024,3072,42083156,57344,75711296,0,2381.095196,919.555429,2907.397974,9,64,1,6056,0,539,0,0,1024,88187889,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 41268.295, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067698.961, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8052209.973, ""latency_p99_upper_ns"": 8388608}]"
+three_distinct_speeds,copy,4,ok,copy,0.12.0,8388608,41728,318432,98400,47152,131120,1867776,132225648,46118905,22053057,1024,3072,42083156,33669120,75711296,0,1988.501642,1767.427593,2896.491937,9,1025,1,7143,0,536,0,0,0,87128865,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 42165.024, ""latency_p99_upper_ns"": 53248}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068266.918, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8051060.116, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy_no_pool,0,ok,copy,0.12.0,8388608,41728,4373248,98400,4233264,131120,6078464,216945564,116157398,25703347,1024,3072,42083156,100663296,75711296,0,3917.681175,512250.563648,3150.964519,4082,3072,1024,0,0,383,0,0,0,48939452,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 117402.264, ""latency_p99_upper_ns"": 524288}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 6063368.282, ""latency_p99_upper_ns"": 31457280}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 9260486.827, ""latency_p99_upper_ns"": 33554432}]"
+same_large_field,shared_copy,0,ok,shared_copy,0.12.0,8388608,41728,2371504,98400,2100224,131120,3178496,133534289,66640259,22303696,1024,3072,42083156,33554432,75711296,0,2044.238204,269834.895225,2930.529378,82,0,1,6047,0,500,0,0,2048,67897726,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51484.234, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078396.125, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8065897.697, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,borrow,0,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133553215,68694192,22344284,1024,3072,42083156,0,75711296,0,541236.958782,0.0,2932.875901,76,0,1,5029,0,479,0,0,0,65966946,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52115.836, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078241.217, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8065472.756, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy,0,ok,copy,0.12.0,8388608,41728,4504544,98400,4233264,131120,5292032,134319516,74842901,22151327,1024,3072,42083156,100663296,75711296,0,2277.11069,440218.115158,2909.330552,9,841,1,7327,0,490,0,0,0,60554269,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 57181.88, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5083619.048, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8071315.085, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold2,0,ok,threshold,0.12.0,8388608,41728,2371504,98400,2100224,131120,3194880,133454646,66234578,22159339,1024,3072,42083156,33554432,75711296,0,2031.818258,269861.554419,2910.266617,78,0,1,6051,0,487,0,0,2048,68332882,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51484.504, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5077341.499, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8066441.419, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,compact,0,ok,compact,0.12.0,8388608,41728,4730672,4230192,2001776,131120,6340608,134096312,74886880,22733900,1024,3072,42083156,33554432,75711296,0,341111.908821,100293.13752,2983.138393,921,779,1,4429,0,471,1024,0,0,60362834,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 54080.027, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5083405.479, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068401.165, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold8,0,ok,threshold,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133447386,68765370,22353361,1024,3072,42083156,0,75711296,0,541359.828153,0.0,2934.692831,77,0,1,5028,0,487,0,0,0,65809490,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52372.427, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078845.858, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067374.837, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,compact,1,ok,compact,0.12.0,8388608,41728,4730672,4230192,2034592,131120,6340608,134204038,74836392,22853483,1024,3072,42083156,33554432,75711296,0,341117.919471,100364.674661,2998.098529,881,767,1,4481,0,478,1024,0,0,60375968,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 54356.541, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5083649.875, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070735.206, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,shared_copy,1,ok,shared_copy,0.12.0,8388608,41728,2371504,98400,2100224,131120,3244032,133593447,66172548,22181499,1024,3072,42083156,33554432,75711296,0,2031.917671,269851.910584,2913.380586,80,0,1,6049,0,471,0,0,2048,68489180,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51688.946, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5077231.39, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8066199.086, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy,1,ok,copy,0.12.0,8388608,41728,4504544,98400,4233264,131120,5292032,134436483,75053693,22215239,1024,3072,42083156,100663296,75711296,0,2284.410516,440251.541897,2917.091676,9,832,1,7336,0,483,0,0,0,60480780,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 57382.812, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5083530.123, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8072471.683, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold2,1,ok,threshold,0.12.0,8388608,41728,2371504,98400,2100224,131120,3194880,134034428,69077852,22386881,1024,3072,42083156,33554432,75711296,0,2071.219143,269907.925264,2941.415878,82,0,1,6047,0,521,0,0,2048,66160244,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52463.206, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5079970.677, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068543.087, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold8,1,ok,threshold,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133602318,69361342,22347294,1024,3072,42083156,0,75711296,0,541336.354919,0.0,2933.602437,76,0,1,5029,0,500,0,0,0,65433839,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52034.221, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5079700.568, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067086.654, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,borrow,1,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133878082,69431646,22393309,1024,3072,42083156,0,75711296,0,541341.633608,0.0,2938.771712,78,0,1,5027,0,504,0,0,0,65629618,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52136.236, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078986.712, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067041.718, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy_no_pool,1,ok,copy,0.12.0,8388608,41728,4373248,98400,4233264,131120,5816320,134331933,75355273,22600489,1024,3072,42083156,100663296,75711296,0,2317.391148,440282.284319,2923.56271,4082,3072,1024,0,0,497,0,0,0,60100461,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 58242.85, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5084649.973, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8073924.058, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,compact,2,ok,compact,0.12.0,8388608,41728,4730672,4230192,2001776,131120,6340608,134074359,74679639,22736073,1024,3072,42083156,33554432,75711296,0,341236.488763,100267.211633,2982.994817,917,794,1,4418,0,471,1024,0,0,60515136,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 53738.105, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5085195.058, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069412.397, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy,2,ok,copy,0.12.0,8388608,41728,4504544,98400,4233264,131120,5275648,136064340,79416596,22572078,1024,3072,42083156,100663296,75711296,0,2363.378088,440586.829499,2964.218039,9,623,1,7545,0,463,0,0,0,57551670,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 60112.085, ""latency_p99_upper_ns"": 114688}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5087074.439, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8080314.649, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold2,2,ok,threshold,0.12.0,8388608,41728,2371504,98400,2100224,131120,3244032,133655494,66354625,22289900,1024,3072,42083156,33554432,75711296,0,2049.709126,269797.797722,2927.700988,78,0,1,6051,0,500,0,0,2048,68212138,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51495.073, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078118.015, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8064823.351, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,shared_copy,2,ok,shared_copy,0.12.0,8388608,41728,2371504,98400,2100224,131120,3293184,133683827,66973903,22176603,1024,3072,42083156,33554432,75711296,0,2039.054037,269818.155226,2912.084727,78,0,1,6051,0,494,0,0,2048,67816366,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51459.966, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5079235.137, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8065334.704, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold8,2,ok,threshold,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133681327,68797289,22338685,1024,3072,42083156,0,75711296,0,541289.267391,0.0,2932.086428,77,0,1,5028,0,482,0,0,0,65978872,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52563.022, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078578.492, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8066255.165, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,borrow,2,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133814643,68888479,22335355,1024,3072,42083156,0,75711296,0,541341.207084,0.0,2930.637289,79,0,1,5026,0,510,0,0,0,66149871,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51550.405, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5079607.126, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067127.786, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy_no_pool,2,ok,copy,0.12.0,8388608,41728,4373248,98400,4233264,131120,5816320,134471775,75393460,22629325,1024,3072,42083156,100663296,75711296,0,2321.181236,440258.792854,2921.16977,4082,3072,1024,0,0,508,0,0,0,60227676,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 57516.822, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5084982.783, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8073926.553, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold2,3,ok,threshold,0.12.0,8388608,41728,2371504,98400,2100224,131120,3194880,133441147,66434679,22119254,1024,3072,42083156,33554432,75711296,0,2030.079604,269825.85573,2905.779822,80,0,1,6049,0,482,0,0,2048,68150063,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51190.857, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5077733.031, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8065349.133, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy_no_pool,3,ok,copy,0.12.0,8388608,41728,4373248,98400,4233264,131120,5816320,134289332,76002725,22740343,1024,3072,42083156,100663296,75711296,0,2334.193158,440332.993703,2936.962125,4082,3072,1024,0,0,504,0,0,0,59412631,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 59077.132, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5084960.638, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8075279.097, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,shared_copy,3,ok,shared_copy,0.12.0,8388608,41728,2371504,98400,2100224,131120,3309568,133495777,66421587,22309367,1024,3072,42083156,33554432,75711296,0,2045.123956,269842.975673,2931.949276,76,0,1,6053,0,497,0,0,2048,68249668,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51216.396, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078028.333, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8066089.677, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,borrow,3,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,134337593,71105404,22575073,1024,3072,42083156,0,75711296,0,541448.462493,0.0,2962.344728,83,0,1,5022,0,495,0,0,0,64431294,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 53339.614, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5080292.879, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068765.424, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy,3,ok,copy,0.12.0,8388608,41728,4504544,98400,4233264,131120,5292032,134254523,76196602,22285338,1024,3072,42083156,100663296,75711296,0,2295.795833,440394.753415,2927.722098,9,820,1,7348,0,485,0,0,0,59133245,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 58635.959, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5084371.237, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8075247.27, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,compact,3,ok,compact,0.12.0,8388608,41728,4730672,4230192,2034592,131120,6340608,134306041,75274107,22714288,1024,3072,42083156,33554432,75711296,0,341191.973388,100297.728905,2980.354846,917,749,1,4463,0,478,1024,0,0,60108647,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 54208.683, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5084673.227, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069796.873, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold8,3,ok,threshold,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133817208,69542587,22437830,1024,3072,42083156,0,75711296,0,541408.186663,0.0,2945.936502,80,0,1,5025,0,483,0,0,0,65488224,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52016.168, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5080750.668, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068031.861, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy_no_pool,4,ok,copy,0.12.0,8388608,41728,4373248,98400,4233264,131120,5799936,134597856,77396908,22833216,1024,3072,42083156,100663296,75711296,0,2355.524795,440416.150759,2941.859195,4082,3072,1024,0,0,512,0,0,0,58493375,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 59008.762, ""latency_p99_upper_ns"": 98304}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5086107.088, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8076875.839, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,copy,4,ok,copy,0.12.0,8388608,41728,4504544,98400,4233264,131120,5292032,139508137,88332555,22864596,1024,3072,42083156,100663296,75711296,0,2528.253394,442384.852596,3002.901193,9,688,1,7480,0,372,0,0,0,51495885,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 66698.854, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5129096.339, ""latency_p99_upper_ns"": 6815744}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8093643.623, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold8,4,ok,threshold,0.12.0,8388608,41728,4435888,4230192,0,131120,4833280,134457305,70251788,22431999,1024,3072,42083156,0,75711296,0,541395.863387,0.0,2942.397573,87,0,1,5018,0,495,0,0,0,65292089,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 53169.212, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5080238.856, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068047.314, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,threshold2,4,ok,threshold,0.12.0,8388608,41728,2371504,98400,2100224,131120,3211264,133756102,67373341,22155214,1024,3072,42083156,33554432,75711296,0,2045.927873,269868.863264,2910.706656,81,0,1,6048,0,506,0,0,2048,67548515,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 51135.428, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078670.025, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8066898.388, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,shared_copy,4,ok,shared_copy,0.12.0,8388608,41728,2371504,98400,2100224,131120,3571712,135030934,71236281,22446961,1024,3072,42083156,33554432,75711296,0,2100.123381,269953.875704,2948.934561,94,0,1,6035,0,507,0,0,2048,65033726,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 53626.014, ""latency_p99_upper_ns"": 90112}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5087279.412, ""latency_p99_upper_ns"": 5767168}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070672.959, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,compact,4,ok,compact,0.12.0,8388608,41728,4730672,4230192,1936144,131120,6340608,135831514,84355786,23107923,1024,3072,42083156,33554432,75711296,0,341580.411374,100419.005421,3032.899482,853,676,1,4600,0,455,1024,0,0,52913091,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 60319.001, ""latency_p99_upper_ns"": 114688}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5091148.147, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8079928.47, ""latency_p99_upper_ns"": 8388608}]"
+same_large_field,borrow,4,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133667291,68534602,22264001,1024,3072,42083156,0,75711296,0,541292.227145,0.0,2921.518156,77,0,1,5028,0,481,0,0,0,66216721,0,0,"[{""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 1, ""latency_mean_ns"": 52072.538, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 5078370.622, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8066280.939, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,copy,0,ok,copy,0.12.0,8388608,41728,2378784,98400,2107504,131120,3899392,133407209,54623208,22433685,1024,3072,42083156,33669120,75711296,0,2079.608844,270519.575773,2946.904954,118,838,1,7221,0,547,0,0,0,79664236,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 47700.066, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070062.768, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069584.641, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold2,0,ok,threshold,0.12.0,8388608,41728,2378672,98400,2107392,131120,3751936,133312135,53364925,22316231,1024,3072,42083156,33611776,75711296,0,2049.372724,270524.406644,2928.92106,137,841,1,6175,0,552,0,0,1024,80930688,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46742.189, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068879.809, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069119.599, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,shared_copy,0,ok,shared_copy,0.12.0,8388608,41728,2378672,98400,2107392,131120,3948544,154001095,65368726,23437273,1024,3072,42083156,33611776,75711296,0,2683.230182,276097.966097,3083.783614,228,746,1,6179,0,542,0,0,1024,75659422,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 64443.196, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5134851.331, ""latency_p99_upper_ns"": 7340032}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8252758.487, ""latency_p99_upper_ns"": 15728640}]"
+slow_large_field,copy_no_pool,0,ok,copy,0.12.0,8388608,41728,2312896,98400,2107504,131120,3194880,133648283,61986524,25370210,1024,3072,42083156,33669120,75711296,0,2299.022458,270612.233717,2983.123314,4082,3072,1024,0,0,653,0,0,0,72630111,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 53967.42, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5080263.648, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8075763.615, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,compact,0,ok,compact,0.12.0,8388608,41728,4730672,4230192,2067408,131120,6340608,133472857,62602975,23134595,1024,3072,42083156,33554432,75711296,0,340709.17918,100724.838498,3036.377571,923,841,1,4365,0,526,1024,0,0,71679132,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 48473.289, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5073065.738, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070772.518, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold8,0,ok,threshold,0.12.0,8388608,41728,4443056,4230192,7168,131120,4751360,133297584,55094453,22718797,1024,3072,42083156,57344,75711296,0,541537.659914,577.166247,2982.289653,130,809,1,5190,0,541,0,0,1024,79050000,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 48100.386, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069482.618, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070037.129, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,borrow,0,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133477288,54372368,22466675,1024,3072,42083156,0,75711296,0,541562.393801,0.0,2949.789857,80,0,1,5025,0,554,0,0,0,80055686,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46918.51, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069158.581, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070277.888, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold2,1,ok,threshold,0.12.0,8388608,41728,2378672,98400,2107392,131120,3932160,133312509,53734268,22533891,1024,3072,42083156,33611776,75711296,0,2071.170012,270512.249274,2959.58098,159,828,1,6166,0,575,0,0,1024,80473327,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46789.037, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070040.373, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069081.323, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,copy_no_pool,1,ok,copy,0.12.0,8388608,41728,2312896,98400,2107504,131120,3194880,134281084,62190824,25213500,1024,3072,42083156,33669120,75711296,0,2300.277794,270647.838041,2975.251132,4082,3072,1024,0,0,663,0,0,0,73016473,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 54287.927, ""latency_p99_upper_ns"": 98304}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5080276.456, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8077074.059, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,copy,1,ok,copy,0.12.0,8388608,41728,2378784,98400,2107504,131120,3948544,133395362,55263103,22335683,1024,3072,42083156,33669120,75711296,0,2070.323199,270570.958879,2935.679247,123,825,1,7229,0,563,0,0,0,79157676,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 47428.456, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070071.895, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070912.004, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,shared_copy,1,ok,shared_copy,0.12.0,8388608,41728,2378672,98400,2107392,131120,3522560,133405307,53873137,22349414,1024,3072,42083156,33611776,75711296,0,2061.271642,270527.998952,2936.171471,144,811,1,6198,0,551,0,0,1024,80515381,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46564.507, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069570.9, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069438.264, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,compact,1,ok,compact,0.12.0,8388608,41728,4730672,4230192,2100224,131120,6340608,133295478,60929076,22892803,1024,3072,42083156,33554432,75711296,0,340588.424197,100730.999866,3007.852808,919,842,1,4368,0,524,1024,0,0,73271182,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 48259.733, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5071843.127, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069717.131, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,borrow,1,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,5390336,133317978,54530428,22789187,1024,3072,42083156,0,75711296,0,541551.170985,0.0,2991.654899,72,0,1,5033,0,541,0,0,0,79523636,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 47420.19, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5069779.505, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070214.071, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold8,1,ok,threshold,0.12.0,8388608,41728,4443056,4230192,7168,131120,5357568,133341496,54857945,22618201,1024,3072,42083156,57344,75711296,0,541518.37496,577.265132,2971.205031,122,835,1,5172,0,562,0,0,1024,79396532,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 47550.497, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070242.543, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069675.14, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold8,2,ok,threshold,0.12.0,8388608,41728,4443056,4230192,7168,131120,5292032,134650847,58145169,22801055,1024,3072,42083156,57344,75711296,0,542663.104532,578.298773,2995.063232,127,804,1,5198,0,553,0,0,1024,77457652,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 49310.575, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5080294.837, ""latency_p99_upper_ns"": 5767168}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8086977.866, ""latency_p99_upper_ns"": 9437184}]"
+slow_large_field,copy,2,ok,copy,0.12.0,8388608,41728,2378784,98400,2107504,131120,3964928,133179940,52486508,22146823,1024,3072,42083156,33669120,75711296,0,2028.917653,270508.558846,2910.035715,119,875,1,7183,0,532,0,0,0,81744820,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46179.91, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068139.702, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068218.996, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,borrow,2,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133254368,52756869,22242096,1024,3072,42083156,0,75711296,0,541465.495701,0.0,2919.456949,79,0,1,5026,0,537,0,0,0,81620293,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 45804.026, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067325.337, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068611.8, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,compact,2,ok,compact,0.12.0,8388608,41728,4730672,4230192,2100224,131120,6340608,133273853,59503628,22579568,1024,3072,42083156,33554432,75711296,0,340521.103381,100725.988305,2964.439632,921,839,1,4369,0,529,1024,0,0,75001351,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46820.147, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5071087.53, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068767.35, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,shared_copy,2,ok,shared_copy,0.12.0,8388608,41728,2378672,98400,2107392,131120,3293184,133216315,51631438,22079426,1024,3072,42083156,33611776,75711296,0,2014.496107,270449.091989,2901.711431,152,827,1,6174,0,543,0,0,1024,82698735,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 45041.785, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068002.393, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8066161.602, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,copy_no_pool,2,ok,copy,0.12.0,8388608,41728,2312896,98400,2107504,131120,3178496,133362779,58084129,24178012,1024,3072,42083156,33669120,75711296,0,2196.167723,270535.919777,2934.024512,4082,3072,1024,0,0,605,0,0,0,76526627,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 50758.917, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5074265.021, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8071772.438, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold2,2,ok,threshold,0.12.0,8388608,41728,2378672,98400,2107392,131120,3751936,133094723,52669444,22153975,1024,3072,42083156,33611776,75711296,0,2026.350691,270487.081841,2910.446252,162,813,1,6178,0,529,0,0,1024,81515465,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46192.459, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067955.414, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067608.796, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,compact,3,ok,compact,0.12.0,8388608,41728,4730672,4230192,2100224,131120,6340608,133282278,59568109,22558632,1024,3072,42083156,33554432,75711296,0,340502.324772,100714.490169,2961.00573,923,826,1,4380,0,521,1024,0,0,74934523,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46941.425, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5070856.899, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068200.595, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,copy_no_pool,3,ok,copy,0.12.0,8388608,41728,2312896,98400,2107504,131120,3194880,133540522,58472614,24345793,1024,3072,42083156,33669120,75711296,0,2208.417867,270579.890216,2933.62499,4082,3072,1024,0,0,602,0,0,0,76325826,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 51364.197, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5074603.185, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8073207.582, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,borrow,3,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133275839,52234245,22266351,1024,3072,42083156,0,75711296,0,541367.893955,0.0,2922.935825,79,0,1,5026,0,545,0,0,0,82113024,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 45842.847, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5066801.978, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067096.868, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,shared_copy,3,ok,shared_copy,0.12.0,8388608,41728,2378672,98400,2107392,131120,3899392,133064931,52117164,22057182,1024,3072,42083156,33611776,75711296,0,2015.469116,270480.700872,2897.68644,130,842,1,6181,0,555,0,0,1024,82111301,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 45400.789, ""latency_p99_upper_ns"": 57344}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068255.587, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067189.304, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold8,3,ok,threshold,0.12.0,8388608,41728,4443056,4230192,7168,131120,5292032,133337450,53670610,22263776,1024,3072,42083156,57344,75711296,0,541459.255198,577.165832,2922.14622,137,796,1,5196,0,551,0,0,1024,80837357,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46321.397, ""latency_p99_upper_ns"": 65536}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068514.702, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8068639.91, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold2,3,ok,threshold,0.12.0,8388608,41728,2378672,98400,2107392,131120,3522560,134333542,52726291,22219255,1024,3072,42083156,33611776,75711296,0,2039.714059,270798.061653,2918.877137,140,894,1,6119,0,516,0,0,1024,82638596,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46056.021, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5218345.888, ""latency_p99_upper_ns"": 9437184}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8076530.434, ""latency_p99_upper_ns"": 9437184}]"
+slow_large_field,copy,3,ok,copy,0.12.0,8388608,41728,2378784,98400,2107504,131120,3948544,133174357,52057974,22117835,1024,3072,42083156,33669120,75711296,0,2021.284206,270501.113842,2905.858494,114,849,1,7214,0,548,0,0,0,82257586,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 45428.889, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068142.38, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067808.96, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,copy_no_pool,4,ok,copy,0.12.0,8388608,41728,2312896,98400,2107504,131120,3178496,133410638,57999768,24134627,1024,3072,42083156,33669120,75711296,0,2191.645151,270510.58659,2926.663567,4082,3072,1024,0,0,603,0,0,0,76638749,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 51016.097, ""latency_p99_upper_ns"": 81920}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5074074.877, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8071032.382, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold2,4,ok,threshold,0.12.0,8388608,41728,2378672,98400,2107392,131120,3342336,133213102,52362717,22103023,1024,3072,42083156,33611776,75711296,0,2022.420003,270482.639278,2904.168619,127,855,1,6171,0,539,0,0,1024,81964913,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46044.098, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5068262.707, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067371.07, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,copy,4,ok,copy,0.12.0,8388608,41728,2378784,98400,2107504,131120,3751936,133177547,52033688,22154380,1024,3072,42083156,33669120,75711296,0,2025.354638,270478.813202,2910.934805,129,851,1,7197,0,527,0,0,0,82109654,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 45825.604, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067937.156, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067321.024, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,borrow,4,ok,borrow,0.12.0,8388608,41728,4435888,4230192,0,131120,4767744,133311566,53725285,22239925,1024,3072,42083156,0,75711296,0,541575.408384,0.0,2920.340305,80,0,1,5025,0,555,0,0,0,80727666,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46306.509, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067147.548, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8070390.562, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,threshold8,4,ok,threshold,0.12.0,8388608,41728,4443056,4230192,7168,131120,5439488,134101658,54158047,22315394,1024,3072,42083156,57344,75711296,0,541512.874989,578.716358,2931.279646,161,826,1,5142,0,533,0,0,1024,81103821,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46533.691, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5082538.041, ""latency_p99_upper_ns"": 6291456}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8069591.938, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,shared_copy,4,ok,shared_copy,0.12.0,8388608,41728,2378672,98400,2107392,131120,3948544,133134639,52075173,22128273,1024,3072,42083156,33611776,75711296,0,2026.862393,270471.476005,2908.200691,122,816,1,6215,0,512,0,0,1024,82080012,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 46182.627, ""latency_p99_upper_ns"": 61440}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5067504.389, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067072.688, ""latency_p99_upper_ns"": 8388608}]"
+slow_large_field,compact,4,ok,compact,0.12.0,8388608,41728,4730672,4230192,2100224,131120,6406144,133118662,59607669,22648269,1024,3072,42083156,33554432,75711296,0,340524.867797,100684.088672,2973.798584,921,855,1,4353,0,532,1024,0,0,74562185,0,0,"[{""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 1, ""latency_mean_ns"": 47236.674, ""latency_p99_upper_ns"": 73728}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5071269.907, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 33554432, ""peak_count"": 64, ""latency_mean_ns"": 8067723.707, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,copy,0,ok,copy,0.12.0,2097152,41728,1104848,393312,79920,524336,1884160,134798098,52545277,26282225,1024,3072,49877076,26872064,114814580,0,6883.203763,1809.304759,10115.296993,367,948,374,6256,0,579,0,0,0,83482902,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47583.467, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5081460.678, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8083520.998, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,threshold8,0,ok,threshold,0.12.0,2097152,41728,1097792,393312,11312,524336,2031616,134514858,50924568,26180831,1024,3072,49877076,1059072,114814580,0,7475.627393,924.635553,10091.334952,237,96,257,5800,0,593,0,0,1024,84806597,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 46212.018, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5080764.964, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8080296.584, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,copy_no_pool,0,ok,copy,0.12.0,2097152,41728,842368,393312,79920,524336,1949696,135580728,59055276,27303180,1024,3072,49877076,26872064,114814580,0,8268.860241,1866.643736,10141.702783,3849,3072,1024,0,0,540,0,0,0,77683199,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 53763.312, ""latency_p99_upper_ns"": 163840}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5090947.59, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8093934.847, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,shared_copy,0,ok,shared_copy,0.12.0,2097152,41728,1097792,393312,72752,524336,1884160,134733970,52189389,26273407,1024,3072,49877076,26814720,114814580,0,6865.482399,1226.436074,10135.432887,365,246,368,5942,0,587,0,0,1024,83766349,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47383.279, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5081532.837, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082383.547, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,threshold2,0,ok,threshold,0.12.0,2097152,41728,1097792,393312,72752,524336,1884160,134703694,51981424,26239241,1024,3072,49877076,26814720,114814580,0,6818.11346,1220.93584,10109.238408,368,240,366,5947,0,592,0,0,1024,83962412,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47186.16, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5082027.365, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082717.212, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,compact,0,ok,compact,0.12.0,2097152,41728,2096880,2047296,3360,524336,3702784,330325402,56526552,257818693,1024,3072,49877076,57344,114814580,0,542057.64214,342.456081,10454.037412,1653,1018,587,2639,1256,0,1024,0,0,275134226,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 313916.181, ""latency_p99_upper_ns"": 4718592}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5359649.404, ""latency_p99_upper_ns"": 10485760}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 49, ""latency_mean_ns"": 8345801.06, ""latency_p99_upper_ns"": 12582912}]"
+mixed_tight,borrow,0,ok,borrow,0.12.0,2097152,41728,2097024,2047296,0,524336,3702784,517444947,58385263,414175159,1024,3072,49877076,0,114814580,0,866552.611778,0.0,10646.478365,1612,0,580,2681,1250,0,0,0,0,460552293,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 496739.613, ""latency_p99_upper_ns"": 7864320}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5531099.266, ""latency_p99_upper_ns"": 12582912}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 8547759.071, ""latency_p99_upper_ns"": 15728640}]"
+mixed_tight,copy_no_pool,1,ok,copy,0.12.0,2097152,41728,842368,393312,79920,524336,1916928,135818181,60130654,27699522,1024,3072,49877076,26872064,114814580,0,8398.686166,1885.555543,10206.603011,3849,3072,1024,0,0,537,0,0,0,76838088,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 54782.86, ""latency_p99_upper_ns"": 180224}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5093567.929, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8096201.349, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,threshold8,1,ok,threshold,0.12.0,2097152,41728,1097792,393312,11312,524336,2064384,134627181,51051158,26242598,1024,3072,49877076,1059072,114814580,0,7499.787736,924.734083,10090.493068,237,100,258,5795,0,587,0,0,1024,84733901,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 46365.068, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5080938.783, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8081319.231, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,shared_copy,1,ok,shared_copy,0.12.0,2097152,41728,1097792,393312,72752,524336,1900544,134660107,51849496,26235438,1024,3072,49877076,26814720,114814580,0,6785.330268,1222.453816,10103.005533,366,239,366,5950,0,579,0,0,1024,84013028,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47133.761, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5081549.099, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082113.784, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,borrow,1,ok,borrow,0.12.0,2097152,41728,2097024,2047296,0,524336,3719168,516876187,58189132,413488109,1024,3072,49877076,0,114814580,0,866155.703795,0.0,10618.392921,1617,0,581,2675,1263,0,0,0,0,460124695,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 496219.126, ""latency_p99_upper_ns"": 7864320}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5528237.536, ""latency_p99_upper_ns"": 12582912}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 8541844.312, ""latency_p99_upper_ns"": 15728640}]"
+mixed_tight,threshold2,1,ok,threshold,0.12.0,2097152,41728,1097792,393312,72752,524336,1867776,134596389,51879163,26207832,1024,3072,49877076,26814720,114814580,0,6795.293198,1224.224319,10089.609534,369,233,365,5954,0,596,0,0,1024,83947198,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47146.396, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5082305.345, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082431.98, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,copy,1,ok,copy,0.12.0,2097152,41728,1104848,393312,79920,524336,1884160,134667407,52286009,26255995,1024,3072,49877076,26872064,114814580,0,6830.10163,1802.54536,10104.51946,370,952,375,6248,0,590,0,0,0,83595691,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47383.187, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5081689.522, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082200.92, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,compact,1,ok,compact,0.12.0,2097152,41728,2097104,2047296,3360,524336,3833856,330507221,57227425,258042238,1024,3072,49877076,57344,114814580,0,542403.396989,342.198165,10516.149038,1657,1018,592,2630,1262,0,1024,0,0,274671771,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 314160.033, ""latency_p99_upper_ns"": 4718592}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5361935.781, ""latency_p99_upper_ns"": 10485760}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 49, ""latency_mean_ns"": 8345834.982, ""latency_p99_upper_ns"": 12582912}]"
+mixed_tight,threshold2,2,ok,threshold,0.12.0,2097152,41728,1097792,393312,72752,524336,1900544,134805557,52099602,26201196,1024,3072,49877076,26814720,114814580,0,6802.764732,1222.5673,10082.699192,363,241,368,5949,0,592,0,0,1024,83969655,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47229.56, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5083369.83, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082868.22, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,copy,2,ok,copy,0.12.0,2097152,41728,1104736,393312,79920,524336,1900544,135397301,55535720,28191073,1024,3072,49877076,26872064,114814580,0,7296.683374,1825.786254,10851.770961,364,925,376,6280,0,556,0,0,0,81078620,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 50441.328, ""latency_p99_upper_ns"": 163840}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5086253.071, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8088040.091, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,threshold8,2,ok,threshold,0.12.0,2097152,41728,1097792,393312,11312,524336,2048000,135143135,54181725,28073539,1024,3072,49877076,1059072,114814580,0,7971.142415,924.89692,10793.387694,237,89,259,5805,0,587,0,0,1024,82256234,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 49150.131, ""latency_p99_upper_ns"": 163840}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5084346.661, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8083800.547, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,borrow,2,ok,borrow,0.12.0,2097152,41728,2097024,2047296,0,524336,3719168,519737827,63401151,410873515,1024,3072,49877076,0,114814580,0,869390.688093,0.0,10874.854354,1612,0,584,2677,1267,0,0,0,0,457480540,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 498894.016, ""latency_p99_upper_ns"": 7864320}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5554388.391, ""latency_p99_upper_ns"": 12582912}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 8572436.932, ""latency_p99_upper_ns"": 15728640}]"
+mixed_tight,copy_no_pool,2,ok,copy,0.12.0,2097152,41728,842368,393312,79920,524336,1933312,135808674,61310764,27409732,1024,3072,49877076,26872064,114814580,0,8492.13471,1909.35548,10184.015437,3849,3072,1024,0,0,538,0,0,0,75774039,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 55750.675, ""latency_p99_upper_ns"": 196608}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5097561.83, ""latency_p99_upper_ns"": 5767168}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8097147.871, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,compact,2,ok,compact,0.12.0,2097152,41728,2096496,2047296,3360,524336,3719168,330655613,57992572,257126466,1024,3072,49877076,57344,114814580,0,542286.835871,342.201335,10592.82654,1620,1018,589,2670,1246,0,1024,0,0,274045502,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 314214.117, ""latency_p99_upper_ns"": 4718592}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5361784.028, ""latency_p99_upper_ns"": 10485760}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 49, ""latency_mean_ns"": 8345733.162, ""latency_p99_upper_ns"": 12582912}]"
+mixed_tight,shared_copy,2,ok,shared_copy,0.12.0,2097152,41728,1097792,393312,72752,524336,1900544,134725602,52125932,26256537,1024,3072,49877076,26814720,114814580,0,6831.305534,1226.062002,10092.239062,365,226,360,5970,0,576,0,0,1024,83779201,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47423.973, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5082508.901, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082650.268, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,compact,3,ok,compact,0.12.0,2097152,41728,2096656,2047296,3360,524336,3719168,332809762,64506702,255295188,1024,3072,49877076,57344,114814580,0,544031.993873,348.13532,10927.535411,1623,1008,583,2683,1227,0,1024,0,0,270135277,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 316210.616, ""latency_p99_upper_ns"": 4718592}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5373501.093, ""latency_p99_upper_ns"": 10485760}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 49, ""latency_mean_ns"": 8409239.137, ""latency_p99_upper_ns"": 12582912}]"
+mixed_tight,threshold2,3,ok,threshold,0.12.0,2097152,41728,1097792,393312,72752,524336,1884160,134646087,51969989,26192074,1024,3072,49877076,26814720,114814580,0,6812.54134,1229.637184,10083.25152,369,241,365,5946,0,582,0,0,1024,83864700,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47274.193, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5082291.09, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8084035.099, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,copy,3,ok,copy,0.12.0,2097152,41728,1104848,393312,79920,524336,1900544,134732754,52218674,26241260,1024,3072,49877076,26872064,114814580,0,6810.984162,1804.529206,10104.502269,368,949,374,6254,0,584,0,0,0,83719457,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47404.955, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5082343.96, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082308.803, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,shared_copy,3,ok,shared_copy,0.12.0,2097152,41728,1097792,393312,72752,524336,1884160,134687032,52085095,26215409,1024,3072,49877076,26814720,114814580,0,6852.502663,1223.987439,10100.060122,365,235,364,5957,0,585,0,0,1024,83840818,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47354.551, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5081713.058, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082095.55, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,borrow,3,ok,borrow,0.12.0,2097152,41728,2097072,2047296,0,524336,3719168,516415971,57490634,412616864,1024,3072,49877076,0,114814580,0,865936.173669,0.0,10532.202462,1624,0,582,2667,1258,0,0,0,0,460245677,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 495746.732, ""latency_p99_upper_ns"": 7864320}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5527499.187, ""latency_p99_upper_ns"": 12582912}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 8541909.699, ""latency_p99_upper_ns"": 15728640}]"
+mixed_tight,copy_no_pool,3,ok,copy,0.12.0,2097152,41728,842368,393312,79920,524336,1916928,135708499,59498427,27293452,1024,3072,49877076,26872064,114814580,0,8320.069469,1853.590142,10172.640074,3849,3072,1024,0,0,550,0,0,0,77469437,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 53679.489, ""latency_p99_upper_ns"": 180224}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5093041.554, ""latency_p99_upper_ns"": 5767168}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8093789.758, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,threshold8,3,ok,threshold,0.12.0,2097152,41728,1097792,393312,11312,524336,2048000,134580129,50901202,26138215,1024,3072,49877076,1059072,114814580,0,7468.893211,924.833553,10074.589742,237,99,260,5794,0,603,0,0,1024,84894241,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 46199.245, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5080295.214, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082153.254, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,compact,4,ok,compact,0.12.0,2097152,41728,2095936,2047296,3360,524336,3768320,330364653,56737666,257390641,1024,3072,49877076,57344,114814580,0,542129.496495,342.3922,10487.947668,1626,1018,584,2669,1234,0,1024,0,0,274961005,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 314000.227, ""latency_p99_upper_ns"": 4718592}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5359881.678, ""latency_p99_upper_ns"": 10485760}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 49, ""latency_mean_ns"": 8345470.393, ""latency_p99_upper_ns"": 12582912}]"
+mixed_tight,threshold2,4,ok,threshold,0.12.0,2097152,41728,1097792,393312,72752,524336,1884160,134641407,51772899,26303805,1024,3072,49877076,26814720,114814580,0,6823.249522,1222.425123,10156.044791,363,239,367,5952,0,584,0,0,1024,83930889,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47208.702, ""latency_p99_upper_ns"": 131072}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5081715.549, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8082551.032, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,threshold8,4,ok,threshold,0.12.0,2097152,41728,1097792,393312,11312,524336,2048000,134636889,51692325,26232175,1024,3072,49877076,1059072,114814580,0,7604.090343,924.831397,10109.160375,237,97,259,5797,0,600,0,0,1024,84203974,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 46850.104, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5081772.482, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8081990.295, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,borrow,4,ok,borrow,0.12.0,2097152,41728,2097024,2047296,0,524336,3719168,516507633,56956626,413462854,1024,3072,49877076,0,114814580,0,866066.604394,0.0,10498.181654,1635,0,588,2650,1258,0,0,0,0,460890935,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 495819.875, ""latency_p99_upper_ns"": 7864320}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 5527650.658, ""latency_p99_upper_ns"": 12582912}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 30, ""latency_mean_ns"": 8542682.03, ""latency_p99_upper_ns"": 15728640}]"
+mixed_tight,shared_copy,4,ok,shared_copy,0.12.0,2097152,41728,1097792,393312,72752,524336,1884160,134698503,52062419,26232455,1024,3072,49877076,26814720,114814580,0,6807.634601,1226.698884,10096.010075,358,229,363,5971,0,573,0,0,1024,83820760,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 47338.287, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5082803.919, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8081430.476, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,copy_no_pool,4,ok,copy,0.12.0,2097152,41728,842368,393312,79920,524336,1933312,135617374,59370307,27370335,1024,3072,49877076,26872064,114814580,0,8306.118105,1881.770411,10193.350967,3849,3072,1024,0,0,541,0,0,0,77447978,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 54118.737, ""latency_p99_upper_ns"": 180224}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5093193.132, ""latency_p99_upper_ns"": 5767168}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8094306.969, ""latency_p99_upper_ns"": 8388608}]"
+mixed_tight,copy,4,ok,copy,0.12.0,2097152,41728,1104736,393312,79920,524336,1884160,135101765,53612089,26369173,1024,3072,49877076,26872064,114814580,0,6944.794448,1822.831558,10130.150831,348,886,368,6343,0,561,0,0,0,82695691,0,0,"[{""emitted"": 1024, ""bytes"": 26757376, ""peak_count"": 1, ""latency_mean_ns"": 48573.396, ""latency_p99_upper_ns"": 147456}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 5083912.79, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 1024, ""bytes"": 57344, ""peak_count"": 64, ""latency_mean_ns"": 8083831.809, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy_no_pool,0,ok,copy,0.12.0,2097152,41728,170672,24672,14896,98400,1835008,66381179,14937944,10634360,512,1536,5259666,319488,6312628,14086144,191.835816,748.822762,436.544508,1273,1536,1024,0,0,200,0,0,0,51858352,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26502.654, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055591.338, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8038187.436, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,borrow,0,ok,borrow,0.12.0,2097152,41728,1208192,1059888,0,98400,2785280,66395496,14949295,10748920,512,1536,5259666,0,6312628,14086144,67632.156584,0.0,441.340054,70,0,2,2225,0,210,0,0,0,51867983,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26677.738, ""latency_p99_upper_ns"": 40960}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055635.91, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8040456.355, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold8,0,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1851392,66344311,14626628,10598197,512,1536,5259666,290816,6312628,14086144,189.993439,460.282359,437.832685,6,65,2,3248,0,201,0,0,512,52108022,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26057.676, ""latency_p99_upper_ns"": 30720}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054707.516, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8038718.686, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,shared_copy,0,ok,shared_copy,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66360126,14610198,10558947,512,1536,5259666,290816,6312628,14086144,189.278595,460.358957,433.475216,6,65,2,3248,0,210,0,0,512,52155647,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26056.072, ""latency_p99_upper_ns"": 30720}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054720.746, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8039527.611, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,compact,0,ok,compact,0.12.0,2097152,41728,1209200,1059888,7168,98400,2899968,66352515,15174956,10767457,512,1536,5259666,28672,6312628,14086144,42522.512719,170.992644,444.338525,462,456,2,1889,0,203,512,0,0,51584350,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26833.023, ""latency_p99_upper_ns"": 40960}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055824.906, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8037818.045, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy,0,ok,copy,0.12.0,2097152,41728,187312,24672,14896,98400,1835008,66358774,14704746,10580140,512,1536,5259666,319488,6312628,14086144,190.401936,748.797361,434.190092,6,546,2,3279,0,198,0,0,0,52055904,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26138.373, ""latency_p99_upper_ns"": 32768}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054837.756, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8038347.354, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold2,0,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1851392,66343256,14610428,10601249,512,1536,5259666,290816,6312628,14086144,190.005404,460.293238,435.304664,6,65,2,3248,0,199,0,0,512,52113330,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26103.785, ""latency_p99_upper_ns"": 32768}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054791.455, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8038668.434, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold8,1,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66620533,15009658,10599246,512,1536,5259666,290816,6312628,14086144,190.711835,461.162485,439.194778,6,67,2,3246,0,195,0,0,512,52089340,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26514.744, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5059599.168, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8052646.494, ""latency_p99_upper_ns"": 9437184}]"
+metadata_heavy,copy_no_pool,1,ok,copy,0.12.0,2097152,41728,170672,24672,14896,98400,1835008,69246534,16610834,11758115,512,1536,5259666,319488,6312628,14086144,215.365911,778.347747,481.501477,1273,1536,1024,0,0,164,0,0,0,52395416,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 29861.328, ""latency_p99_upper_ns"": 57344}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5481519.523, ""latency_p99_upper_ns"": 10485760}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8132478.682, ""latency_p99_upper_ns"": 10485760}]"
+metadata_heavy,shared_copy,1,ok,shared_copy,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,67338922,15543047,10965512,512,1536,5259666,290816,6312628,14086144,198.833427,461.43294,455.786835,6,88,2,3225,0,170,0,0,512,52096250,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 27620.291, ""latency_p99_upper_ns"": 53248}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5405312.287, ""latency_p99_upper_ns"": 9437184}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8058747.33, ""latency_p99_upper_ns"": 9437184}]"
+metadata_heavy,copy,1,ok,copy,0.12.0,2097152,41728,187312,24672,14896,98400,1835008,66437867,14887131,10719986,512,1536,5259666,319488,6312628,14086144,192.97609,748.927344,442.835871,6,546,2,3279,0,206,0,0,0,51911693,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26502.299, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055229.1, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8040517.863, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold2,1,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66341459,14573003,10555283,512,1536,5259666,290816,6312628,14086144,189.19959,460.262935,433.563583,6,65,2,3248,0,202,0,0,512,52150322,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26047.523, ""latency_p99_upper_ns"": 30720}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054844.742, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8037890.609, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,borrow,1,ok,borrow,0.12.0,2097152,41728,1208192,1059888,0,98400,2801664,66302663,14680433,10680579,512,1536,5259666,0,6312628,14086144,67603.216745,0.0,441.482243,70,0,2,2225,0,203,0,0,0,52022749,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26241.209, ""latency_p99_upper_ns"": 32768}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054887.895, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8036938.547, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,compact,1,ok,compact,0.12.0,2097152,41728,1209200,1059888,7168,98400,2916352,66647996,15645769,10897093,512,1536,5259666,28672,6312628,14086144,42531.184104,171.185155,448.382215,462,456,2,1889,0,208,512,0,0,51484721,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 27363.605, ""latency_p99_upper_ns"": 49152}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5056927.043, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8042303.346, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,shared_copy,2,ok,shared_copy,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66509552,15439851,10723161,512,1536,5259666,290816,6312628,14086144,194.692414,460.401472,443.814792,6,65,2,3248,0,188,0,0,512,51559999,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 27221.904, ""latency_p99_upper_ns"": 40960}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5102159.828, ""latency_p99_upper_ns"": 6291456}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8039936.652, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold2,2,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1851392,66675868,15617313,10794500,512,1536,5259666,290816,6312628,14086144,196.368374,460.582208,453.168708,6,72,2,3241,0,206,0,0,512,51576007,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 27391.873, ""latency_p99_upper_ns"": 45056}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5063401.098, ""latency_p99_upper_ns"": 5767168}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8043009.266, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold8,2,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66346091,14749114,10663838,512,1536,5259666,290816,6312628,14086144,191.566293,460.387978,441.272197,6,65,2,3248,0,198,0,0,512,51956135,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26444.111, ""latency_p99_upper_ns"": 40960}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055009.982, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8040053.549, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,borrow,2,ok,borrow,0.12.0,2097152,41728,1208192,1059888,0,98400,2768896,66340906,14840713,10710339,512,1536,5259666,0,6312628,14086144,67628.848076,0.0,441.911742,70,0,2,2225,0,214,0,0,0,51926596,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26533.881, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055556.934, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8040046.408, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy_no_pool,2,ok,copy,0.12.0,2097152,41728,170672,24672,14896,98400,1851392,66367572,14844612,10626817,512,1536,5259666,319488,6312628,14086144,191.787957,748.742987,439.039763,1273,1536,1024,0,0,202,0,0,0,51918865,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26443.496, ""latency_p99_upper_ns"": 32768}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055503.67, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8037164.848, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,compact,2,ok,compact,0.12.0,2097152,41728,1209200,1059888,7168,98400,2916352,66456221,15237761,10832907,512,1536,5259666,28672,6312628,14086144,42524.965388,171.106633,446.50427,462,456,2,1889,0,211,512,0,0,51628844,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26894.043, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5056115.033, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8040094.787, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy,2,ok,copy,0.12.0,2097152,41728,187312,24672,14896,98400,1818624,66358054,14786258,10588699,512,1536,5259666,319488,6312628,14086144,190.767691,748.949984,437.20655,6,546,2,3279,0,202,0,0,0,51982953,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26290.178, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055243.682, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8040476.592, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,shared_copy,3,ok,shared_copy,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66340017,14641398,10583202,512,1536,5259666,290816,6312628,14086144,189.850967,460.201756,434.46664,6,65,2,3248,0,200,0,0,512,52101474,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26126.43, ""latency_p99_upper_ns"": 32768}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054896.006, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8037126.236, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,borrow,3,ok,borrow,0.12.0,2097152,41728,1208192,1059888,0,98400,2768896,66397387,14926280,10743184,512,1536,5259666,0,6312628,14086144,67622.063537,0.0,442.942199,70,0,2,2225,0,212,0,0,0,51935519,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26540.598, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055706.689, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8039215.434, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy,3,ok,copy,0.12.0,2097152,41728,187312,24672,14896,98400,1835008,66347591,14688841,10558757,512,1536,5259666,319488,6312628,14086144,190.237303,748.906445,434.052077,6,546,2,3279,0,201,0,0,0,52048363,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26185.322, ""latency_p99_upper_ns"": 30720}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054934.725, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8039957.693, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,compact,3,ok,compact,0.12.0,2097152,41728,1209200,1059888,7168,98400,2916352,66425702,15126826,10762572,512,1536,5259666,28672,6312628,14086144,42518.234312,171.124375,442.813559,462,456,2,1889,0,207,512,0,0,51722109,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26699.592, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055311.875, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8039588.77, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold8,3,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66342183,14624958,10558072,512,1536,5259666,290816,6312628,14086144,189.596919,460.250779,436.103934,6,65,2,3248,0,199,0,0,512,52113950,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26082.711, ""latency_p99_upper_ns"": 32768}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054439.891, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8037911.916, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy_no_pool,3,ok,copy,0.12.0,2097152,41728,170672,24672,14896,98400,1835008,66421906,14924036,10733514,512,1536,5259666,319488,6312628,14086144,193.388376,748.846289,443.059571,1273,1536,1024,0,0,207,0,0,0,51871512,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26630.812, ""latency_p99_upper_ns"": 40960}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055263.076, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8039399.533, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold2,3,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66363184,14629240,10568715,512,1536,5259666,290816,6312628,14086144,189.501318,460.195707,436.121559,6,65,2,3248,0,199,0,0,512,52142767,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26129.004, ""latency_p99_upper_ns"": 30720}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054684.932, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8036606.693, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold2,4,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66354498,14608671,10552983,512,1536,5259666,290816,6312628,14086144,189.286265,460.245208,433.358809,6,65,2,3248,0,197,0,0,512,52143565,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26044.074, ""latency_p99_upper_ns"": 30720}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054765.459, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8037717.471, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy_no_pool,4,ok,copy,0.12.0,2097152,41728,170672,24672,14896,98400,1851392,66440315,15042208,10714630,512,1536,5259666,319488,6312628,14086144,193.34471,749.072578,442.776453,1273,1536,1024,0,0,203,0,0,0,51818059,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26719.596, ""latency_p99_upper_ns"": 40960}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055539.021, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8042928.75, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,shared_copy,4,ok,shared_copy,0.12.0,2097152,41728,180144,24672,7728,98400,1835008,66550686,14565611,10569760,512,1536,5259666,290816,6312628,14086144,189.196651,460.427963,435.936714,6,70,2,3243,0,199,0,0,512,52394259,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26054.17, ""latency_p99_upper_ns"": 30720}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054468.873, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8040821.889, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,compact,4,ok,compact,0.12.0,2097152,41728,1209200,1059888,7168,98400,2932736,66346982,15115580,10730381,512,1536,5259666,28672,6312628,14086144,42539.689103,170.963142,440.137993,462,456,2,1889,0,200,512,0,0,51642878,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26772.898, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5057876.094, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8039346.365, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,threshold8,4,ok,threshold,0.12.0,2097152,41728,180144,24672,7728,98400,1851392,66367685,14678765,10612300,512,1536,5259666,290816,6312628,14086144,190.199351,460.290364,438.024028,6,65,2,3248,0,200,0,0,512,52099788,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26178.92, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054797.43, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8038471.338, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,borrow,4,ok,borrow,0.12.0,2097152,41728,1208192,1059888,0,98400,2785280,66395630,14769876,10679603,512,1536,5259666,0,6312628,14086144,67615.986637,0.0,438.40001,70,0,2,2225,0,207,0,0,0,52035492,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26350.551, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5054965.338, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8038469.066, ""latency_p99_upper_ns"": 8388608}]"
+metadata_heavy,copy,4,ok,copy,0.12.0,2097152,41728,187312,24672,14896,98400,1835008,66338315,14754424,10609310,512,1536,5259666,319488,6312628,14086144,190.968242,748.772024,435.574111,6,546,2,3279,0,199,0,0,0,51978025,0,0,"[{""emitted"": 512, ""bytes"": 262144, ""peak_count"": 1, ""latency_mean_ns"": 26285.051, ""latency_p99_upper_ns"": 36864}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 5055200.059, ""latency_p99_upper_ns"": 5242880}, {""emitted"": 512, ""bytes"": 28672, ""peak_count"": 64, ""latency_mean_ns"": 8037468.172, ""latency_p99_upper_ns"": 8388608}]"
diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/raw.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/raw.json new file mode 100644 index 0000000..9bf47cb --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/raw.json @@ -0,0 +1,12532 @@ +[ + { + "case": "one_slow", + "variant": "threshold2", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1835008, + "elapsed_ns": 131703634, + "cpu_ns": 34750648, + "parse_call_ns": 21989760, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1842.615404, + "output_byte_seconds": 919.75921, + "parser_byte_seconds": 2883.244752, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97097912, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8051720.034, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "compact", + "repeat": 0, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 131996119, + "cpu_ns": 38004572, + "parse_call_ns": 22434299, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540791.270719, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2928.256675, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 26, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 94192606, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8057605.331, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "borrow", + "repeat": 0, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5521408, + "elapsed_ns": 132124751, + "cpu_ns": 37612015, + "parse_call_ns": 22444229, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540845.471315, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2930.090388, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 53, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 94791711, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8058299.849, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold8", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131620844, + "cpu_ns": 34446035, + "parse_call_ns": 21905479, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1832.609103, + "output_byte_seconds": 920.078119, + "parser_byte_seconds": 2872.388672, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97293567, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054286.008, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy_no_pool", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 237296, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1835008, + "elapsed_ns": 132233158, + "cpu_ns": 41767998, + "parse_call_ns": 24756316, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2069.543688, + "output_byte_seconds": 920.140424, + "parser_byte_seconds": 2901.308237, + "source_allocations": 4082, + "output_allocations": 1024, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 19, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 90619067, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8059175.662, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131600270, + "cpu_ns": 34455850, + "parse_call_ns": 21964978, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1832.537919, + "output_byte_seconds": 920.025416, + "parser_byte_seconds": 2880.380829, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97270417, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8053796.131, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "shared_copy", + "repeat": 0, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1802240, + "elapsed_ns": 131715584, + "cpu_ns": 34456703, + "parse_call_ns": 21940218, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1833.007769, + "output_byte_seconds": 920.07894, + "parser_byte_seconds": 2877.016945, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97393619, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054276.953, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "compact", + "repeat": 1, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 132171843, + "cpu_ns": 38070312, + "parse_call_ns": 22523797, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540815.06589, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2937.269732, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 40, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 94338529, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8057936.641, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy_no_pool", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 237296, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1835008, + "elapsed_ns": 132199048, + "cpu_ns": 42202499, + "parse_call_ns": 24805054, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2084.967873, + "output_byte_seconds": 920.132302, + "parser_byte_seconds": 2905.942804, + "source_allocations": 4082, + "output_allocations": 1024, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 29, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 90146770, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8059478.906, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "shared_copy", + "repeat": 1, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131708165, + "cpu_ns": 34804961, + "parse_call_ns": 22044118, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1844.634105, + "output_byte_seconds": 919.759432, + "parser_byte_seconds": 2890.857973, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97053198, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8051751.524, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold8", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131798127, + "cpu_ns": 34803856, + "parse_call_ns": 22044512, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1846.36305, + "output_byte_seconds": 919.612987, + "parser_byte_seconds": 2891.382584, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97156768, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050483.774, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "borrow", + "repeat": 1, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5521408, + "elapsed_ns": 131976979, + "cpu_ns": 38591495, + "parse_call_ns": 22573964, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540860.949553, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2940.513903, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 28, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 93626665, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8058679.423, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131752053, + "cpu_ns": 34888537, + "parse_call_ns": 21974890, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1845.194241, + "output_byte_seconds": 919.599974, + "parser_byte_seconds": 2880.87266, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 17, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97030621, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050419.036, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold2", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131720609, + "cpu_ns": 34747849, + "parse_call_ns": 21999728, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1843.805295, + "output_byte_seconds": 919.628727, + "parser_byte_seconds": 2884.923744, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97113750, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050578.244, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131735071, + "cpu_ns": 34877739, + "parse_call_ns": 22030534, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1847.409016, + "output_byte_seconds": 919.636562, + "parser_byte_seconds": 2888.89668, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 96987832, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050770.82, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "compact", + "repeat": 2, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 132186905, + "cpu_ns": 38709650, + "parse_call_ns": 22486562, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540826.901328, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2931.839266, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 36, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 93736446, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8058254.455, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "shared_copy", + "repeat": 2, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1835008, + "elapsed_ns": 134003964, + "cpu_ns": 36820353, + "parse_call_ns": 22141327, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1907.646695, + "output_byte_seconds": 924.396419, + "parser_byte_seconds": 2903.071145, + "source_allocations": 9, + "output_allocations": 116, + "parser_allocations": 1, + "reuses": 6004, + "budget_waits": 0, + "slot_waits": 25, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97345374, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8093864.112, + "latency_p99_upper_ns": 9437184 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold2", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 132093280, + "cpu_ns": 37140317, + "parse_call_ns": 22338927, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1928.638533, + "output_byte_seconds": 919.864631, + "parser_byte_seconds": 2930.310014, + "source_allocations": 9, + "output_allocations": 68, + "parser_allocations": 1, + "reuses": 6052, + "budget_waits": 0, + "slot_waits": 20, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 95013211, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054865.09, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "borrow", + "repeat": 2, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5554176, + "elapsed_ns": 132469040, + "cpu_ns": 41862764, + "parse_call_ns": 22964342, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541099.43903, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2986.938382, + "source_allocations": 73, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5032, + "budget_waits": 0, + "slot_waits": 49, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 90878204, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8063020.332, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy_no_pool", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 237296, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1802240, + "elapsed_ns": 137450461, + "cpu_ns": 46306762, + "parse_call_ns": 25720817, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2217.245474, + "output_byte_seconds": 934.97033, + "parser_byte_seconds": 2945.941354, + "source_allocations": 4082, + "output_allocations": 1024, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 23, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 91429169, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8191749.104, + "latency_p99_upper_ns": 11534336 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold8", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131976855, + "cpu_ns": 36997295, + "parse_call_ns": 22056693, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1904.528214, + "output_byte_seconds": 920.059714, + "parser_byte_seconds": 2891.295258, + "source_allocations": 9, + "output_allocations": 65, + "parser_allocations": 1, + "reuses": 6055, + "budget_waits": 0, + "slot_waits": 21, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 95230128, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8056258.001, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold2", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 132144262, + "cpu_ns": 36810738, + "parse_call_ns": 22394390, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1914.610493, + "output_byte_seconds": 920.080986, + "parser_byte_seconds": 2937.462741, + "source_allocations": 9, + "output_allocations": 71, + "parser_allocations": 1, + "reuses": 6049, + "budget_waits": 0, + "slot_waits": 39, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 95437850, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8056316.891, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold8", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131627799, + "cpu_ns": 34748983, + "parse_call_ns": 22050963, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1845.118437, + "output_byte_seconds": 920.020651, + "parser_byte_seconds": 2891.313221, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 19, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 96996391, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054019.278, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "compact", + "repeat": 3, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 132093080, + "cpu_ns": 38378509, + "parse_call_ns": 22460168, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540907.798619, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2928.186919, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 43, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 93965206, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8059384.654, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy_no_pool", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 237296, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1802240, + "elapsed_ns": 132246841, + "cpu_ns": 41768202, + "parse_call_ns": 24673720, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2066.312998, + "output_byte_seconds": 920.19798, + "parser_byte_seconds": 2896.434244, + "source_allocations": 4082, + "output_allocations": 1024, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 90608121, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8059680.607, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131689453, + "cpu_ns": 35243759, + "parse_call_ns": 22012604, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1857.757504, + "output_byte_seconds": 919.947028, + "parser_byte_seconds": 2887.51415, + "source_allocations": 9, + "output_allocations": 68, + "parser_allocations": 1, + "reuses": 6052, + "budget_waits": 0, + "slot_waits": 27, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 96622803, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8053765.222, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "shared_copy", + "repeat": 3, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1802240, + "elapsed_ns": 132103712, + "cpu_ns": 35248408, + "parse_call_ns": 22004780, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1858.44117, + "output_byte_seconds": 919.973445, + "parser_byte_seconds": 2885.070598, + "source_allocations": 9, + "output_allocations": 73, + "parser_allocations": 1, + "reuses": 6047, + "budget_waits": 0, + "slot_waits": 22, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97018510, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054006.321, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "borrow", + "repeat": 3, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 132436008, + "cpu_ns": 40869521, + "parse_call_ns": 22656213, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540941.338269, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2953.318689, + "source_allocations": 75, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5030, + "budget_waits": 0, + "slot_waits": 52, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 91954404, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8060462.982, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "compact", + "repeat": 4, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5554176, + "elapsed_ns": 132110379, + "cpu_ns": 38257181, + "parse_call_ns": 22557256, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540811.125254, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2944.323595, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 56, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 94112952, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8057929.717, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold8", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1802240, + "elapsed_ns": 131804916, + "cpu_ns": 34977352, + "parse_call_ns": 22094634, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1853.831647, + "output_byte_seconds": 920.149467, + "parser_byte_seconds": 2897.713188, + "source_allocations": 9, + "output_allocations": 67, + "parser_allocations": 1, + "reuses": 6053, + "budget_waits": 0, + "slot_waits": 31, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 96937037, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8055361.395, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy_no_pool", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 237296, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 135161451, + "cpu_ns": 44330024, + "parse_call_ns": 25503982, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2157.809223, + "output_byte_seconds": 924.543547, + "parser_byte_seconds": 2946.975235, + "source_allocations": 4082, + "output_allocations": 1024, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 35, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 91118882, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8099348.558, + "latency_p99_upper_ns": 10485760 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold2", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1802240, + "elapsed_ns": 132588310, + "cpu_ns": 37673671, + "parse_call_ns": 22518430, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1949.11739, + "output_byte_seconds": 920.155569, + "parser_byte_seconds": 2954.042078, + "source_allocations": 9, + "output_allocations": 78, + "parser_allocations": 1, + "reuses": 6042, + "budget_waits": 0, + "slot_waits": 36, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 94895218, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8057756.78, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 131620718, + "cpu_ns": 34580946, + "parse_call_ns": 21993246, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1837.245494, + "output_byte_seconds": 919.925321, + "parser_byte_seconds": 2884.001314, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 16, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 97154068, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8053071.22, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "borrow", + "repeat": 4, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 4427696, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 132000791, + "cpu_ns": 37927424, + "parse_call_ns": 22507290, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540831.163899, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2938.768696, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 30, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 94241105, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8058140.749, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "shared_copy", + "repeat": 4, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 33536, + "peak_reserved_bytes": 303072, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1818624, + "elapsed_ns": 132283829, + "cpu_ns": 37157688, + "parse_call_ns": 22116989, + "accepted": 1024, + "emitted": 1024, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1917.711679, + "output_byte_seconds": 920.053817, + "parser_byte_seconds": 2900.603467, + "source_allocations": 9, + "output_allocations": 67, + "parser_allocations": 1, + "reuses": 6053, + "budget_waits": 0, + "slot_waits": 38, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 95415784, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8056291.708, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "borrow", + "repeat": 0, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5373952, + "elapsed_ns": 132804710, + "cpu_ns": 49883960, + "parse_call_ns": 22784485, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540593.356702, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2990.235132, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 578, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 84130488, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 45505.588, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070252.164, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8055489.045, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "compact", + "repeat": 0, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4436784, + "source_peak_bytes": 4230192, + "output_peak_bytes": 6944, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 132699783, + "cpu_ns": 50261222, + "parse_call_ns": 22546544, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340105.36302, + "output_byte_seconds": 342.277671, + "parser_byte_seconds": 2958.801865, + "source_allocations": 897, + "output_allocations": 913, + "parser_allocations": 1, + "reuses": 4319, + "budget_waits": 0, + "slot_waits": 550, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83652241, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 45463.711, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069982.729, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054576.42, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "shared_copy", + "repeat": 0, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1835008, + "elapsed_ns": 132438636, + "cpu_ns": 46508730, + "parse_call_ns": 22104890, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1994.687521, + "output_byte_seconds": 1188.73405, + "parser_byte_seconds": 2903.952009, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 538, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87037859, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42492.519, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069205.345, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8052022.729, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 318432, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132301692, + "cpu_ns": 46274024, + "parse_call_ns": 22012014, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1991.776071, + "output_byte_seconds": 1766.57547, + "parser_byte_seconds": 2891.89841, + "source_allocations": 9, + "output_allocations": 1025, + "parser_allocations": 1, + "reuses": 7143, + "budget_waits": 0, + "slot_waits": 563, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 87083196, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42221.575, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068352.743, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8049671.308, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy_no_pool", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 252544, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1933312, + "elapsed_ns": 132221655, + "cpu_ns": 46432790, + "parse_call_ns": 22094470, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2002.66499, + "output_byte_seconds": 1764.18462, + "parser_byte_seconds": 2896.537304, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 534, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 86822233, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42461.52, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068300.921, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054065.407, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold2", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132423215, + "cpu_ns": 46202689, + "parse_call_ns": 22137673, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1992.776523, + "output_byte_seconds": 1183.803646, + "parser_byte_seconds": 2909.259091, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 559, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87298460, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42209.067, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068504.791, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8052025.9, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold8", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132072752, + "cpu_ns": 44818714, + "parse_call_ns": 22007505, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2372.402719, + "output_byte_seconds": 919.579066, + "parser_byte_seconds": 2891.111165, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 531, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 88271004, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 41081.91, + "latency_p99_upper_ns": 49152 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067409.945, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8052229.961, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 318432, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132122049, + "cpu_ns": 45960158, + "parse_call_ns": 22062914, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1985.913742, + "output_byte_seconds": 1766.681864, + "parser_byte_seconds": 2899.647733, + "source_allocations": 9, + "output_allocations": 1025, + "parser_allocations": 1, + "reuses": 7143, + "budget_waits": 0, + "slot_waits": 530, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 87159643, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42080.341, + "latency_p99_upper_ns": 53248 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068084.019, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8053834.564, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy_no_pool", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 252544, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1884160, + "elapsed_ns": 132225178, + "cpu_ns": 46345346, + "parse_call_ns": 22106440, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1998.568774, + "output_byte_seconds": 1764.360213, + "parser_byte_seconds": 2898.609, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 531, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 86929226, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42412.925, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068204.178, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8052346.592, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold2", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132119197, + "cpu_ns": 45785542, + "parse_call_ns": 22119160, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1985.975023, + "output_byte_seconds": 1182.61037, + "parser_byte_seconds": 2906.107753, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 535, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87289352, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42063.476, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068193.573, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8051854.179, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "compact", + "repeat": 1, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4436784, + "source_peak_bytes": 4230192, + "output_peak_bytes": 6944, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5603328, + "elapsed_ns": 132689456, + "cpu_ns": 50225388, + "parse_call_ns": 22679397, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340115.562513, + "output_byte_seconds": 342.313483, + "parser_byte_seconds": 2976.797692, + "source_allocations": 896, + "output_allocations": 908, + "parser_allocations": 1, + "reuses": 4325, + "budget_waits": 0, + "slot_waits": 548, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83654110, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 45534.298, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070135.917, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8055038.37, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold8", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132687220, + "cpu_ns": 45610919, + "parse_call_ns": 22143479, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2398.303122, + "output_byte_seconds": 919.310014, + "parser_byte_seconds": 2908.551305, + "source_allocations": 9, + "output_allocations": 73, + "parser_allocations": 1, + "reuses": 6047, + "budget_waits": 0, + "slot_waits": 557, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 88180228, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 41592.506, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067924.312, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050249.576, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "shared_copy", + "repeat": 1, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 133179387, + "cpu_ns": 47009567, + "parse_call_ns": 22053622, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2001.098596, + "output_byte_seconds": 1195.094634, + "parser_byte_seconds": 2898.481421, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 619, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87400987, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42811.697, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068344.258, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054983.926, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "borrow", + "repeat": 1, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5373952, + "elapsed_ns": 132590517, + "cpu_ns": 49691116, + "parse_call_ns": 22667622, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540625.036225, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2974.002082, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 555, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 84069041, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 45356.652, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069262.154, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8055874.806, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "borrow", + "repeat": 2, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5373952, + "elapsed_ns": 132595906, + "cpu_ns": 48936945, + "parse_call_ns": 22622066, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540538.226737, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2969.006017, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 550, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 84820014, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 44590.665, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069713.765, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054486.477, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold2", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132243573, + "cpu_ns": 45674856, + "parse_call_ns": 22029775, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1978.146096, + "output_byte_seconds": 1181.588579, + "parser_byte_seconds": 2894.51268, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 547, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87607899, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 41881.888, + "latency_p99_upper_ns": 53248 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067912.655, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8049931.179, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "compact", + "repeat": 2, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4436784, + "source_peak_bytes": 4230192, + "output_peak_bytes": 6944, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5603328, + "elapsed_ns": 132732463, + "cpu_ns": 49739534, + "parse_call_ns": 22671992, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340105.112239, + "output_byte_seconds": 342.468555, + "parser_byte_seconds": 2974.922545, + "source_allocations": 893, + "output_allocations": 887, + "parser_allocations": 1, + "reuses": 4349, + "budget_waits": 0, + "slot_waits": 562, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 84142850, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 44979.318, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069891.561, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8056164.197, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold8", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132272059, + "cpu_ns": 45320979, + "parse_call_ns": 22141356, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2389.486333, + "output_byte_seconds": 919.390876, + "parser_byte_seconds": 2908.882515, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 563, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 88007671, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 41406.16, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068143.112, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050851.553, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 318432, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1867776, + "elapsed_ns": 132351078, + "cpu_ns": 46122525, + "parse_call_ns": 22082400, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1991.903399, + "output_byte_seconds": 1762.771138, + "parser_byte_seconds": 2900.645032, + "source_allocations": 9, + "output_allocations": 1026, + "parser_allocations": 1, + "reuses": 7142, + "budget_waits": 0, + "slot_waits": 558, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 87297075, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42091.413, + "latency_p99_upper_ns": 53248 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068444.485, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8048834.24, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "shared_copy", + "repeat": 2, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132284819, + "cpu_ns": 45823432, + "parse_call_ns": 22066434, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1983.569581, + "output_byte_seconds": 1182.323172, + "parser_byte_seconds": 2898.432644, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 542, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87424368, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42064.699, + "latency_p99_upper_ns": 53248 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068229.227, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050900.843, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy_no_pool", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 252544, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1900544, + "elapsed_ns": 132459707, + "cpu_ns": 47692750, + "parse_call_ns": 22170762, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2023.677274, + "output_byte_seconds": 1777.322506, + "parser_byte_seconds": 2907.132194, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 540, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 85990890, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 43327.485, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070694.294, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8055086.674, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "shared_copy", + "repeat": 3, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132140503, + "cpu_ns": 45825210, + "parse_call_ns": 22125197, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1985.69773, + "output_byte_seconds": 1183.215978, + "parser_byte_seconds": 2906.687041, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 533, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87310088, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42062.03, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068036.782, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050191.375, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy_no_pool", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 252544, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1916928, + "elapsed_ns": 132297747, + "cpu_ns": 46669804, + "parse_call_ns": 22175323, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2009.648765, + "output_byte_seconds": 1766.146335, + "parser_byte_seconds": 2907.094824, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 542, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 86663675, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42678.905, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068637.263, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054142.04, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 318432, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132282510, + "cpu_ns": 46415608, + "parse_call_ns": 22079359, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1996.444688, + "output_byte_seconds": 1765.761752, + "parser_byte_seconds": 2901.17633, + "source_allocations": 9, + "output_allocations": 1025, + "parser_allocations": 1, + "reuses": 7143, + "budget_waits": 0, + "slot_waits": 554, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 86986908, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42277.028, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070038.574, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050279.443, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "borrow", + "repeat": 3, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5373952, + "elapsed_ns": 132489898, + "cpu_ns": 48808681, + "parse_call_ns": 22654163, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540484.370521, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2972.753951, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 556, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 84863248, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 44578.165, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069410.422, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8053620.063, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold2", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1835008, + "elapsed_ns": 132283398, + "cpu_ns": 45911917, + "parse_call_ns": 22085581, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1984.871517, + "output_byte_seconds": 1183.416247, + "parser_byte_seconds": 2903.347153, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 561, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87433143, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42043.254, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068356.958, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8050581.195, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold8", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132082805, + "cpu_ns": 44815017, + "parse_call_ns": 22070822, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2374.331913, + "output_byte_seconds": 919.214871, + "parser_byte_seconds": 2899.672121, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 534, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 88275454, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 41070.548, + "latency_p99_upper_ns": 53248 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067369.015, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8049008.122, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "compact", + "repeat": 3, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4436784, + "source_peak_bytes": 4230192, + "output_peak_bytes": 7056, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5488640, + "elapsed_ns": 132597323, + "cpu_ns": 49981497, + "parse_call_ns": 22749218, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340108.105991, + "output_byte_seconds": 342.554096, + "parser_byte_seconds": 2986.081381, + "source_allocations": 912, + "output_allocations": 907, + "parser_allocations": 1, + "reuses": 4310, + "budget_waits": 0, + "slot_waits": 563, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83819563, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 45265.223, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069991.558, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8056976.805, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy_no_pool", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 252544, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1900544, + "elapsed_ns": 132985160, + "cpu_ns": 49981054, + "parse_call_ns": 23442854, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2129.142526, + "output_byte_seconds": 1795.211172, + "parser_byte_seconds": 3074.346252, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 542, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 84089494, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 45510.754, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5098662.604, + "latency_p99_upper_ns": 6291456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8056999.864, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "borrow", + "repeat": 4, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5373952, + "elapsed_ns": 133433884, + "cpu_ns": 51462618, + "parse_call_ns": 23747967, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 540814.68462, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 3118.422109, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 596, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83315856, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 46811.327, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070925.9, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8058845.673, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold2", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1867776, + "elapsed_ns": 132186422, + "cpu_ns": 45811373, + "parse_call_ns": 22083462, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1983.12097, + "output_byte_seconds": 1182.647891, + "parser_byte_seconds": 2900.448352, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 532, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87421696, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42006.171, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068025.061, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8049545.414, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "shared_copy", + "repeat": 4, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 39984, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132188663, + "cpu_ns": 45991210, + "parse_call_ns": 22145612, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1991.371251, + "output_byte_seconds": 1184.961172, + "parser_byte_seconds": 2909.800223, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 7080, + "budget_waits": 0, + "slot_waits": 543, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 87186189, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42139.616, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068490.238, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8052428.242, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "compact", + "repeat": 4, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4436784, + "source_peak_bytes": 4230192, + "output_peak_bytes": 7056, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5537792, + "elapsed_ns": 132746292, + "cpu_ns": 50870250, + "parse_call_ns": 22660664, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340246.674827, + "output_byte_seconds": 342.038296, + "parser_byte_seconds": 2973.982939, + "source_allocations": 893, + "output_allocations": 895, + "parser_allocations": 1, + "reuses": 4341, + "budget_waits": 0, + "slot_waits": 545, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83141858, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 45809.599, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5072116.599, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8054634.315, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold8", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 311264, + "source_peak_bytes": 98400, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1851392, + "elapsed_ns": 132177385, + "cpu_ns": 44977578, + "parse_call_ns": 22128368, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2381.095196, + "output_byte_seconds": 919.555429, + "parser_byte_seconds": 2907.397974, + "source_allocations": 9, + "output_allocations": 64, + "parser_allocations": 1, + "reuses": 6056, + "budget_waits": 0, + "slot_waits": 539, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 88187889, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 41268.295, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067698.961, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8052209.973, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 318432, + "source_peak_bytes": 98400, + "output_peak_bytes": 47152, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 1867776, + "elapsed_ns": 132225648, + "cpu_ns": 46118905, + "parse_call_ns": 22053057, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 1988.501642, + "output_byte_seconds": 1767.427593, + "parser_byte_seconds": 2896.491937, + "source_allocations": 9, + "output_allocations": 1025, + "parser_allocations": 1, + "reuses": 7143, + "budget_waits": 0, + "slot_waits": 536, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 87128865, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 42165.024, + "latency_p99_upper_ns": 53248 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068266.918, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8051060.116, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy_no_pool", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4373248, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6078464, + "elapsed_ns": 216945564, + "cpu_ns": 116157398, + "parse_call_ns": 25703347, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 3917.681175, + "output_byte_seconds": 512250.563648, + "parser_byte_seconds": 3150.964519, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 383, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 48939452, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 117402.264, + "latency_p99_upper_ns": 524288 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 6063368.282, + "latency_p99_upper_ns": 31457280 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 9260486.827, + "latency_p99_upper_ns": 33554432 + } + ] + }, + { + "case": "same_large_field", + "variant": "shared_copy", + "repeat": 0, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3178496, + "elapsed_ns": 133534289, + "cpu_ns": 66640259, + "parse_call_ns": 22303696, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2044.238204, + "output_byte_seconds": 269834.895225, + "parser_byte_seconds": 2930.529378, + "source_allocations": 82, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6047, + "budget_waits": 0, + "slot_waits": 500, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 67897726, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51484.234, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078396.125, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8065897.697, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "borrow", + "repeat": 0, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133553215, + "cpu_ns": 68694192, + "parse_call_ns": 22344284, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541236.958782, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2932.875901, + "source_allocations": 76, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5029, + "budget_waits": 0, + "slot_waits": 479, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 65966946, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52115.836, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078241.217, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8065472.756, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4504544, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5292032, + "elapsed_ns": 134319516, + "cpu_ns": 74842901, + "parse_call_ns": 22151327, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2277.11069, + "output_byte_seconds": 440218.115158, + "parser_byte_seconds": 2909.330552, + "source_allocations": 9, + "output_allocations": 841, + "parser_allocations": 1, + "reuses": 7327, + "budget_waits": 0, + "slot_waits": 490, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60554269, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 57181.88, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5083619.048, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8071315.085, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold2", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3194880, + "elapsed_ns": 133454646, + "cpu_ns": 66234578, + "parse_call_ns": 22159339, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2031.818258, + "output_byte_seconds": 269861.554419, + "parser_byte_seconds": 2910.266617, + "source_allocations": 78, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6051, + "budget_waits": 0, + "slot_waits": 487, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 68332882, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51484.504, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5077341.499, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8066441.419, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "compact", + "repeat": 0, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2001776, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 134096312, + "cpu_ns": 74886880, + "parse_call_ns": 22733900, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 341111.908821, + "output_byte_seconds": 100293.13752, + "parser_byte_seconds": 2983.138393, + "source_allocations": 921, + "output_allocations": 779, + "parser_allocations": 1, + "reuses": 4429, + "budget_waits": 0, + "slot_waits": 471, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60362834, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 54080.027, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5083405.479, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068401.165, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold8", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133447386, + "cpu_ns": 68765370, + "parse_call_ns": 22353361, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541359.828153, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2934.692831, + "source_allocations": 77, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5028, + "budget_waits": 0, + "slot_waits": 487, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 65809490, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52372.427, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078845.858, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067374.837, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "compact", + "repeat": 1, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2034592, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 134204038, + "cpu_ns": 74836392, + "parse_call_ns": 22853483, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 341117.919471, + "output_byte_seconds": 100364.674661, + "parser_byte_seconds": 2998.098529, + "source_allocations": 881, + "output_allocations": 767, + "parser_allocations": 1, + "reuses": 4481, + "budget_waits": 0, + "slot_waits": 478, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60375968, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 54356.541, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5083649.875, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070735.206, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "shared_copy", + "repeat": 1, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3244032, + "elapsed_ns": 133593447, + "cpu_ns": 66172548, + "parse_call_ns": 22181499, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2031.917671, + "output_byte_seconds": 269851.910584, + "parser_byte_seconds": 2913.380586, + "source_allocations": 80, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6049, + "budget_waits": 0, + "slot_waits": 471, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 68489180, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51688.946, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5077231.39, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8066199.086, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4504544, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5292032, + "elapsed_ns": 134436483, + "cpu_ns": 75053693, + "parse_call_ns": 22215239, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2284.410516, + "output_byte_seconds": 440251.541897, + "parser_byte_seconds": 2917.091676, + "source_allocations": 9, + "output_allocations": 832, + "parser_allocations": 1, + "reuses": 7336, + "budget_waits": 0, + "slot_waits": 483, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60480780, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 57382.812, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5083530.123, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8072471.683, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold2", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3194880, + "elapsed_ns": 134034428, + "cpu_ns": 69077852, + "parse_call_ns": 22386881, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2071.219143, + "output_byte_seconds": 269907.925264, + "parser_byte_seconds": 2941.415878, + "source_allocations": 82, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6047, + "budget_waits": 0, + "slot_waits": 521, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 66160244, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52463.206, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5079970.677, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068543.087, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold8", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133602318, + "cpu_ns": 69361342, + "parse_call_ns": 22347294, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541336.354919, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2933.602437, + "source_allocations": 76, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5029, + "budget_waits": 0, + "slot_waits": 500, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 65433839, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52034.221, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5079700.568, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067086.654, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "borrow", + "repeat": 1, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133878082, + "cpu_ns": 69431646, + "parse_call_ns": 22393309, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541341.633608, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2938.771712, + "source_allocations": 78, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5027, + "budget_waits": 0, + "slot_waits": 504, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 65629618, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52136.236, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078986.712, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067041.718, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy_no_pool", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4373248, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5816320, + "elapsed_ns": 134331933, + "cpu_ns": 75355273, + "parse_call_ns": 22600489, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2317.391148, + "output_byte_seconds": 440282.284319, + "parser_byte_seconds": 2923.56271, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 497, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60100461, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 58242.85, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5084649.973, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8073924.058, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "compact", + "repeat": 2, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2001776, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 134074359, + "cpu_ns": 74679639, + "parse_call_ns": 22736073, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 341236.488763, + "output_byte_seconds": 100267.211633, + "parser_byte_seconds": 2982.994817, + "source_allocations": 917, + "output_allocations": 794, + "parser_allocations": 1, + "reuses": 4418, + "budget_waits": 0, + "slot_waits": 471, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60515136, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 53738.105, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5085195.058, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069412.397, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4504544, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5275648, + "elapsed_ns": 136064340, + "cpu_ns": 79416596, + "parse_call_ns": 22572078, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2363.378088, + "output_byte_seconds": 440586.829499, + "parser_byte_seconds": 2964.218039, + "source_allocations": 9, + "output_allocations": 623, + "parser_allocations": 1, + "reuses": 7545, + "budget_waits": 0, + "slot_waits": 463, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 57551670, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 60112.085, + "latency_p99_upper_ns": 114688 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5087074.439, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8080314.649, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold2", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3244032, + "elapsed_ns": 133655494, + "cpu_ns": 66354625, + "parse_call_ns": 22289900, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2049.709126, + "output_byte_seconds": 269797.797722, + "parser_byte_seconds": 2927.700988, + "source_allocations": 78, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6051, + "budget_waits": 0, + "slot_waits": 500, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 68212138, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51495.073, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078118.015, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8064823.351, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "shared_copy", + "repeat": 2, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3293184, + "elapsed_ns": 133683827, + "cpu_ns": 66973903, + "parse_call_ns": 22176603, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2039.054037, + "output_byte_seconds": 269818.155226, + "parser_byte_seconds": 2912.084727, + "source_allocations": 78, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6051, + "budget_waits": 0, + "slot_waits": 494, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 67816366, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51459.966, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5079235.137, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8065334.704, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold8", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133681327, + "cpu_ns": 68797289, + "parse_call_ns": 22338685, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541289.267391, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2932.086428, + "source_allocations": 77, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5028, + "budget_waits": 0, + "slot_waits": 482, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 65978872, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52563.022, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078578.492, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8066255.165, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "borrow", + "repeat": 2, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133814643, + "cpu_ns": 68888479, + "parse_call_ns": 22335355, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541341.207084, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2930.637289, + "source_allocations": 79, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5026, + "budget_waits": 0, + "slot_waits": 510, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 66149871, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51550.405, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5079607.126, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067127.786, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy_no_pool", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4373248, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5816320, + "elapsed_ns": 134471775, + "cpu_ns": 75393460, + "parse_call_ns": 22629325, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2321.181236, + "output_byte_seconds": 440258.792854, + "parser_byte_seconds": 2921.16977, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 508, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60227676, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 57516.822, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5084982.783, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8073926.553, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold2", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3194880, + "elapsed_ns": 133441147, + "cpu_ns": 66434679, + "parse_call_ns": 22119254, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2030.079604, + "output_byte_seconds": 269825.85573, + "parser_byte_seconds": 2905.779822, + "source_allocations": 80, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6049, + "budget_waits": 0, + "slot_waits": 482, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 68150063, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51190.857, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5077733.031, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8065349.133, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy_no_pool", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4373248, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5816320, + "elapsed_ns": 134289332, + "cpu_ns": 76002725, + "parse_call_ns": 22740343, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2334.193158, + "output_byte_seconds": 440332.993703, + "parser_byte_seconds": 2936.962125, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 504, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 59412631, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 59077.132, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5084960.638, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8075279.097, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "shared_copy", + "repeat": 3, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3309568, + "elapsed_ns": 133495777, + "cpu_ns": 66421587, + "parse_call_ns": 22309367, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2045.123956, + "output_byte_seconds": 269842.975673, + "parser_byte_seconds": 2931.949276, + "source_allocations": 76, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6053, + "budget_waits": 0, + "slot_waits": 497, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 68249668, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51216.396, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078028.333, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8066089.677, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "borrow", + "repeat": 3, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 134337593, + "cpu_ns": 71105404, + "parse_call_ns": 22575073, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541448.462493, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2962.344728, + "source_allocations": 83, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5022, + "budget_waits": 0, + "slot_waits": 495, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 64431294, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 53339.614, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5080292.879, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068765.424, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4504544, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5292032, + "elapsed_ns": 134254523, + "cpu_ns": 76196602, + "parse_call_ns": 22285338, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2295.795833, + "output_byte_seconds": 440394.753415, + "parser_byte_seconds": 2927.722098, + "source_allocations": 9, + "output_allocations": 820, + "parser_allocations": 1, + "reuses": 7348, + "budget_waits": 0, + "slot_waits": 485, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 59133245, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 58635.959, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5084371.237, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8075247.27, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "compact", + "repeat": 3, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2034592, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 134306041, + "cpu_ns": 75274107, + "parse_call_ns": 22714288, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 341191.973388, + "output_byte_seconds": 100297.728905, + "parser_byte_seconds": 2980.354846, + "source_allocations": 917, + "output_allocations": 749, + "parser_allocations": 1, + "reuses": 4463, + "budget_waits": 0, + "slot_waits": 478, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 60108647, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 54208.683, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5084673.227, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069796.873, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold8", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133817208, + "cpu_ns": 69542587, + "parse_call_ns": 22437830, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541408.186663, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2945.936502, + "source_allocations": 80, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5025, + "budget_waits": 0, + "slot_waits": 483, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 65488224, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52016.168, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5080750.668, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068031.861, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy_no_pool", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4373248, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5799936, + "elapsed_ns": 134597856, + "cpu_ns": 77396908, + "parse_call_ns": 22833216, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2355.524795, + "output_byte_seconds": 440416.150759, + "parser_byte_seconds": 2941.859195, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 512, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 58493375, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 59008.762, + "latency_p99_upper_ns": 98304 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5086107.088, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8076875.839, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4504544, + "source_peak_bytes": 98400, + "output_peak_bytes": 4233264, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5292032, + "elapsed_ns": 139508137, + "cpu_ns": 88332555, + "parse_call_ns": 22864596, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 100663296, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2528.253394, + "output_byte_seconds": 442384.852596, + "parser_byte_seconds": 3002.901193, + "source_allocations": 9, + "output_allocations": 688, + "parser_allocations": 1, + "reuses": 7480, + "budget_waits": 0, + "slot_waits": 372, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51495885, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 66698.854, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5129096.339, + "latency_p99_upper_ns": 6815744 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8093643.623, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold8", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4833280, + "elapsed_ns": 134457305, + "cpu_ns": 70251788, + "parse_call_ns": 22431999, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541395.863387, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2942.397573, + "source_allocations": 87, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5018, + "budget_waits": 0, + "slot_waits": 495, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 65292089, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 53169.212, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5080238.856, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068047.314, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold2", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3211264, + "elapsed_ns": 133756102, + "cpu_ns": 67373341, + "parse_call_ns": 22155214, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2045.927873, + "output_byte_seconds": 269868.863264, + "parser_byte_seconds": 2910.706656, + "source_allocations": 81, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6048, + "budget_waits": 0, + "slot_waits": 506, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 67548515, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 51135.428, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078670.025, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8066898.388, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "shared_copy", + "repeat": 4, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2371504, + "source_peak_bytes": 98400, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3571712, + "elapsed_ns": 135030934, + "cpu_ns": 71236281, + "parse_call_ns": 22446961, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2100.123381, + "output_byte_seconds": 269953.875704, + "parser_byte_seconds": 2948.934561, + "source_allocations": 94, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 6035, + "budget_waits": 0, + "slot_waits": 507, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 2048, + "sleep_ns": 65033726, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 53626.014, + "latency_p99_upper_ns": 90112 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5087279.412, + "latency_p99_upper_ns": 5767168 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070672.959, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "compact", + "repeat": 4, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 1936144, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 135831514, + "cpu_ns": 84355786, + "parse_call_ns": 23107923, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 341580.411374, + "output_byte_seconds": 100419.005421, + "parser_byte_seconds": 3032.899482, + "source_allocations": 853, + "output_allocations": 676, + "parser_allocations": 1, + "reuses": 4600, + "budget_waits": 0, + "slot_waits": 455, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 52913091, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 60319.001, + "latency_p99_upper_ns": 114688 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5091148.147, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8079928.47, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "borrow", + "repeat": 4, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133667291, + "cpu_ns": 68534602, + "parse_call_ns": 22264001, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541292.227145, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2921.518156, + "source_allocations": 77, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5028, + "budget_waits": 0, + "slot_waits": 481, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 66216721, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 1, + "latency_mean_ns": 52072.538, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 5078370.622, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8066280.939, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378784, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3899392, + "elapsed_ns": 133407209, + "cpu_ns": 54623208, + "parse_call_ns": 22433685, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2079.608844, + "output_byte_seconds": 270519.575773, + "parser_byte_seconds": 2946.904954, + "source_allocations": 118, + "output_allocations": 838, + "parser_allocations": 1, + "reuses": 7221, + "budget_waits": 0, + "slot_waits": 547, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 79664236, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 47700.066, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070062.768, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069584.641, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold2", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3751936, + "elapsed_ns": 133312135, + "cpu_ns": 53364925, + "parse_call_ns": 22316231, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2049.372724, + "output_byte_seconds": 270524.406644, + "parser_byte_seconds": 2928.92106, + "source_allocations": 137, + "output_allocations": 841, + "parser_allocations": 1, + "reuses": 6175, + "budget_waits": 0, + "slot_waits": 552, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 80930688, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46742.189, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068879.809, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069119.599, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "shared_copy", + "repeat": 0, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3948544, + "elapsed_ns": 154001095, + "cpu_ns": 65368726, + "parse_call_ns": 23437273, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2683.230182, + "output_byte_seconds": 276097.966097, + "parser_byte_seconds": 3083.783614, + "source_allocations": 228, + "output_allocations": 746, + "parser_allocations": 1, + "reuses": 6179, + "budget_waits": 0, + "slot_waits": 542, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 75659422, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 64443.196, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5134851.331, + "latency_p99_upper_ns": 7340032 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8252758.487, + "latency_p99_upper_ns": 15728640 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy_no_pool", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2312896, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3194880, + "elapsed_ns": 133648283, + "cpu_ns": 61986524, + "parse_call_ns": 25370210, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2299.022458, + "output_byte_seconds": 270612.233717, + "parser_byte_seconds": 2983.123314, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 653, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 72630111, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 53967.42, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5080263.648, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8075763.615, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "compact", + "repeat": 0, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2067408, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 133472857, + "cpu_ns": 62602975, + "parse_call_ns": 23134595, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340709.17918, + "output_byte_seconds": 100724.838498, + "parser_byte_seconds": 3036.377571, + "source_allocations": 923, + "output_allocations": 841, + "parser_allocations": 1, + "reuses": 4365, + "budget_waits": 0, + "slot_waits": 526, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 71679132, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 48473.289, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5073065.738, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070772.518, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold8", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4443056, + "source_peak_bytes": 4230192, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4751360, + "elapsed_ns": 133297584, + "cpu_ns": 55094453, + "parse_call_ns": 22718797, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541537.659914, + "output_byte_seconds": 577.166247, + "parser_byte_seconds": 2982.289653, + "source_allocations": 130, + "output_allocations": 809, + "parser_allocations": 1, + "reuses": 5190, + "budget_waits": 0, + "slot_waits": 541, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 79050000, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 48100.386, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069482.618, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070037.129, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "borrow", + "repeat": 0, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133477288, + "cpu_ns": 54372368, + "parse_call_ns": 22466675, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541562.393801, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2949.789857, + "source_allocations": 80, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5025, + "budget_waits": 0, + "slot_waits": 554, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 80055686, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46918.51, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069158.581, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070277.888, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold2", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3932160, + "elapsed_ns": 133312509, + "cpu_ns": 53734268, + "parse_call_ns": 22533891, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2071.170012, + "output_byte_seconds": 270512.249274, + "parser_byte_seconds": 2959.58098, + "source_allocations": 159, + "output_allocations": 828, + "parser_allocations": 1, + "reuses": 6166, + "budget_waits": 0, + "slot_waits": 575, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 80473327, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46789.037, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070040.373, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069081.323, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy_no_pool", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2312896, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3194880, + "elapsed_ns": 134281084, + "cpu_ns": 62190824, + "parse_call_ns": 25213500, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2300.277794, + "output_byte_seconds": 270647.838041, + "parser_byte_seconds": 2975.251132, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 663, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 73016473, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 54287.927, + "latency_p99_upper_ns": 98304 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5080276.456, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8077074.059, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378784, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3948544, + "elapsed_ns": 133395362, + "cpu_ns": 55263103, + "parse_call_ns": 22335683, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2070.323199, + "output_byte_seconds": 270570.958879, + "parser_byte_seconds": 2935.679247, + "source_allocations": 123, + "output_allocations": 825, + "parser_allocations": 1, + "reuses": 7229, + "budget_waits": 0, + "slot_waits": 563, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 79157676, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 47428.456, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070071.895, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070912.004, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "shared_copy", + "repeat": 1, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3522560, + "elapsed_ns": 133405307, + "cpu_ns": 53873137, + "parse_call_ns": 22349414, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2061.271642, + "output_byte_seconds": 270527.998952, + "parser_byte_seconds": 2936.171471, + "source_allocations": 144, + "output_allocations": 811, + "parser_allocations": 1, + "reuses": 6198, + "budget_waits": 0, + "slot_waits": 551, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 80515381, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46564.507, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069570.9, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069438.264, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "compact", + "repeat": 1, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 133295478, + "cpu_ns": 60929076, + "parse_call_ns": 22892803, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340588.424197, + "output_byte_seconds": 100730.999866, + "parser_byte_seconds": 3007.852808, + "source_allocations": 919, + "output_allocations": 842, + "parser_allocations": 1, + "reuses": 4368, + "budget_waits": 0, + "slot_waits": 524, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 73271182, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 48259.733, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5071843.127, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069717.131, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "borrow", + "repeat": 1, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5390336, + "elapsed_ns": 133317978, + "cpu_ns": 54530428, + "parse_call_ns": 22789187, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541551.170985, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2991.654899, + "source_allocations": 72, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5033, + "budget_waits": 0, + "slot_waits": 541, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 79523636, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 47420.19, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5069779.505, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070214.071, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold8", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4443056, + "source_peak_bytes": 4230192, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5357568, + "elapsed_ns": 133341496, + "cpu_ns": 54857945, + "parse_call_ns": 22618201, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541518.37496, + "output_byte_seconds": 577.265132, + "parser_byte_seconds": 2971.205031, + "source_allocations": 122, + "output_allocations": 835, + "parser_allocations": 1, + "reuses": 5172, + "budget_waits": 0, + "slot_waits": 562, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 79396532, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 47550.497, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070242.543, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069675.14, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold8", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4443056, + "source_peak_bytes": 4230192, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5292032, + "elapsed_ns": 134650847, + "cpu_ns": 58145169, + "parse_call_ns": 22801055, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 542663.104532, + "output_byte_seconds": 578.298773, + "parser_byte_seconds": 2995.063232, + "source_allocations": 127, + "output_allocations": 804, + "parser_allocations": 1, + "reuses": 5198, + "budget_waits": 0, + "slot_waits": 553, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 77457652, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 49310.575, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5080294.837, + "latency_p99_upper_ns": 5767168 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8086977.866, + "latency_p99_upper_ns": 9437184 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378784, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3964928, + "elapsed_ns": 133179940, + "cpu_ns": 52486508, + "parse_call_ns": 22146823, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2028.917653, + "output_byte_seconds": 270508.558846, + "parser_byte_seconds": 2910.035715, + "source_allocations": 119, + "output_allocations": 875, + "parser_allocations": 1, + "reuses": 7183, + "budget_waits": 0, + "slot_waits": 532, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 81744820, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46179.91, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068139.702, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068218.996, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "borrow", + "repeat": 2, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133254368, + "cpu_ns": 52756869, + "parse_call_ns": 22242096, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541465.495701, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2919.456949, + "source_allocations": 79, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5026, + "budget_waits": 0, + "slot_waits": 537, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 81620293, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 45804.026, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067325.337, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068611.8, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "compact", + "repeat": 2, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 133273853, + "cpu_ns": 59503628, + "parse_call_ns": 22579568, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340521.103381, + "output_byte_seconds": 100725.988305, + "parser_byte_seconds": 2964.439632, + "source_allocations": 921, + "output_allocations": 839, + "parser_allocations": 1, + "reuses": 4369, + "budget_waits": 0, + "slot_waits": 529, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 75001351, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46820.147, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5071087.53, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068767.35, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "shared_copy", + "repeat": 2, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3293184, + "elapsed_ns": 133216315, + "cpu_ns": 51631438, + "parse_call_ns": 22079426, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2014.496107, + "output_byte_seconds": 270449.091989, + "parser_byte_seconds": 2901.711431, + "source_allocations": 152, + "output_allocations": 827, + "parser_allocations": 1, + "reuses": 6174, + "budget_waits": 0, + "slot_waits": 543, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 82698735, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 45041.785, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068002.393, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8066161.602, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy_no_pool", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2312896, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3178496, + "elapsed_ns": 133362779, + "cpu_ns": 58084129, + "parse_call_ns": 24178012, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2196.167723, + "output_byte_seconds": 270535.919777, + "parser_byte_seconds": 2934.024512, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 605, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 76526627, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 50758.917, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5074265.021, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8071772.438, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold2", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3751936, + "elapsed_ns": 133094723, + "cpu_ns": 52669444, + "parse_call_ns": 22153975, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2026.350691, + "output_byte_seconds": 270487.081841, + "parser_byte_seconds": 2910.446252, + "source_allocations": 162, + "output_allocations": 813, + "parser_allocations": 1, + "reuses": 6178, + "budget_waits": 0, + "slot_waits": 529, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 81515465, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46192.459, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067955.414, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067608.796, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "compact", + "repeat": 3, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6340608, + "elapsed_ns": 133282278, + "cpu_ns": 59568109, + "parse_call_ns": 22558632, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340502.324772, + "output_byte_seconds": 100714.490169, + "parser_byte_seconds": 2961.00573, + "source_allocations": 923, + "output_allocations": 826, + "parser_allocations": 1, + "reuses": 4380, + "budget_waits": 0, + "slot_waits": 521, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 74934523, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46941.425, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5070856.899, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068200.595, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy_no_pool", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2312896, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3194880, + "elapsed_ns": 133540522, + "cpu_ns": 58472614, + "parse_call_ns": 24345793, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2208.417867, + "output_byte_seconds": 270579.890216, + "parser_byte_seconds": 2933.62499, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 602, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 76325826, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 51364.197, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5074603.185, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8073207.582, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "borrow", + "repeat": 3, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133275839, + "cpu_ns": 52234245, + "parse_call_ns": 22266351, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541367.893955, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2922.935825, + "source_allocations": 79, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5026, + "budget_waits": 0, + "slot_waits": 545, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 82113024, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 45842.847, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5066801.978, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067096.868, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "shared_copy", + "repeat": 3, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3899392, + "elapsed_ns": 133064931, + "cpu_ns": 52117164, + "parse_call_ns": 22057182, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2015.469116, + "output_byte_seconds": 270480.700872, + "parser_byte_seconds": 2897.68644, + "source_allocations": 130, + "output_allocations": 842, + "parser_allocations": 1, + "reuses": 6181, + "budget_waits": 0, + "slot_waits": 555, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 82111301, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 45400.789, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068255.587, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067189.304, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold8", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4443056, + "source_peak_bytes": 4230192, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5292032, + "elapsed_ns": 133337450, + "cpu_ns": 53670610, + "parse_call_ns": 22263776, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541459.255198, + "output_byte_seconds": 577.165832, + "parser_byte_seconds": 2922.14622, + "source_allocations": 137, + "output_allocations": 796, + "parser_allocations": 1, + "reuses": 5196, + "budget_waits": 0, + "slot_waits": 551, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 80837357, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46321.397, + "latency_p99_upper_ns": 65536 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068514.702, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8068639.91, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold2", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3522560, + "elapsed_ns": 134333542, + "cpu_ns": 52726291, + "parse_call_ns": 22219255, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2039.714059, + "output_byte_seconds": 270798.061653, + "parser_byte_seconds": 2918.877137, + "source_allocations": 140, + "output_allocations": 894, + "parser_allocations": 1, + "reuses": 6119, + "budget_waits": 0, + "slot_waits": 516, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 82638596, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46056.021, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5218345.888, + "latency_p99_upper_ns": 9437184 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8076530.434, + "latency_p99_upper_ns": 9437184 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378784, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3948544, + "elapsed_ns": 133174357, + "cpu_ns": 52057974, + "parse_call_ns": 22117835, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2021.284206, + "output_byte_seconds": 270501.113842, + "parser_byte_seconds": 2905.858494, + "source_allocations": 114, + "output_allocations": 849, + "parser_allocations": 1, + "reuses": 7214, + "budget_waits": 0, + "slot_waits": 548, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 82257586, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 45428.889, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068142.38, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067808.96, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy_no_pool", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2312896, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3178496, + "elapsed_ns": 133410638, + "cpu_ns": 57999768, + "parse_call_ns": 24134627, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2191.645151, + "output_byte_seconds": 270510.58659, + "parser_byte_seconds": 2926.663567, + "source_allocations": 4082, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 603, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 76638749, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 51016.097, + "latency_p99_upper_ns": 81920 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5074074.877, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8071032.382, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold2", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3342336, + "elapsed_ns": 133213102, + "cpu_ns": 52362717, + "parse_call_ns": 22103023, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2022.420003, + "output_byte_seconds": 270482.639278, + "parser_byte_seconds": 2904.168619, + "source_allocations": 127, + "output_allocations": 855, + "parser_allocations": 1, + "reuses": 6171, + "budget_waits": 0, + "slot_waits": 539, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 81964913, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46044.098, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5068262.707, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067371.07, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378784, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107504, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3751936, + "elapsed_ns": 133177547, + "cpu_ns": 52033688, + "parse_call_ns": 22154380, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33669120, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2025.354638, + "output_byte_seconds": 270478.813202, + "parser_byte_seconds": 2910.934805, + "source_allocations": 129, + "output_allocations": 851, + "parser_allocations": 1, + "reuses": 7197, + "budget_waits": 0, + "slot_waits": 527, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 82109654, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 45825.604, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067937.156, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067321.024, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "borrow", + "repeat": 4, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4435888, + "source_peak_bytes": 4230192, + "output_peak_bytes": 0, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 4767744, + "elapsed_ns": 133311566, + "cpu_ns": 53725285, + "parse_call_ns": 22239925, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 0, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541575.408384, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 2920.340305, + "source_allocations": 80, + "output_allocations": 0, + "parser_allocations": 1, + "reuses": 5025, + "budget_waits": 0, + "slot_waits": 555, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 80727666, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46306.509, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067147.548, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8070390.562, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold8", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4443056, + "source_peak_bytes": 4230192, + "output_peak_bytes": 7168, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 5439488, + "elapsed_ns": 134101658, + "cpu_ns": 54158047, + "parse_call_ns": 22315394, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 541512.874989, + "output_byte_seconds": 578.716358, + "parser_byte_seconds": 2931.279646, + "source_allocations": 161, + "output_allocations": 826, + "parser_allocations": 1, + "reuses": 5142, + "budget_waits": 0, + "slot_waits": 533, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 81103821, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46533.691, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5082538.041, + "latency_p99_upper_ns": 6291456 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8069591.938, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "shared_copy", + "repeat": 4, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2378672, + "source_peak_bytes": 98400, + "output_peak_bytes": 2107392, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 3948544, + "elapsed_ns": 133134639, + "cpu_ns": 52075173, + "parse_call_ns": 22128273, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33611776, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 2026.862393, + "output_byte_seconds": 270471.476005, + "parser_byte_seconds": 2908.200691, + "source_allocations": 122, + "output_allocations": 816, + "parser_allocations": 1, + "reuses": 6215, + "budget_waits": 0, + "slot_waits": 512, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 82080012, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 46182.627, + "latency_p99_upper_ns": 61440 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5067504.389, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067072.688, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "compact", + "repeat": 4, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 8388608, + "fixed_bytes": 41728, + "peak_reserved_bytes": 4730672, + "source_peak_bytes": 4230192, + "output_peak_bytes": 2100224, + "parser_peak_bytes": 131120, + "rss_peak_bytes": 6406144, + "elapsed_ns": 133118662, + "cpu_ns": 59607669, + "parse_call_ns": 22648269, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 42083156, + "selection_copy_bytes": 33554432, + "reader_copy_bytes": 75711296, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 340524.867797, + "output_byte_seconds": 100684.088672, + "parser_byte_seconds": 2973.798584, + "source_allocations": 921, + "output_allocations": 855, + "parser_allocations": 1, + "reuses": 4353, + "budget_waits": 0, + "slot_waits": 532, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 74562185, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 1, + "latency_mean_ns": 47236.674, + "latency_p99_upper_ns": 73728 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5071269.907, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 33554432, + "peak_count": 64, + "latency_mean_ns": 8067723.707, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1104848, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134798098, + "cpu_ns": 52545277, + "parse_call_ns": 26282225, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6883.203763, + "output_byte_seconds": 1809.304759, + "parser_byte_seconds": 10115.296993, + "source_allocations": 367, + "output_allocations": 948, + "parser_allocations": 374, + "reuses": 6256, + "budget_waits": 0, + "slot_waits": 579, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83482902, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47583.467, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5081460.678, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8083520.998, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold8", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 11312, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 2031616, + "elapsed_ns": 134514858, + "cpu_ns": 50924568, + "parse_call_ns": 26180831, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 1059072, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 7475.627393, + "output_byte_seconds": 924.635553, + "parser_byte_seconds": 10091.334952, + "source_allocations": 237, + "output_allocations": 96, + "parser_allocations": 257, + "reuses": 5800, + "budget_waits": 0, + "slot_waits": 593, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 84806597, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 46212.018, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5080764.964, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8080296.584, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy_no_pool", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 842368, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1949696, + "elapsed_ns": 135580728, + "cpu_ns": 59055276, + "parse_call_ns": 27303180, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 8268.860241, + "output_byte_seconds": 1866.643736, + "parser_byte_seconds": 10141.702783, + "source_allocations": 3849, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 540, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 77683199, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 53763.312, + "latency_p99_upper_ns": 163840 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5090947.59, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8093934.847, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "shared_copy", + "repeat": 0, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134733970, + "cpu_ns": 52189389, + "parse_call_ns": 26273407, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6865.482399, + "output_byte_seconds": 1226.436074, + "parser_byte_seconds": 10135.432887, + "source_allocations": 365, + "output_allocations": 246, + "parser_allocations": 368, + "reuses": 5942, + "budget_waits": 0, + "slot_waits": 587, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83766349, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47383.279, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5081532.837, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082383.547, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold2", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134703694, + "cpu_ns": 51981424, + "parse_call_ns": 26239241, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6818.11346, + "output_byte_seconds": 1220.93584, + "parser_byte_seconds": 10109.238408, + "source_allocations": 368, + "output_allocations": 240, + "parser_allocations": 366, + "reuses": 5947, + "budget_waits": 0, + "slot_waits": 592, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83962412, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47186.16, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5082027.365, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082717.212, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "compact", + "repeat": 0, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2096880, + "source_peak_bytes": 2047296, + "output_peak_bytes": 3360, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3702784, + "elapsed_ns": 330325402, + "cpu_ns": 56526552, + "parse_call_ns": 257818693, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 542057.64214, + "output_byte_seconds": 342.456081, + "parser_byte_seconds": 10454.037412, + "source_allocations": 1653, + "output_allocations": 1018, + "parser_allocations": 587, + "reuses": 2639, + "budget_waits": 1256, + "slot_waits": 0, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 275134226, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 313916.181, + "latency_p99_upper_ns": 4718592 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5359649.404, + "latency_p99_upper_ns": 10485760 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 49, + "latency_mean_ns": 8345801.06, + "latency_p99_upper_ns": 12582912 + } + ] + }, + { + "case": "mixed_tight", + "variant": "borrow", + "repeat": 0, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2097024, + "source_peak_bytes": 2047296, + "output_peak_bytes": 0, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3702784, + "elapsed_ns": 517444947, + "cpu_ns": 58385263, + "parse_call_ns": 414175159, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 0, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 866552.611778, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 10646.478365, + "source_allocations": 1612, + "output_allocations": 0, + "parser_allocations": 580, + "reuses": 2681, + "budget_waits": 1250, + "slot_waits": 0, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 460552293, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 496739.613, + "latency_p99_upper_ns": 7864320 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5531099.266, + "latency_p99_upper_ns": 12582912 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 8547759.071, + "latency_p99_upper_ns": 15728640 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy_no_pool", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 842368, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1916928, + "elapsed_ns": 135818181, + "cpu_ns": 60130654, + "parse_call_ns": 27699522, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 8398.686166, + "output_byte_seconds": 1885.555543, + "parser_byte_seconds": 10206.603011, + "source_allocations": 3849, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 537, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 76838088, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 54782.86, + "latency_p99_upper_ns": 180224 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5093567.929, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8096201.349, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold8", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 11312, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 2064384, + "elapsed_ns": 134627181, + "cpu_ns": 51051158, + "parse_call_ns": 26242598, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 1059072, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 7499.787736, + "output_byte_seconds": 924.734083, + "parser_byte_seconds": 10090.493068, + "source_allocations": 237, + "output_allocations": 100, + "parser_allocations": 258, + "reuses": 5795, + "budget_waits": 0, + "slot_waits": 587, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 84733901, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 46365.068, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5080938.783, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8081319.231, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "shared_copy", + "repeat": 1, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1900544, + "elapsed_ns": 134660107, + "cpu_ns": 51849496, + "parse_call_ns": 26235438, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6785.330268, + "output_byte_seconds": 1222.453816, + "parser_byte_seconds": 10103.005533, + "source_allocations": 366, + "output_allocations": 239, + "parser_allocations": 366, + "reuses": 5950, + "budget_waits": 0, + "slot_waits": 579, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 84013028, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47133.761, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5081549.099, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082113.784, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "borrow", + "repeat": 1, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2097024, + "source_peak_bytes": 2047296, + "output_peak_bytes": 0, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3719168, + "elapsed_ns": 516876187, + "cpu_ns": 58189132, + "parse_call_ns": 413488109, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 0, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 866155.703795, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 10618.392921, + "source_allocations": 1617, + "output_allocations": 0, + "parser_allocations": 581, + "reuses": 2675, + "budget_waits": 1263, + "slot_waits": 0, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 460124695, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 496219.126, + "latency_p99_upper_ns": 7864320 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5528237.536, + "latency_p99_upper_ns": 12582912 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 8541844.312, + "latency_p99_upper_ns": 15728640 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold2", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1867776, + "elapsed_ns": 134596389, + "cpu_ns": 51879163, + "parse_call_ns": 26207832, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6795.293198, + "output_byte_seconds": 1224.224319, + "parser_byte_seconds": 10089.609534, + "source_allocations": 369, + "output_allocations": 233, + "parser_allocations": 365, + "reuses": 5954, + "budget_waits": 0, + "slot_waits": 596, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83947198, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47146.396, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5082305.345, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082431.98, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1104848, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134667407, + "cpu_ns": 52286009, + "parse_call_ns": 26255995, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6830.10163, + "output_byte_seconds": 1802.54536, + "parser_byte_seconds": 10104.51946, + "source_allocations": 370, + "output_allocations": 952, + "parser_allocations": 375, + "reuses": 6248, + "budget_waits": 0, + "slot_waits": 590, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83595691, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47383.187, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5081689.522, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082200.92, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "compact", + "repeat": 1, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2097104, + "source_peak_bytes": 2047296, + "output_peak_bytes": 3360, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3833856, + "elapsed_ns": 330507221, + "cpu_ns": 57227425, + "parse_call_ns": 258042238, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 542403.396989, + "output_byte_seconds": 342.198165, + "parser_byte_seconds": 10516.149038, + "source_allocations": 1657, + "output_allocations": 1018, + "parser_allocations": 592, + "reuses": 2630, + "budget_waits": 1262, + "slot_waits": 0, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 274671771, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 314160.033, + "latency_p99_upper_ns": 4718592 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5361935.781, + "latency_p99_upper_ns": 10485760 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 49, + "latency_mean_ns": 8345834.982, + "latency_p99_upper_ns": 12582912 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold2", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1900544, + "elapsed_ns": 134805557, + "cpu_ns": 52099602, + "parse_call_ns": 26201196, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6802.764732, + "output_byte_seconds": 1222.5673, + "parser_byte_seconds": 10082.699192, + "source_allocations": 363, + "output_allocations": 241, + "parser_allocations": 368, + "reuses": 5949, + "budget_waits": 0, + "slot_waits": 592, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83969655, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47229.56, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5083369.83, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082868.22, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1104736, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1900544, + "elapsed_ns": 135397301, + "cpu_ns": 55535720, + "parse_call_ns": 28191073, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 7296.683374, + "output_byte_seconds": 1825.786254, + "parser_byte_seconds": 10851.770961, + "source_allocations": 364, + "output_allocations": 925, + "parser_allocations": 376, + "reuses": 6280, + "budget_waits": 0, + "slot_waits": 556, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 81078620, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 50441.328, + "latency_p99_upper_ns": 163840 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5086253.071, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8088040.091, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold8", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 11312, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 2048000, + "elapsed_ns": 135143135, + "cpu_ns": 54181725, + "parse_call_ns": 28073539, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 1059072, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 7971.142415, + "output_byte_seconds": 924.89692, + "parser_byte_seconds": 10793.387694, + "source_allocations": 237, + "output_allocations": 89, + "parser_allocations": 259, + "reuses": 5805, + "budget_waits": 0, + "slot_waits": 587, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 82256234, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 49150.131, + "latency_p99_upper_ns": 163840 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5084346.661, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8083800.547, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "borrow", + "repeat": 2, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2097024, + "source_peak_bytes": 2047296, + "output_peak_bytes": 0, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3719168, + "elapsed_ns": 519737827, + "cpu_ns": 63401151, + "parse_call_ns": 410873515, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 0, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 869390.688093, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 10874.854354, + "source_allocations": 1612, + "output_allocations": 0, + "parser_allocations": 584, + "reuses": 2677, + "budget_waits": 1267, + "slot_waits": 0, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 457480540, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 498894.016, + "latency_p99_upper_ns": 7864320 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5554388.391, + "latency_p99_upper_ns": 12582912 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 8572436.932, + "latency_p99_upper_ns": 15728640 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy_no_pool", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 842368, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1933312, + "elapsed_ns": 135808674, + "cpu_ns": 61310764, + "parse_call_ns": 27409732, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 8492.13471, + "output_byte_seconds": 1909.35548, + "parser_byte_seconds": 10184.015437, + "source_allocations": 3849, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 538, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 75774039, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 55750.675, + "latency_p99_upper_ns": 196608 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5097561.83, + "latency_p99_upper_ns": 5767168 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8097147.871, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "compact", + "repeat": 2, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2096496, + "source_peak_bytes": 2047296, + "output_peak_bytes": 3360, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3719168, + "elapsed_ns": 330655613, + "cpu_ns": 57992572, + "parse_call_ns": 257126466, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 542286.835871, + "output_byte_seconds": 342.201335, + "parser_byte_seconds": 10592.82654, + "source_allocations": 1620, + "output_allocations": 1018, + "parser_allocations": 589, + "reuses": 2670, + "budget_waits": 1246, + "slot_waits": 0, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 274045502, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 314214.117, + "latency_p99_upper_ns": 4718592 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5361784.028, + "latency_p99_upper_ns": 10485760 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 49, + "latency_mean_ns": 8345733.162, + "latency_p99_upper_ns": 12582912 + } + ] + }, + { + "case": "mixed_tight", + "variant": "shared_copy", + "repeat": 2, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1900544, + "elapsed_ns": 134725602, + "cpu_ns": 52125932, + "parse_call_ns": 26256537, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6831.305534, + "output_byte_seconds": 1226.062002, + "parser_byte_seconds": 10092.239062, + "source_allocations": 365, + "output_allocations": 226, + "parser_allocations": 360, + "reuses": 5970, + "budget_waits": 0, + "slot_waits": 576, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83779201, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47423.973, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5082508.901, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082650.268, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "compact", + "repeat": 3, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2096656, + "source_peak_bytes": 2047296, + "output_peak_bytes": 3360, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3719168, + "elapsed_ns": 332809762, + "cpu_ns": 64506702, + "parse_call_ns": 255295188, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 544031.993873, + "output_byte_seconds": 348.13532, + "parser_byte_seconds": 10927.535411, + "source_allocations": 1623, + "output_allocations": 1008, + "parser_allocations": 583, + "reuses": 2683, + "budget_waits": 1227, + "slot_waits": 0, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 270135277, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 316210.616, + "latency_p99_upper_ns": 4718592 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5373501.093, + "latency_p99_upper_ns": 10485760 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 49, + "latency_mean_ns": 8409239.137, + "latency_p99_upper_ns": 12582912 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold2", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134646087, + "cpu_ns": 51969989, + "parse_call_ns": 26192074, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6812.54134, + "output_byte_seconds": 1229.637184, + "parser_byte_seconds": 10083.25152, + "source_allocations": 369, + "output_allocations": 241, + "parser_allocations": 365, + "reuses": 5946, + "budget_waits": 0, + "slot_waits": 582, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83864700, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47274.193, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5082291.09, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8084035.099, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1104848, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1900544, + "elapsed_ns": 134732754, + "cpu_ns": 52218674, + "parse_call_ns": 26241260, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6810.984162, + "output_byte_seconds": 1804.529206, + "parser_byte_seconds": 10104.502269, + "source_allocations": 368, + "output_allocations": 949, + "parser_allocations": 374, + "reuses": 6254, + "budget_waits": 0, + "slot_waits": 584, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 83719457, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47404.955, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5082343.96, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082308.803, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "shared_copy", + "repeat": 3, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134687032, + "cpu_ns": 52085095, + "parse_call_ns": 26215409, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6852.502663, + "output_byte_seconds": 1223.987439, + "parser_byte_seconds": 10100.060122, + "source_allocations": 365, + "output_allocations": 235, + "parser_allocations": 364, + "reuses": 5957, + "budget_waits": 0, + "slot_waits": 585, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83840818, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47354.551, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5081713.058, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082095.55, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "borrow", + "repeat": 3, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2097072, + "source_peak_bytes": 2047296, + "output_peak_bytes": 0, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3719168, + "elapsed_ns": 516415971, + "cpu_ns": 57490634, + "parse_call_ns": 412616864, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 0, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 865936.173669, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 10532.202462, + "source_allocations": 1624, + "output_allocations": 0, + "parser_allocations": 582, + "reuses": 2667, + "budget_waits": 1258, + "slot_waits": 0, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 460245677, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 495746.732, + "latency_p99_upper_ns": 7864320 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5527499.187, + "latency_p99_upper_ns": 12582912 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 8541909.699, + "latency_p99_upper_ns": 15728640 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy_no_pool", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 842368, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1916928, + "elapsed_ns": 135708499, + "cpu_ns": 59498427, + "parse_call_ns": 27293452, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 8320.069469, + "output_byte_seconds": 1853.590142, + "parser_byte_seconds": 10172.640074, + "source_allocations": 3849, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 550, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 77469437, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 53679.489, + "latency_p99_upper_ns": 180224 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5093041.554, + "latency_p99_upper_ns": 5767168 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8093789.758, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold8", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 11312, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 2048000, + "elapsed_ns": 134580129, + "cpu_ns": 50901202, + "parse_call_ns": 26138215, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 1059072, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 7468.893211, + "output_byte_seconds": 924.833553, + "parser_byte_seconds": 10074.589742, + "source_allocations": 237, + "output_allocations": 99, + "parser_allocations": 260, + "reuses": 5794, + "budget_waits": 0, + "slot_waits": 603, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 84894241, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 46199.245, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5080295.214, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082153.254, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "compact", + "repeat": 4, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2095936, + "source_peak_bytes": 2047296, + "output_peak_bytes": 3360, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3768320, + "elapsed_ns": 330364653, + "cpu_ns": 56737666, + "parse_call_ns": 257390641, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 57344, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 542129.496495, + "output_byte_seconds": 342.3922, + "parser_byte_seconds": 10487.947668, + "source_allocations": 1626, + "output_allocations": 1018, + "parser_allocations": 584, + "reuses": 2669, + "budget_waits": 1234, + "slot_waits": 0, + "compacted": 1024, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 274961005, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 314000.227, + "latency_p99_upper_ns": 4718592 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5359881.678, + "latency_p99_upper_ns": 10485760 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 49, + "latency_mean_ns": 8345470.393, + "latency_p99_upper_ns": 12582912 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold2", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134641407, + "cpu_ns": 51772899, + "parse_call_ns": 26303805, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6823.249522, + "output_byte_seconds": 1222.425123, + "parser_byte_seconds": 10156.044791, + "source_allocations": 363, + "output_allocations": 239, + "parser_allocations": 367, + "reuses": 5952, + "budget_waits": 0, + "slot_waits": 584, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83930889, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47208.702, + "latency_p99_upper_ns": 131072 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5081715.549, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8082551.032, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold8", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 11312, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 2048000, + "elapsed_ns": 134636889, + "cpu_ns": 51692325, + "parse_call_ns": 26232175, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 1059072, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 7604.090343, + "output_byte_seconds": 924.831397, + "parser_byte_seconds": 10109.160375, + "source_allocations": 237, + "output_allocations": 97, + "parser_allocations": 259, + "reuses": 5797, + "budget_waits": 0, + "slot_waits": 600, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 84203974, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 46850.104, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5081772.482, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8081990.295, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "borrow", + "repeat": 4, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 2097024, + "source_peak_bytes": 2047296, + "output_peak_bytes": 0, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 3719168, + "elapsed_ns": 516507633, + "cpu_ns": 56956626, + "parse_call_ns": 413462854, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 0, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 866066.604394, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 10498.181654, + "source_allocations": 1635, + "output_allocations": 0, + "parser_allocations": 588, + "reuses": 2650, + "budget_waits": 1258, + "slot_waits": 0, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 460890935, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 495819.875, + "latency_p99_upper_ns": 7864320 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 5527650.658, + "latency_p99_upper_ns": 12582912 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 30, + "latency_mean_ns": 8542682.03, + "latency_p99_upper_ns": 15728640 + } + ] + }, + { + "case": "mixed_tight", + "variant": "shared_copy", + "repeat": 4, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1097792, + "source_peak_bytes": 393312, + "output_peak_bytes": 72752, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 134698503, + "cpu_ns": 52062419, + "parse_call_ns": 26232455, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26814720, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6807.634601, + "output_byte_seconds": 1226.698884, + "parser_byte_seconds": 10096.010075, + "source_allocations": 358, + "output_allocations": 229, + "parser_allocations": 363, + "reuses": 5971, + "budget_waits": 0, + "slot_waits": 573, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 1024, + "sleep_ns": 83820760, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 47338.287, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5082803.919, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8081430.476, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy_no_pool", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 842368, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1933312, + "elapsed_ns": 135617374, + "cpu_ns": 59370307, + "parse_call_ns": 27370335, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 8306.118105, + "output_byte_seconds": 1881.770411, + "parser_byte_seconds": 10193.350967, + "source_allocations": 3849, + "output_allocations": 3072, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 541, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 77447978, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 54118.737, + "latency_p99_upper_ns": 180224 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5093193.132, + "latency_p99_upper_ns": 5767168 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8094306.969, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1104736, + "source_peak_bytes": 393312, + "output_peak_bytes": 79920, + "parser_peak_bytes": 524336, + "rss_peak_bytes": 1884160, + "elapsed_ns": 135101765, + "cpu_ns": 53612089, + "parse_call_ns": 26369173, + "accepted": 1024, + "emitted": 3072, + "input_bytes": 49877076, + "selection_copy_bytes": 26872064, + "reader_copy_bytes": 114814580, + "parser_realloc_copy_bytes": 0, + "source_byte_seconds": 6944.794448, + "output_byte_seconds": 1822.831558, + "parser_byte_seconds": 10130.150831, + "source_allocations": 348, + "output_allocations": 886, + "parser_allocations": 368, + "reuses": 6343, + "budget_waits": 0, + "slot_waits": 561, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 82695691, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 1024, + "bytes": 26757376, + "peak_count": 1, + "latency_mean_ns": 48573.396, + "latency_p99_upper_ns": 147456 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 5083912.79, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 1024, + "bytes": 57344, + "peak_count": 64, + "latency_mean_ns": 8083831.809, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy_no_pool", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 170672, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66381179, + "cpu_ns": 14937944, + "parse_call_ns": 10634360, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 191.835816, + "output_byte_seconds": 748.822762, + "parser_byte_seconds": 436.544508, + "source_allocations": 1273, + "output_allocations": 1536, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 200, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51858352, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26502.654, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055591.338, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8038187.436, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "borrow", + "repeat": 0, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1208192, + "source_peak_bytes": 1059888, + "output_peak_bytes": 0, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2785280, + "elapsed_ns": 66395496, + "cpu_ns": 14949295, + "parse_call_ns": 10748920, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 0, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 67632.156584, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 441.340054, + "source_allocations": 70, + "output_allocations": 0, + "parser_allocations": 2, + "reuses": 2225, + "budget_waits": 0, + "slot_waits": 210, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51867983, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26677.738, + "latency_p99_upper_ns": 40960 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055635.91, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8040456.355, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold8", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1851392, + "elapsed_ns": 66344311, + "cpu_ns": 14626628, + "parse_call_ns": 10598197, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.993439, + "output_byte_seconds": 460.282359, + "parser_byte_seconds": 437.832685, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 201, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52108022, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26057.676, + "latency_p99_upper_ns": 30720 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054707.516, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8038718.686, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "shared_copy", + "repeat": 0, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66360126, + "cpu_ns": 14610198, + "parse_call_ns": 10558947, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.278595, + "output_byte_seconds": 460.358957, + "parser_byte_seconds": 433.475216, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 210, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52155647, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26056.072, + "latency_p99_upper_ns": 30720 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054720.746, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8039527.611, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "compact", + "repeat": 0, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1209200, + "source_peak_bytes": 1059888, + "output_peak_bytes": 7168, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2899968, + "elapsed_ns": 66352515, + "cpu_ns": 15174956, + "parse_call_ns": 10767457, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 28672, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 42522.512719, + "output_byte_seconds": 170.992644, + "parser_byte_seconds": 444.338525, + "source_allocations": 462, + "output_allocations": 456, + "parser_allocations": 2, + "reuses": 1889, + "budget_waits": 0, + "slot_waits": 203, + "compacted": 512, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51584350, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26833.023, + "latency_p99_upper_ns": 40960 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055824.906, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8037818.045, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy", + "repeat": 0, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 187312, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66358774, + "cpu_ns": 14704746, + "parse_call_ns": 10580140, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 190.401936, + "output_byte_seconds": 748.797361, + "parser_byte_seconds": 434.190092, + "source_allocations": 6, + "output_allocations": 546, + "parser_allocations": 2, + "reuses": 3279, + "budget_waits": 0, + "slot_waits": 198, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 52055904, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26138.373, + "latency_p99_upper_ns": 32768 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054837.756, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8038347.354, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold2", + "repeat": 0, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1851392, + "elapsed_ns": 66343256, + "cpu_ns": 14610428, + "parse_call_ns": 10601249, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 190.005404, + "output_byte_seconds": 460.293238, + "parser_byte_seconds": 435.304664, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 199, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52113330, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26103.785, + "latency_p99_upper_ns": 32768 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054791.455, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8038668.434, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold8", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66620533, + "cpu_ns": 15009658, + "parse_call_ns": 10599246, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 190.711835, + "output_byte_seconds": 461.162485, + "parser_byte_seconds": 439.194778, + "source_allocations": 6, + "output_allocations": 67, + "parser_allocations": 2, + "reuses": 3246, + "budget_waits": 0, + "slot_waits": 195, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52089340, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26514.744, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5059599.168, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8052646.494, + "latency_p99_upper_ns": 9437184 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy_no_pool", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 170672, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 69246534, + "cpu_ns": 16610834, + "parse_call_ns": 11758115, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 215.365911, + "output_byte_seconds": 778.347747, + "parser_byte_seconds": 481.501477, + "source_allocations": 1273, + "output_allocations": 1536, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 164, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 52395416, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 29861.328, + "latency_p99_upper_ns": 57344 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5481519.523, + "latency_p99_upper_ns": 10485760 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8132478.682, + "latency_p99_upper_ns": 10485760 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "shared_copy", + "repeat": 1, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 67338922, + "cpu_ns": 15543047, + "parse_call_ns": 10965512, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 198.833427, + "output_byte_seconds": 461.43294, + "parser_byte_seconds": 455.786835, + "source_allocations": 6, + "output_allocations": 88, + "parser_allocations": 2, + "reuses": 3225, + "budget_waits": 0, + "slot_waits": 170, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52096250, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 27620.291, + "latency_p99_upper_ns": 53248 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5405312.287, + "latency_p99_upper_ns": 9437184 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8058747.33, + "latency_p99_upper_ns": 9437184 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy", + "repeat": 1, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 187312, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66437867, + "cpu_ns": 14887131, + "parse_call_ns": 10719986, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 192.97609, + "output_byte_seconds": 748.927344, + "parser_byte_seconds": 442.835871, + "source_allocations": 6, + "output_allocations": 546, + "parser_allocations": 2, + "reuses": 3279, + "budget_waits": 0, + "slot_waits": 206, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51911693, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26502.299, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055229.1, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8040517.863, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold2", + "repeat": 1, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66341459, + "cpu_ns": 14573003, + "parse_call_ns": 10555283, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.19959, + "output_byte_seconds": 460.262935, + "parser_byte_seconds": 433.563583, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 202, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52150322, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26047.523, + "latency_p99_upper_ns": 30720 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054844.742, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8037890.609, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "borrow", + "repeat": 1, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1208192, + "source_peak_bytes": 1059888, + "output_peak_bytes": 0, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2801664, + "elapsed_ns": 66302663, + "cpu_ns": 14680433, + "parse_call_ns": 10680579, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 0, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 67603.216745, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 441.482243, + "source_allocations": 70, + "output_allocations": 0, + "parser_allocations": 2, + "reuses": 2225, + "budget_waits": 0, + "slot_waits": 203, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 52022749, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26241.209, + "latency_p99_upper_ns": 32768 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054887.895, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8036938.547, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "compact", + "repeat": 1, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1209200, + "source_peak_bytes": 1059888, + "output_peak_bytes": 7168, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2916352, + "elapsed_ns": 66647996, + "cpu_ns": 15645769, + "parse_call_ns": 10897093, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 28672, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 42531.184104, + "output_byte_seconds": 171.185155, + "parser_byte_seconds": 448.382215, + "source_allocations": 462, + "output_allocations": 456, + "parser_allocations": 2, + "reuses": 1889, + "budget_waits": 0, + "slot_waits": 208, + "compacted": 512, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51484721, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 27363.605, + "latency_p99_upper_ns": 49152 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5056927.043, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8042303.346, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "shared_copy", + "repeat": 2, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66509552, + "cpu_ns": 15439851, + "parse_call_ns": 10723161, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 194.692414, + "output_byte_seconds": 460.401472, + "parser_byte_seconds": 443.814792, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 188, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 51559999, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 27221.904, + "latency_p99_upper_ns": 40960 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5102159.828, + "latency_p99_upper_ns": 6291456 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8039936.652, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold2", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1851392, + "elapsed_ns": 66675868, + "cpu_ns": 15617313, + "parse_call_ns": 10794500, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 196.368374, + "output_byte_seconds": 460.582208, + "parser_byte_seconds": 453.168708, + "source_allocations": 6, + "output_allocations": 72, + "parser_allocations": 2, + "reuses": 3241, + "budget_waits": 0, + "slot_waits": 206, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 51576007, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 27391.873, + "latency_p99_upper_ns": 45056 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5063401.098, + "latency_p99_upper_ns": 5767168 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8043009.266, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold8", + "repeat": 2, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66346091, + "cpu_ns": 14749114, + "parse_call_ns": 10663838, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 191.566293, + "output_byte_seconds": 460.387978, + "parser_byte_seconds": 441.272197, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 198, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 51956135, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26444.111, + "latency_p99_upper_ns": 40960 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055009.982, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8040053.549, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "borrow", + "repeat": 2, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1208192, + "source_peak_bytes": 1059888, + "output_peak_bytes": 0, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2768896, + "elapsed_ns": 66340906, + "cpu_ns": 14840713, + "parse_call_ns": 10710339, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 0, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 67628.848076, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 441.911742, + "source_allocations": 70, + "output_allocations": 0, + "parser_allocations": 2, + "reuses": 2225, + "budget_waits": 0, + "slot_waits": 214, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51926596, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26533.881, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055556.934, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8040046.408, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy_no_pool", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 170672, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1851392, + "elapsed_ns": 66367572, + "cpu_ns": 14844612, + "parse_call_ns": 10626817, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 191.787957, + "output_byte_seconds": 748.742987, + "parser_byte_seconds": 439.039763, + "source_allocations": 1273, + "output_allocations": 1536, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 202, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51918865, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26443.496, + "latency_p99_upper_ns": 32768 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055503.67, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8037164.848, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "compact", + "repeat": 2, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1209200, + "source_peak_bytes": 1059888, + "output_peak_bytes": 7168, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2916352, + "elapsed_ns": 66456221, + "cpu_ns": 15237761, + "parse_call_ns": 10832907, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 28672, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 42524.965388, + "output_byte_seconds": 171.106633, + "parser_byte_seconds": 446.50427, + "source_allocations": 462, + "output_allocations": 456, + "parser_allocations": 2, + "reuses": 1889, + "budget_waits": 0, + "slot_waits": 211, + "compacted": 512, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51628844, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26894.043, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5056115.033, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8040094.787, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy", + "repeat": 2, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 187312, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1818624, + "elapsed_ns": 66358054, + "cpu_ns": 14786258, + "parse_call_ns": 10588699, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 190.767691, + "output_byte_seconds": 748.949984, + "parser_byte_seconds": 437.20655, + "source_allocations": 6, + "output_allocations": 546, + "parser_allocations": 2, + "reuses": 3279, + "budget_waits": 0, + "slot_waits": 202, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51982953, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26290.178, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055243.682, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8040476.592, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "shared_copy", + "repeat": 3, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66340017, + "cpu_ns": 14641398, + "parse_call_ns": 10583202, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.850967, + "output_byte_seconds": 460.201756, + "parser_byte_seconds": 434.46664, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 200, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52101474, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26126.43, + "latency_p99_upper_ns": 32768 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054896.006, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8037126.236, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "borrow", + "repeat": 3, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1208192, + "source_peak_bytes": 1059888, + "output_peak_bytes": 0, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2768896, + "elapsed_ns": 66397387, + "cpu_ns": 14926280, + "parse_call_ns": 10743184, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 0, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 67622.063537, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 442.942199, + "source_allocations": 70, + "output_allocations": 0, + "parser_allocations": 2, + "reuses": 2225, + "budget_waits": 0, + "slot_waits": 212, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51935519, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26540.598, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055706.689, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8039215.434, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 187312, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66347591, + "cpu_ns": 14688841, + "parse_call_ns": 10558757, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 190.237303, + "output_byte_seconds": 748.906445, + "parser_byte_seconds": 434.052077, + "source_allocations": 6, + "output_allocations": 546, + "parser_allocations": 2, + "reuses": 3279, + "budget_waits": 0, + "slot_waits": 201, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 52048363, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26185.322, + "latency_p99_upper_ns": 30720 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054934.725, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8039957.693, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "compact", + "repeat": 3, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1209200, + "source_peak_bytes": 1059888, + "output_peak_bytes": 7168, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2916352, + "elapsed_ns": 66425702, + "cpu_ns": 15126826, + "parse_call_ns": 10762572, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 28672, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 42518.234312, + "output_byte_seconds": 171.124375, + "parser_byte_seconds": 442.813559, + "source_allocations": 462, + "output_allocations": 456, + "parser_allocations": 2, + "reuses": 1889, + "budget_waits": 0, + "slot_waits": 207, + "compacted": 512, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51722109, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26699.592, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055311.875, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8039588.77, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold8", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66342183, + "cpu_ns": 14624958, + "parse_call_ns": 10558072, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.596919, + "output_byte_seconds": 460.250779, + "parser_byte_seconds": 436.103934, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 199, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52113950, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26082.711, + "latency_p99_upper_ns": 32768 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054439.891, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8037911.916, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy_no_pool", + "repeat": 3, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 170672, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66421906, + "cpu_ns": 14924036, + "parse_call_ns": 10733514, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 193.388376, + "output_byte_seconds": 748.846289, + "parser_byte_seconds": 443.059571, + "source_allocations": 1273, + "output_allocations": 1536, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 207, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51871512, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26630.812, + "latency_p99_upper_ns": 40960 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055263.076, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8039399.533, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold2", + "repeat": 3, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66363184, + "cpu_ns": 14629240, + "parse_call_ns": 10568715, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.501318, + "output_byte_seconds": 460.195707, + "parser_byte_seconds": 436.121559, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 199, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52142767, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26129.004, + "latency_p99_upper_ns": 30720 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054684.932, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8036606.693, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold2", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66354498, + "cpu_ns": 14608671, + "parse_call_ns": 10552983, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.286265, + "output_byte_seconds": 460.245208, + "parser_byte_seconds": 433.358809, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 197, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52143565, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26044.074, + "latency_p99_upper_ns": 30720 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054765.459, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8037717.471, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy_no_pool", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 170672, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1851392, + "elapsed_ns": 66440315, + "cpu_ns": 15042208, + "parse_call_ns": 10714630, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 193.34471, + "output_byte_seconds": 749.072578, + "parser_byte_seconds": 442.776453, + "source_allocations": 1273, + "output_allocations": 1536, + "parser_allocations": 1024, + "reuses": 0, + "budget_waits": 0, + "slot_waits": 203, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51818059, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26719.596, + "latency_p99_upper_ns": 40960 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055539.021, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8042928.75, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "shared_copy", + "repeat": 4, + "status": "ok", + "policy": "shared_copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66550686, + "cpu_ns": 14565611, + "parse_call_ns": 10569760, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 189.196651, + "output_byte_seconds": 460.427963, + "parser_byte_seconds": 435.936714, + "source_allocations": 6, + "output_allocations": 70, + "parser_allocations": 2, + "reuses": 3243, + "budget_waits": 0, + "slot_waits": 199, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52394259, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26054.17, + "latency_p99_upper_ns": 30720 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054468.873, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8040821.889, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "compact", + "repeat": 4, + "status": "ok", + "policy": "compact", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1209200, + "source_peak_bytes": 1059888, + "output_peak_bytes": 7168, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2932736, + "elapsed_ns": 66346982, + "cpu_ns": 15115580, + "parse_call_ns": 10730381, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 28672, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 42539.689103, + "output_byte_seconds": 170.963142, + "parser_byte_seconds": 440.137993, + "source_allocations": 462, + "output_allocations": 456, + "parser_allocations": 2, + "reuses": 1889, + "budget_waits": 0, + "slot_waits": 200, + "compacted": 512, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51642878, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26772.898, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5057876.094, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8039346.365, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold8", + "repeat": 4, + "status": "ok", + "policy": "threshold", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 180144, + "source_peak_bytes": 24672, + "output_peak_bytes": 7728, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1851392, + "elapsed_ns": 66367685, + "cpu_ns": 14678765, + "parse_call_ns": 10612300, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 290816, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 190.199351, + "output_byte_seconds": 460.290364, + "parser_byte_seconds": 438.024028, + "source_allocations": 6, + "output_allocations": 65, + "parser_allocations": 2, + "reuses": 3248, + "budget_waits": 0, + "slot_waits": 200, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 512, + "sleep_ns": 52099788, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26178.92, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054797.43, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8038471.338, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "borrow", + "repeat": 4, + "status": "ok", + "policy": "borrow", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 1208192, + "source_peak_bytes": 1059888, + "output_peak_bytes": 0, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 2785280, + "elapsed_ns": 66395630, + "cpu_ns": 14769876, + "parse_call_ns": 10679603, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 0, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 67615.986637, + "output_byte_seconds": 0.0, + "parser_byte_seconds": 438.40001, + "source_allocations": 70, + "output_allocations": 0, + "parser_allocations": 2, + "reuses": 2225, + "budget_waits": 0, + "slot_waits": 207, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 52035492, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26350.551, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5054965.338, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8038469.066, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy", + "repeat": 4, + "status": "ok", + "policy": "copy", + "parser_version": "0.12.0", + "budget_bytes": 2097152, + "fixed_bytes": 41728, + "peak_reserved_bytes": 187312, + "source_peak_bytes": 24672, + "output_peak_bytes": 14896, + "parser_peak_bytes": 98400, + "rss_peak_bytes": 1835008, + "elapsed_ns": 66338315, + "cpu_ns": 14754424, + "parse_call_ns": 10609310, + "accepted": 512, + "emitted": 1536, + "input_bytes": 5259666, + "selection_copy_bytes": 319488, + "reader_copy_bytes": 6312628, + "parser_realloc_copy_bytes": 14086144, + "source_byte_seconds": 190.968242, + "output_byte_seconds": 748.772024, + "parser_byte_seconds": 435.574111, + "source_allocations": 6, + "output_allocations": 546, + "parser_allocations": 2, + "reuses": 3279, + "budget_waits": 0, + "slot_waits": 199, + "compacted": 0, + "compact_skipped": 0, + "dedup_hits": 0, + "sleep_ns": 51978025, + "live_at_stop_bytes": 0, + "unreleased_buffer_bytes": 0, + "queues": [ + { + "emitted": 512, + "bytes": 262144, + "peak_count": 1, + "latency_mean_ns": 26285.051, + "latency_p99_upper_ns": 36864 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 5055200.059, + "latency_p99_upper_ns": 5242880 + }, + { + "emitted": 512, + "bytes": 28672, + "peak_count": 64, + "latency_mean_ns": 8037468.172, + "latency_p99_upper_ns": 8388608 + } + ] + } +] diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/benchmark.c b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/benchmark.c new file mode 100644 index 0000000..474bc7f --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/benchmark.c @@ -0,0 +1,391 @@ +#define _POSIX_C_SOURCE 200809L +#include <assert.h> +#include <errno.h> +#include <inttypes.h> +#include <signal.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include "vendor/yyjson/yyjson.h" + +enum { BRANCHES=3, CACHE=16, READ_SIZE=16384, MAX_JSON=1024*1024, HIST=512 }; +enum { SOURCE, OUTPUT, PARSER, KINDS }; +typedef struct Item Item; +typedef struct Block { + size_t capacity, refs; + uint64_t since; + unsigned kind; + Item *views; + _Alignas(max_align_t) unsigned char data[]; +} Block; +struct Item { + Block *block; + Item *prev, *next; + size_t offset, length; + uint64_t sequence, start, ready; +}; +typedef struct Queue { + Item *items; + size_t head, count, peak; + const char *key; + uint64_t hold, service, last, emitted, bytes, latency_sum, histogram[HIST]; +} Queue; +typedef struct Engine { + Queue q[BRANCHES]; + Block *cache[CACHE]; + unsigned char read_buffer[READ_SIZE]; + size_t read_at, read_end; + size_t budget, fixed, slots, fanout, reserved, active, cached, peak; + size_t by_kind[KINDS], peak_kind[KINDS]; + long double byte_ns[KINDS]; + uint64_t allocations[KINDS], reuses, accepted, emitted, input_bytes; + uint64_t selection_copies, reader_copies, parser_copies, parse_ns; + uint64_t budget_waits, slot_waits, compacted, compact_skipped, dedup_hits; + uint64_t sleep_ns, elapsed_ns, cpu_ns, cancel_after; + unsigned ratio; + int pool; + const char *policy, *status; +} Engine; +static volatile sig_atomic_t stopped; +static void stop(int sig) { (void)sig; stopped=1; } +static uint64_t ns(clockid_t clock) { + struct timespec t; + if (clock_gettime(clock,&t)) abort(); + return (uint64_t)t.tv_sec*UINT64_C(1000000000)+(uint64_t)t.tv_nsec; +} +static size_t rounded(size_t n) { size_t c=64; while(c<n)c*=2; return c; } +static size_t charge(const Block *b) { return sizeof(*b)+b->capacity; } +static void check(Engine *e) { + assert(e->active+e->cached==e->reserved); + assert(e->by_kind[0]+e->by_kind[1]+e->by_kind[2]==e->active); + assert(e->fixed+e->reserved<=e->budget); + if(e->reserved>e->peak)e->peak=e->reserved; + for(unsigned k=0;k<KINDS;++k)if(e->by_kind[k]>e->peak_kind[k])e->peak_kind[k]=e->by_kind[k]; +} +static void evict(Engine *e,size_t i) { + Block *b=e->cache[i]; if(!b)return; + assert(!b->refs && !b->views); + e->reserved-=charge(b); e->cached-=charge(b); free(b); e->cache[i]=NULL; +} +static Block *take(Engine *e,size_t n,unsigned kind) { + size_t capacity=rounded(n),need=sizeof(Block)+capacity; + Block *b=NULL; + if(e->pool)for(size_t i=0;i<CACHE;++i)if(e->cache[i] && e->cache[i]->capacity==capacity) { + b=e->cache[i]; e->cache[i]=NULL; e->cached-=need; ++e->reuses; break; + } + if(!b) { + for(size_t i=0;i<CACHE && need>e->budget-e->fixed-e->reserved;++i)evict(e,i); + if(need>e->budget-e->fixed-e->reserved)return NULL; + b=malloc(need); + if(!b){e->status="allocation_failed";return NULL;} + b->capacity=capacity; e->reserved+=need; ++e->allocations[kind]; + } + b->kind=kind;b->refs=1;b->views=NULL;b->since=ns(CLOCK_MONOTONIC); + e->active+=need;e->by_kind[kind]+=need;check(e);return b; +} +static void drop(Engine *e,Block *b) { + if(!b)return; + assert(b->refs); + if(--b->refs)return; + assert(!b->views); + size_t bytes=charge(b); + e->byte_ns[b->kind]+=(long double)bytes*(ns(CLOCK_MONOTONIC)-b->since); + e->active-=bytes;e->by_kind[b->kind]-=bytes; + if(e->pool && bytes<=e->budget/4-e->cached)for(size_t i=0;i<CACHE;++i)if(!e->cache[i]) { + e->cache[i]=b;e->cached+=bytes;check(e);return; + } + e->reserved-=bytes;free(b);check(e); +} +static void attach(Item *item) { + Block *b=item->block; + item->prev=NULL;item->next=b->views; + if(b->views)b->views->prev=item; + b->views=item; +} +static void detach(Item *item) { + if(item->prev)item->prev->next=item->next;else item->block->views=item->next; + if(item->next)item->next->prev=item->prev; + item->prev=item->next=NULL; +} +static void compact(Engine *e,Block *source) { + if(strcmp(e->policy,"compact") || source->kind!=SOURCE || source->refs!=1 || !source->views)return; + Item *item=source->views; + assert(!item->next); + if(source->capacity/rounded(item->length)<e->ratio)return; + /* Opportunistic only: never block or recurse from a completion callback. */ + Block *small=take(e,item->length,OUTPUT); + if(!small){++e->compact_skipped;return;} + memcpy(small->data,source->data+item->offset,item->length); + e->selection_copies+=item->length;++e->compacted; + detach(item);item->block=small;item->offset=0;attach(item);drop(e,source); +} +static uint64_t deadline(Queue *q) { + uint64_t ready=q->items[q->head].ready; + return ready>q->last+q->service?ready:q->last+q->service; +} +static int next_queue(Engine *e) { + int selected=-1;uint64_t soon=UINT64_MAX; + for(size_t k=0;k<e->fanout;++k)if(e->q[k].count && deadline(&e->q[k])<soon) { + selected=(int)k;soon=deadline(&e->q[k]); + } + return selected; +} +static int sleep_to(Engine *e,uint64_t until) { + uint64_t start=ns(CLOCK_MONOTONIC),now=start; + while(now<until && !stopped) { + uint64_t left=until-now; + struct timespec t={(time_t)(left/1000000000),(long)(left%1000000000)}; + if(nanosleep(&t,NULL) && errno!=EINTR){e->status="sleep_failed";return 0;} + now=ns(CLOCK_MONOTONIC); + } + e->sleep_ns+=now-start;return !stopped; +} +static unsigned bucket(uint64_t n) { + unsigned k=0;for(uint64_t x=n;x>1;x>>=1)++k; + return k<3?(unsigned)n:k*8+(unsigned)((n-(UINT64_C(1)<<k))>>(k-3)); +} +static uint64_t upper(unsigned b) { + unsigned k=b/8; + if(b<24)return b+1; + if(k>=63)return UINT64_MAX; + return (UINT64_C(1)<<k)+(uint64_t)(b%8+1)*(UINT64_C(1)<<(k-3)); +} +static int write_all(Engine *e,const void *data,size_t n) { + const unsigned char *p=data; + while(n && !stopped) { + ssize_t w=write(STDOUT_FILENO,p,n); + if(w<0 && errno==EINTR)continue; + if(w<=0){e->status="output_failed";return 0;} + p+=w;n-=(size_t)w; + } + return !stopped; +} +static int emit(Engine *e) { + int k=next_queue(e);assert(k>=0); + Queue *q=&e->q[k];Item *item=&q->items[q->head]; + if(!sleep_to(e,deadline(q)))return 0; + unsigned char header[16]; + for(unsigned i=0;i<4;++i){header[i]=(unsigned char)((unsigned)k>>(8*i));header[4+i]=(unsigned char)(item->length>>(8*i));} + for(unsigned i=0;i<8;++i)header[8+i]=(unsigned char)(item->sequence>>(8*i)); + if(!write_all(e,header,16) || !write_all(e,item->block->data+item->offset,item->length))return 0; + uint64_t now=ns(CLOCK_MONOTONIC),latency=now-item->start; + q->last=now;q->latency_sum+=latency;++q->histogram[bucket(latency)]; + ++q->emitted;q->bytes+=item->length;++e->emitted; + Block *b=item->block;size_t old_refs=b->refs; + detach(item);drop(e,b);item->block=NULL; + q->head=(q->head+1)%e->slots;--q->count; + if(old_refs>1)compact(e,b); + if(e->cancel_after && e->emitted>=e->cancel_after){e->status="cancelled";return 0;} + return !strcmp(e->status,"ok"); +} +static Block *acquire(Engine *e,size_t n,unsigned kind) { + for(;;) { + Block *b=take(e,n,kind);if(b)return b; + if(strcmp(e->status,"ok"))return NULL; + if(next_queue(e)<0){e->status="budget_too_small";return NULL;} + ++e->budget_waits;if(!emit(e))return NULL; + } +} +static void *parser_alloc(void *ctx,size_t n) { + Block *b=acquire(ctx,n,PARSER);return b?b->data:NULL; +} +static Block *from_data(void *p) {return (Block *)((unsigned char *)p-sizeof(Block));} +static void parser_free(void *ctx,void *p) {if(p)drop(ctx,from_data(p));} +static void *parser_realloc(void *ctx,void *p,size_t old,size_t n) { + if(!p)return parser_alloc(ctx,n); + Block *b=from_data(p);assert(b->refs==1 && b->kind==PARSER && old<=b->capacity); + if(n<=b->capacity)return p; + void *fresh=parser_alloc(ctx,n);if(!fresh)return NULL; + memcpy(fresh,p,old);((Engine *)ctx)->parser_copies+=old;parser_free(ctx,p);return fresh; +} +/* Bounded NDJSON reader, accepting a final complete JSON without a newline. */ +static Block *read_record(Engine *e,size_t *length) { + Block *b=acquire(e,256,SOURCE);if(!b)return NULL; + *length=0; + for(;;) { + if(stopped){drop(e,b);return NULL;} + if(e->read_at==e->read_end) { + ssize_t got=read(STDIN_FILENO,e->read_buffer,READ_SIZE); + if(got<0 && errno==EINTR)continue; + if(got<0){e->status="input_failed";drop(e,b);return NULL;} + if(!got){if(*length)break;drop(e,b);return NULL;} + e->read_at=0;e->read_end=(size_t)got;e->input_bytes+=(uint64_t)got; + } + unsigned char *p=e->read_buffer+e->read_at; + size_t available=e->read_end-e->read_at; + unsigned char *newline=memchr(p,'\n',available); + size_t amount=newline?(size_t)(newline-p):available; + if(amount>MAX_JSON-*length){e->status="record_too_large";drop(e,b);return NULL;} + size_t need=*length+amount+YYJSON_PADDING_SIZE; + if(need>b->capacity) { + Block *larger=acquire(e,need,SOURCE); + if(!larger){drop(e,b);return NULL;} + memcpy(larger->data,b->data,*length);e->reader_copies+=*length; + drop(e,b);b=larger; + } + memcpy(b->data+*length,p,amount);e->reader_copies+=amount; + *length+=amount;e->read_at+=amount+(newline?1:0); + if(newline)break; + } + memset(b->data+*length,0,YYJSON_PADDING_SIZE);return b; +} +static int submit(Engine *e,Block *source,size_t length,uint64_t start) { + size_t offsets[BRANCHES]={0},lengths[BRANCHES]={0}; + yyjson_alc alc={parser_alloc,parser_realloc,parser_free,e}; + yyjson_read_err error; + uint64_t before=ns(CLOCK_MONOTONIC); + yyjson_doc *doc=yyjson_read_opts((char *)source->data,length,YYJSON_READ_INSITU,&alc,&error); + e->parse_ns+=ns(CLOCK_MONOTONIC)-before; + if(!doc){if(!strcmp(e->status,"ok"))e->status="invalid_json";drop(e,source);return 0;} + yyjson_val *root=yyjson_doc_get_root(doc); + int valid=yyjson_is_obj(root); + for(size_t k=0;valid && k<e->fanout;++k) { + yyjson_val *value=yyjson_obj_get(root,e->q[k].key); + const char *str=yyjson_get_str(value);size_t size=yyjson_get_len(value); + uintptr_t address=(uintptr_t)str,base=(uintptr_t)source->data; + if(!yyjson_is_str(value) || address<base || address-base>length || size>length-(address-base))valid=0; + else{offsets[k]=(size_t)(address-base);lengths[k]=size;} + } + /* Only decoded string offsets/lengths survive. No DOM nodes escape. */ + yyjson_doc_free(doc); + if(!valid){e->status="invalid_schema";drop(e,source);return 0;} + Item pending[BRANCHES]={{0}}; + for(size_t k=0;k<e->fanout;++k) { + int copy=!strcmp(e->policy,"copy") || !strcmp(e->policy,"shared_copy") || + (!strcmp(e->policy,"threshold") && source->capacity/rounded(lengths[k])>=e->ratio); + int reuse=copy && (!strcmp(e->policy,"shared_copy") || !strcmp(e->policy,"threshold")); + Block *b=NULL;size_t offset=offsets[k]; + if(reuse)for(size_t j=0;j<k;++j)if(offsets[j]==offsets[k] && lengths[j]==lengths[k] && pending[j].block!=source) { + b=pending[j].block;++b->refs;offset=0;++e->dedup_hits;break; + } + if(!b && copy) { + b=acquire(e,lengths[k],OUTPUT); + if(!b) { + for(size_t j=0;j<k;++j)drop(e,pending[j].block); + drop(e,source);return 0; + } + memcpy(b->data,source->data+offset,lengths[k]);e->selection_copies+=lengths[k];offset=0; + } else if(!b){b=source;++b->refs;} + pending[k]=(Item){.block=b,.offset=offset,.length=lengths[k],.sequence=e->accepted,.start=start}; + } + uint64_t now=ns(CLOCK_MONOTONIC); + for(size_t k=0;k<e->fanout;++k) { + Queue *q=&e->q[k];assert(q->count<e->slots); + Item *item=&q->items[(q->head+q->count)%e->slots]; + *item=pending[k];item->ready=now+q->hold;attach(item);++q->count; + if(q->count>q->peak)q->peak=q->count; + } + ++e->accepted;drop(e,source);return 1; +} +static void run(Engine *e) { + while(!stopped && !strcmp(e->status,"ok")) { + int k=next_queue(e); + if(k>=0 && deadline(&e->q[k])<=ns(CLOCK_MONOTONIC)){if(!emit(e))break;continue;} + int full=0;for(size_t j=0;j<e->fanout;++j)if(e->q[j].count==e->slots)full=1; + if(full){++e->slot_waits;if(!emit(e))break;continue;} + size_t length;uint64_t start=ns(CLOCK_MONOTONIC); + Block *source=read_record(e,&length); + if(!source) { + while(!stopped && !strcmp(e->status,"ok") && next_queue(e)>=0)if(!emit(e))break; + break; + } + if(!submit(e,source,length,start))break; + } + if(stopped)e->status="cancelled"; +} +static uint64_t number(const char *s,uint64_t max) { + char *end;errno=0;unsigned long long n=strtoull(s,&end,10); + if(!*s || *s=='-' || *end || errno || n>max){fprintf(stderr,"invalid number\n");exit(2);} + return n; +} +static uint64_t rss(void) { +#if defined(__linux__) + FILE *f=fopen("/proc/self/status","r");if(!f)return 0; + char line[256];unsigned long long kib=0; + while(fgets(line,sizeof(line),f))if(sscanf(line,"VmHWM: %llu kB",&kib)==1)break; + fclose(f);return (uint64_t)kib*1024; +#else + return 0; +#endif +} +int main(int argc,char **argv) { + _Static_assert(sizeof(Block)==offsetof(Block,data),"aligned block header"); + Engine storage={0},*e=&storage; + e->policy="borrow";e->status="ok";e->slots=64;e->fanout=3;e->budget=8*1024*1024;e->ratio=2;e->pool=1; + e->q[0].key="body";e->q[1].key=e->q[2].key="tag"; + e->q[1].hold=5000000;e->q[2].hold=8000000; + for(int i=1;i<argc;i+=2) { + if(i+1==argc){fprintf(stderr,"option needs value\n");return 2;} + const char *key=argv[i],*value=argv[i+1]; + if(!strcmp(key,"--policy"))e->policy=value; + else if(!strcmp(key,"--budget"))e->budget=(size_t)number(value,1073741824); + else if(!strcmp(key,"--slots"))e->slots=(size_t)number(value,4096); + else if(!strcmp(key,"--fanout"))e->fanout=(size_t)number(value,BRANCHES); + else if(!strcmp(key,"--ratio"))e->ratio=(unsigned)number(value,1024); + else if(!strcmp(key,"--pool"))e->pool=(int)number(value,1); + else if(!strcmp(key,"--cancel-after"))e->cancel_after=number(value,UINT64_MAX); + else { + int recognized=0; + for(size_t k=0;k<BRANCHES;++k) { + char option[32]; + snprintf(option,sizeof(option),"--key%zu",k); + if(!strcmp(key,option)){e->q[k].key=value;recognized=1;} + snprintf(option,sizeof(option),"--hold%zu-us",k); + if(!strcmp(key,option)){e->q[k].hold=number(value,10000000)*1000;recognized=1;} + snprintf(option,sizeof(option),"--service%zu-us",k); + if(!strcmp(key,option)){e->q[k].service=number(value,10000000)*1000;recognized=1;} + } + if(!recognized){fprintf(stderr,"unknown option\n");return 2;} + } + } + if(!e->slots || !e->fanout || e->ratio<2 || (strcmp(e->policy,"borrow") && strcmp(e->policy,"copy") && + strcmp(e->policy,"shared_copy") && strcmp(e->policy,"threshold") && strcmp(e->policy,"compact"))) { + fprintf(stderr,"invalid configuration\n");return 2; + } + e->fixed=sizeof(*e)+e->fanout*e->slots*sizeof(Item); + if(e->fixed>e->budget){fprintf(stderr,"budget below metadata\n");return 2;} + for(size_t k=0;k<e->fanout;++k) { + e->q[k].items=calloc(e->slots,sizeof(Item)); + if(!e->q[k].items){for(size_t j=0;j<k;++j)free(e->q[j].items);return 2;} + } + signal(SIGINT,stop);signal(SIGTERM,stop);signal(SIGPIPE,SIG_IGN); + uint64_t start=ns(CLOCK_MONOTONIC),cpu=ns(CLOCK_PROCESS_CPUTIME_ID); + run(e); + size_t live_at_stop=e->active; + for(size_t k=0;k<e->fanout;++k) { + Queue *q=&e->q[k]; + while(q->count){Item *item=&q->items[q->head];detach(item);drop(e,item->block);q->head=(q->head+1)%e->slots;--q->count;} + } + for(size_t i=0;i<CACHE;++i)evict(e,i); + assert(!e->active && !e->reserved); + e->elapsed_ns=ns(CLOCK_MONOTONIC)-start;e->cpu_ns=ns(CLOCK_PROCESS_CPUTIME_ID)-cpu; + fprintf(stderr,"{\"status\":\"%s\",\"policy\":\"%s\",\"parser_version\":\"%s\",\"budget_bytes\":%zu," + "\"fixed_bytes\":%zu,\"peak_reserved_bytes\":%zu,\"source_peak_bytes\":%zu,\"output_peak_bytes\":%zu," + "\"parser_peak_bytes\":%zu,\"rss_peak_bytes\":%" PRIu64 ",\"elapsed_ns\":%" PRIu64 ",\"cpu_ns\":%" PRIu64 "," + "\"parse_call_ns\":%" PRIu64 ",\"accepted\":%" PRIu64 ",\"emitted\":%" PRIu64 ",\"input_bytes\":%" PRIu64 "," + "\"selection_copy_bytes\":%" PRIu64 ",\"reader_copy_bytes\":%" PRIu64 ",\"parser_realloc_copy_bytes\":%" PRIu64 "," + "\"source_byte_seconds\":%.6Lf,\"output_byte_seconds\":%.6Lf,\"parser_byte_seconds\":%.6Lf," + "\"source_allocations\":%" PRIu64 ",\"output_allocations\":%" PRIu64 ",\"parser_allocations\":%" PRIu64 "," + "\"reuses\":%" PRIu64 ",\"budget_waits\":%" PRIu64 ",\"slot_waits\":%" PRIu64 "," + "\"compacted\":%" PRIu64 ",\"compact_skipped\":%" PRIu64 ",\"dedup_hits\":%" PRIu64 ",\"sleep_ns\":%" PRIu64 "," + "\"live_at_stop_bytes\":%zu,\"unreleased_buffer_bytes\":%zu,\"queues\":[", + e->status,e->policy,YYJSON_VERSION_STRING,e->budget,e->fixed,e->fixed+e->peak,e->peak_kind[SOURCE], + e->peak_kind[OUTPUT],e->peak_kind[PARSER],rss(),e->elapsed_ns,e->cpu_ns,e->parse_ns,e->accepted,e->emitted, + e->input_bytes,e->selection_copies,e->reader_copies,e->parser_copies,e->byte_ns[SOURCE]/1e9L, + e->byte_ns[OUTPUT]/1e9L,e->byte_ns[PARSER]/1e9L,e->allocations[SOURCE],e->allocations[OUTPUT], + e->allocations[PARSER],e->reuses,e->budget_waits,e->slot_waits,e->compacted,e->compact_skipped, + e->dedup_hits,e->sleep_ns,live_at_stop,e->reserved); + for(size_t k=0;k<e->fanout;++k) { + Queue *q=&e->q[k];uint64_t target=q->emitted-q->emitted/100,sum=0,p99=0; + for(unsigned b=0;q->emitted && b<HIST;++b){sum+=q->histogram[b];if(sum>=target){p99=upper(b);break;}} + fprintf(stderr,"%s{\"emitted\":%" PRIu64 ",\"bytes\":%" PRIu64 ",\"peak_count\":%zu," + "\"latency_mean_ns\":%.3f,\"latency_p99_upper_ns\":%" PRIu64 "}",k?",":"",q->emitted,q->bytes,q->peak, + q->emitted?(double)q->latency_sum/q->emitted:0,p99); + free(q->items); + } + fprintf(stderr,"]}\n");return !strcmp(e->status,"ok")?0:!strcmp(e->status,"cancelled")?3:2; +} diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/plan.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/plan.json new file mode 100644 index 0000000..60c179b --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/plan.json @@ -0,0 +1,32 @@ +{ + "version": 1, + "written_before_measurement": true, + "seed": 20260923, + "repeats": 5, + "policies": [ + {"name": "borrow", "policy": "borrow"}, + {"name": "copy", "policy": "copy"}, + {"name": "shared_copy", "policy": "shared_copy"}, + {"name": "threshold2", "policy": "threshold", "ratio": 2}, + {"name": "threshold8", "policy": "threshold", "ratio": 8}, + {"name": "compact", "policy": "compact", "ratio": 2}, + {"name": "copy_no_pool", "policy": "copy", "pool": 0} + ], + "cases": [ + {"name": "one_slow", "shape": "large", "records": 1024, "keys": ["tag"], "hold_us": [8000], "service_us": [0], "budget": 8388608, "slots": 64}, + {"name": "three_distinct_speeds", "shape": "large", "records": 1024, "keys": ["body", "tag", "tag"], "hold_us": [0, 5000, 8000], "service_us": [0, 0, 0], "budget": 8388608, "slots": 64}, + {"name": "same_large_field", "shape": "large", "records": 1024, "keys": ["body", "body", "body"], "hold_us": [0, 5000, 8000], "service_us": [0, 0, 0], "budget": 8388608, "slots": 64}, + {"name": "slow_large_field", "shape": "large", "records": 1024, "keys": ["tag", "tag", "body"], "hold_us": [0, 5000, 8000], "service_us": [0, 0, 0], "budget": 8388608, "slots": 64}, + {"name": "mixed_tight", "shape": "mixed", "records": 1024, "keys": ["body", "tag", "tag"], "hold_us": [0, 5000, 8000], "service_us": [0, 0, 0], "budget": 2097152, "slots": 64}, + {"name": "metadata_heavy", "shape": "metadata", "records": 512, "keys": ["body", "tag", "tag"], "hold_us": [0, 5000, 8000], "service_us": [0, 0, 0], "budget": 2097152, "slots": 64} + ], + "expectations": [ + "A slow small-field consumer can retain the input after a fast large-field consumer finishes.", + "Sharing identical compact copies can remove duplicate per-consumer copies without a new runtime.", + "Late compaction may save copied bytes or fail to beat early size-based copying; all remaining owners matter.", + "Parser temporary memory and reader growth must be charged, even when retained selections are small.", + "No novel-policy advantage or production service SLO is presumed." + ], + "comparison_rule": "Report all fixed presets; exploratory measurements, no trained/held-out policy or significance claim.", + "limits": "Real yyjson parser and actual bytes, but synthetic NDJSON corpus and sink delays, three synchronous consumers in one event loop, no open-loop arrivals, thread races, durable output, or independent parser comparison." +} diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/run.py b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/run.py new file mode 100644 index 0000000..e4848bd --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/run.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +"""Verify and measure yyjson parsing with bounded, differently paced consumers.""" +import argparse +import csv +import hashlib +import json +import os +from pathlib import Path +import platform +import random +import shlex +import signal +import statistics +import struct +import subprocess +import tempfile +import time + +ROOT = Path(__file__).resolve().parent +FLAGS = ["-std=c17", "-O2", "-g", "-Wall", "-Wextra", "-Wpedantic", "-Werror"] +FLAGS += ["-DYYJSON_DISABLE_WRITER=1", "-DYYJSON_DISABLE_UTILS=1", "-DYYJSON_DISABLE_INCR_READER=1"] +SOURCE_NAMES = ["benchmark.c", "run.py", "plan.json", "vendor/yyjson/UPSTREAM.json", + "vendor/yyjson/yyjson.c", "vendor/yyjson/yyjson.h", "vendor/yyjson/LICENSE"] + + +def digest(path): + with path.open("rb") as stream: + return hashlib.file_digest(stream, "sha256").hexdigest() + + +def dump(path, value): + path.write_text(json.dumps(value, indent=2, ensure_ascii=False) + "\n") + + +def first_members(pairs): + result = {} + for key, value in pairs: + result.setdefault(key, value) + return result + + +def reference(data, keys): + values = [[] for _ in keys] + for line in data.splitlines(): + row = json.loads(line, object_pairs_hook=first_members) + if not isinstance(row, dict): + raise ValueError("object required") + for k, key in enumerate(keys): + if not isinstance(row.get(key), str): + raise ValueError("string field required") + values[k].append(row[key].encode("utf-8")) + return values + + +def decode(data, keys, expected, partial=False): + at = 0 + found = [[] for _ in keys] + while at < len(data): + if len(data) - at < 16: + raise AssertionError("incomplete output header") + k, length, sequence = struct.unpack_from("<IIQ", data, at) + at += 16 + assert k < len(keys) and sequence == len(found[k]) + assert length <= len(data) - at + found[k].append(data[at:at + length]) + at += length + for k in range(len(keys)): + assert found[k] == (expected[k][:len(found[k])] if partial else expected[k]) + return [len(items) for items in found] + + +def build(cc, output, sanitizer=False): + command = [*cc, *FLAGS] + if sanitizer: + command += ["-O1", "-fsanitize=address,undefined", "-fno-omit-frame-pointer"] + command += [str(ROOT / "benchmark.c"), str(ROOT / "vendor/yyjson/yyjson.c"), "-o", str(output)] + start = time.monotonic_ns() + result = subprocess.run(command, capture_output=True, text=True, check=True) + return {"command": command, "stderr": result.stderr, "sha256": digest(output), + "size_bytes": output.stat().st_size, "build_wall_ns": time.monotonic_ns() - start} + + +def command(binary, case, policy, *extra): + keys = case.get("keys", ["body", "tag", "tag"]) + holds = case.get("hold_us", [0, 5000, 8000][:len(keys)]) + services = case.get("service_us", [0] * len(keys)) + result = [str(binary), "--policy", policy["policy"], "--fanout", str(len(keys)), + "--slots", str(case.get("slots", 64)), "--budget", str(case.get("budget", 8388608)), + "--pool", str(policy.get("pool", 1)), "--ratio", str(policy.get("ratio", 2))] + for k, key in enumerate(keys): + result += [f"--key{k}", key, f"--hold{k}-us", str(holds[k]), f"--service{k}-us", str(services[k])] + return result + list(extra) + + +def metrics(stderr): + try: + m = json.loads(stderr) + except (ValueError, UnicodeError) as exc: + raise AssertionError(f"Expected metrics only, got diagnostics: {stderr!r}") from exc + assert m["unreleased_buffer_bytes"] == 0 + assert m["peak_reserved_bytes"] <= m["budget_bytes"] + assert sum(q["emitted"] for q in m["queues"]) == m["emitted"] + assert m["parser_version"] == "0.12.0" + return m + + +def invoke(binary, case, policy, data, *extra): + proc = subprocess.run(command(binary, case, policy, *extra), input=data, + capture_output=True, timeout=20) + return proc, metrics(proc.stderr) + + +def verify(binary, policies, tmp): + checks = [] + rows = [{"body": "A" * 33000, "tag": "one"}, + {"body": "한글😀\n\x00\"\\", "tag": ""}, + {"body": "", "tag": "둘\t\r"}, + {"body": "z" * 66000, "tag": "three", "meta": [True, None, {"x": -3.5}]}] * 7 + data = b"".join(json.dumps(row, ensure_ascii=i % 2 == 0).encode() + b"\n" for i, row in enumerate(rows)) + for policy in policies: + for keys in [["tag"], ["body", "tag"], ["body", "tag", "tag"]]: + case = {"keys": keys, "hold_us": [500 * k for k in range(len(keys))], "slots": 3} + proc, m = invoke(binary, case, policy, data) + assert proc.returncode == 0 and m["status"] == "ok", proc.stderr + counts = decode(proc.stdout, keys, reference(data, keys)) + assert counts == [len(rows)] * len(keys) and m["accepted"] == len(rows) + assert m["live_at_stop_bytes"] == 0 + checks.append(f'{policy["name"]}: {len(keys)} consumer(s), UTF-8/escapes/NUL/empty, slot reuse') + keys = ["body", "tag", "tag"] + for sample in [b"", data.rstrip(b"\n"), b'{"body":"first","body":"second","tag":"x"}\n']: + proc, m = invoke(binary, {}, policy, sample) + assert proc.returncode == 0 and m["status"] == "ok" + decode(proc.stdout, keys, reference(sample, keys)) + checks.append(f'{policy["name"]}: empty input, final JSON without newline, first duplicate member') + malformed = [b'{"body":"x","tag":', b'{"body":"x","tag":"y","extra":[1,]}', + b'{"body":"x","tag":3}', b'{"body":"x"}', b'[]', + b'{"body":"\xff","tag":"x"}', b'\n', + b'{"body":"x","tag":"y"} true'] + for bad in malformed: + proc, m = invoke(binary, {"hold_us": [1000, 2000, 3000]}, policy, data[:data.index(b"\n") + 1] + bad) + assert proc.returncode == 2 and m["status"] in {"invalid_json", "invalid_schema"}, proc.stderr + checks.append(f'{policy["name"]}: eight invalid JSON/schema cases with retained owners') + proc, m = invoke(binary, {}, policy, data, "--cancel-after", "7") + assert proc.returncode == 3 and m["status"] == "cancelled" and m["emitted"] == 7 + counts = decode(proc.stdout, keys, reference(data, keys), partial=True) + assert sum(counts) == 7 + checks.append(f'{policy["name"]}: cancellation preserves each branch prefix and releases all shared owners') + # Source can fit; the many JSON nodes require more parser storage than the budget. + heavy = json.dumps({"body": "x", "tag": "y", "meta": [0] * 20000}).encode() + proc, m = invoke(binary, {"slots": 1, "budget": 160000}, policy, heavy) + assert proc.returncode == 2 and m["status"] == "budget_too_small", proc.stderr + assert m["accepted"] == 0 + checks.append(f'{policy["name"]}: parser allocation charged and refused within total budget') + # Same decoded field is shared after the document metadata has been freed. + sample = json.dumps({"body": "z" * 40000, "tag": "small"}).encode() + b"\n" + case = {"keys": ["tag", "tag", "tag"], "hold_us": [0, 1000, 2000]} + _, separate = invoke(binary, case, {"policy": "copy"}, sample) + proc, shared = invoke(binary, case, {"policy": "shared_copy"}, sample) + decode(proc.stdout, case["keys"], reference(sample, case["keys"])) + assert separate["selection_copy_bytes"] == 15 and shared["selection_copy_bytes"] == 5 + assert shared["dedup_hits"] == 2 + checks.append("identical selections copied once and live until the final consumer") + case = {"keys": ["body", "tag"], "hold_us": [0, 3000]} + proc, m = invoke(binary, case, {"policy": "compact"}, sample) + decode(proc.stdout, case["keys"], reference(sample, case["keys"])) + assert m["compacted"] == 1 and m["selection_copy_bytes"] == 5 + checks.append("last-owner compaction keeps the delayed decoded value valid") + proc, m = invoke(binary, {}, policies[0], b'x' * (1024 * 1024 + 1)) + assert proc.returncode == 2 and m["status"] == "record_too_large" + checks.append("overlong NDJSON record refused without leaked source") + if Path("/dev/full").exists(): + with open("/dev/full", "wb") as sink: + proc = subprocess.run(command(binary, {}, policies[0]), input=sample, + stdout=sink, stderr=subprocess.PIPE, timeout=10) + assert proc.returncode == 2 and metrics(proc.stderr)["status"] == "output_failed" + checks.append("actual /dev/full failure releases multiple owners") + source = tmp / "signal.ndjson" + source.write_bytes(sample * 20) + with source.open("rb") as inp, open(os.devnull, "wb") as sink: + proc = subprocess.Popen(command(binary, {"hold_us": [500000] * 3}, policies[0]), + stdin=inp, stdout=sink, stderr=subprocess.PIPE) + time.sleep(0.1) + proc.send_signal(signal.SIGTERM) + _, stderr = proc.communicate(timeout=5) + assert proc.returncode == 3 and metrics(stderr)["status"] == "cancelled" + checks.append("SIGTERM during delayed fan-out releases all owners") + return checks + + +def make_fixture(path, case, seed): + rng = random.Random(seed) + hashes = [hashlib.sha256() for _ in case["keys"]] + sizes = [0] * len(hashes) + with path.open("wb") as stream: + for seq in range(case["records"]): + if case["shape"] == "large": + body_size, padding = 32768, 8192 + elif case["shape"] == "mixed": + body_size, padding = rng.choice([(256, 1024), (4096, 16384), (32768, 8192), (65536, 65536)]) + elif case["shape"] == "metadata": + body_size, padding = 512, 512 + else: + raise ValueError(case["shape"]) + tile = rng.randbytes(128).hex() + body = (tile * ((body_size + 255) // 256))[:body_size] + tag = f'{seq:08x}-한글-"-\\-\n-\x00-' + tile[:32] + row = {"id": seq, "body": body, "tag": tag, "padding": "p" * padding, + "meta": list(range(2048)) if case["shape"] == "metadata" else {"ok": True, "n": seq}} + encoded = json.dumps(row, ensure_ascii=seq % 2 == 0, separators=(",", ":")).encode("utf-8") + stream.write(encoded + b"\n") + # Python's independent decoder defines the expected decoded UTF-8 output. + decoded = json.loads(encoded) + for k, key in enumerate(case["keys"]): + selected = decoded[key].encode("utf-8") + hashes[k].update(struct.pack("<QI", seq, len(selected)) + selected) + sizes[k] += len(selected) + return {"case": case["name"], "seed": seed, "records": case["records"], "input_bytes": path.stat().st_size, + "input_sha256": digest(path), "keys": case["keys"], "output_bytes_by_branch": sizes, + "output_sha256_by_branch": [h.hexdigest() for h in hashes]} + + +def verify_output(path, fixture): + count = [0] * len(fixture["keys"]) + hashes = [hashlib.sha256() for _ in count] + sizes = [0] * len(count) + with path.open("rb") as stream: + while header := stream.read(16): + assert len(header) == 16 + k, n, seq = struct.unpack("<IIQ", header) + assert k < len(count) and seq == count[k] and n <= 1024 * 1024 + data = stream.read(n) + assert len(data) == n + hashes[k].update(struct.pack("<QI", seq, n) + data) + count[k] += 1 + sizes[k] += n + assert count == [fixture["records"]] * len(count) + assert sizes == fixture["output_bytes_by_branch"] + assert [h.hexdigest() for h in hashes] == fixture["output_sha256_by_branch"] + + +def sample(binary, source, output, case, policy, fixture): + with source.open("rb") as inp, output.open("wb") as sink: + proc = subprocess.run(command(binary, case, policy), stdin=inp, stdout=sink, + stderr=subprocess.PIPE, timeout=30) + m = metrics(proc.stderr) + assert proc.returncode == 0 and m["status"] == "ok", proc.stderr + assert m["accepted"] == case["records"] and m["emitted"] == case["records"] * len(case["keys"]) + assert m["input_bytes"] == fixture["input_bytes"] + assert m["live_at_stop_bytes"] == 0 + verify_output(output, fixture) + return m + + +def summary(rows): + keys = ["peak_reserved_bytes", "source_peak_bytes", "output_peak_bytes", "parser_peak_bytes", + "rss_peak_bytes", "elapsed_ns", "cpu_ns", "parse_call_ns", "selection_copy_bytes", + "reader_copy_bytes", "parser_realloc_copy_bytes", "source_byte_seconds", "budget_waits", + "compacted", "dedup_hits"] + result = [] + for case, variant in sorted({(r["case"], r["variant"]) for r in rows}): + group = [r for r in rows if (r["case"], r["variant"]) == (case, variant)] + item = {"case": case, "variant": variant, "samples": len(group)} + for key in keys: + values = [r[key] for r in group] + item[key] = {"median": statistics.median(values), "min": min(values), "max": max(values)} + item["queues"] = [{key: statistics.median(r["queues"][k][key] for r in group) + for key in ["peak_count", "latency_mean_ns", "latency_p99_upper_ns"]} + for k in range(len(group[0]["queues"]))] + result.append(item) + return result + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check-only", action="store_true") + parser.add_argument("--output", type=Path) + args = parser.parse_args() + if not args.check_only and args.output is None: + parser.error("provide --check-only or --output NEW_DIRECTORY") + if args.output: + args.output.mkdir(parents=True, exist_ok=False) + upstream = json.loads((ROOT / "vendor/yyjson/UPSTREAM.json").read_text()) + for name, item in upstream["files"].items(): + assert digest(ROOT / "vendor/yyjson" / name) == item["sha256"] + plan = json.loads((ROOT / "plan.json").read_text()) + cc = shlex.split(os.environ.get("CC", "cc")) + affinity = None + if hasattr(os, "sched_getaffinity"): + affinity = min(os.sched_getaffinity(0)) + os.sched_setaffinity(0, {affinity}) + with tempfile.TemporaryDirectory(prefix="pure-fanout-") as folder: + tmp = Path(folder) + binary, sanitizer = tmp / "benchmark", tmp / "benchmark-sanitized" + print("Building optimized parser and pipeline.", flush=True) + builds = [build(cc, binary)] + print("Building parser and pipeline with ASan/UBSan (-O1).", flush=True) + builds.append(build(cc, sanitizer, True)) + print("Checking native and instrumented builds.", flush=True) + checks = {"native": verify(binary, plan["policies"], tmp), + "asan_ubsan": verify(sanitizer, plan["policies"], tmp)} + print(f'Verified {sum(map(len, checks.values()))} scenario groups (native + ASan/UBSan).', flush=True) + if args.check_only: + return + sources = {name: digest(ROOT / name) for name in SOURCE_NAMES} + environment = {"platform": platform.platform(), "machine": platform.machine(), "python": platform.python_version(), + "compiler": subprocess.check_output([*cc, "--version"], text=True), "cpu_affinity": affinity, + "builds": builds, "source_sha256": sources, "upstream": upstream, + "timing": "I/O, parsing, selection, consumers, instrumentation, cleanup; excludes setup/build/verification", + "parse_call_ns": "wall duration inside yyjson_read_opts including allocator backpressure and serviced consumers", + "rss": "Linux current-image VmHWM approximate; zero means unavailable on other OSes", + "limits": "synthetic closed-loop arrivals/delays; one thread; uncontrolled thermals/power; cached file I/O; no fsync", + "started_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())} + if Path("/proc/cpuinfo").exists(): + environment["cpuinfo"] = Path("/proc/cpuinfo").read_text() + dump(args.output / "environment.json", environment) + dump(args.output / "verification.json", checks) + dump(args.output / "plan.json", plan) + for name in sources: + destination = args.output / "sources" / name + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes((ROOT / name).read_bytes()) + rng, rows, fixtures = random.Random(plan["seed"]), [], [] + for case in plan["cases"]: + source, output = tmp / "input.ndjson", tmp / "branches.bin" + info = make_fixture(source, case, plan["seed"]) + fixtures.append(info) + for policy in plan["policies"]: + sample(binary, source, output, case, policy, info) + for repeat in range(plan["repeats"]): + order = list(plan["policies"]) + rng.shuffle(order) + for policy in order: + value = sample(binary, source, output, case, policy, info) + rows.append({"case": case["name"], "variant": policy["name"], "repeat": repeat, **value}) + dump(args.output / "raw.json", rows) + dump(args.output / "fixtures.json", fixtures) + print(f'Completed {case["name"]}: {len(plan["policies"])} variants x {plan["repeats"]} trials.', flush=True) + dump(args.output / "summary.json", summary(rows)) + flat = [{**{k: v for k, v in row.items() if k != "queues"}, "queues": json.dumps(row["queues"])} for row in rows] + with (args.output / "raw.csv").open("w") as out: + writer = csv.DictWriter(out, fieldnames=list(flat[0])) + writer.writeheader() + writer.writerows(flat) + assert sources == {name: digest(ROOT / name) for name in SOURCE_NAMES} + dump(args.output / "complete.json", {"samples": len(rows), "all_branch_outputs_match": True, + "finished_utc": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())}) + print(args.output.resolve()) + + +if __name__ == "__main__": + main() diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/LICENSE b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/LICENSE new file mode 100644 index 0000000..a09bff3 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 YaoYuan <[email protected]> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/UPSTREAM.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/UPSTREAM.json new file mode 100644 index 0000000..7de2396 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/UPSTREAM.json @@ -0,0 +1,23 @@ +{ + "project": "yyjson", + "version": "0.12.0", + "commit": "8b4a38dc994a110abaec8a400615567bd996105f", + "license": "MIT", + "files": { + "yyjson.c": { + "url": "https://raw.githubusercontent.com/ibireme/yyjson/8b4a38dc994a110abaec8a400615567bd996105f/src/yyjson.c", + "sha256": "ac2e9bbb2e2d9149d90878d40506a1d624fa0b33c979a11b61075c54782c6d6a", + "bytes": 410813 + }, + "yyjson.h": { + "url": "https://raw.githubusercontent.com/ibireme/yyjson/8b4a38dc994a110abaec8a400615567bd996105f/src/yyjson.h", + "sha256": "175867c5493a5df648cec566717fa1c29aa2f6096f5f0cf1efad0b65e1f6d7b3", + "bytes": 322407 + }, + "LICENSE": { + "url": "https://raw.githubusercontent.com/ibireme/yyjson/8b4a38dc994a110abaec8a400615567bd996105f/LICENSE", + "sha256": "45e384d3d52c73cba3a64d6e6c25d47cd738cd8a55c30629e3201046eda62947", + "bytes": 1084 + } + } +} diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/yyjson.c b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/yyjson.c new file mode 100644 index 0000000..c16d925 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/yyjson.c @@ -0,0 +1,11065 @@ +/*============================================================================== + Copyright (c) 2020 YaoYuan <[email protected]> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + *============================================================================*/ + +#include "yyjson.h" +#include <math.h> /* for `HUGE_VAL/INFINIY/NAN` macros, no libm required */ + + + +/*============================================================================== + * MARK: - Warning Suppress (Private) + *============================================================================*/ + +#if defined(__clang__) +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-parameter" +# pragma clang diagnostic ignored "-Wunused-label" +# pragma clang diagnostic ignored "-Wunused-macros" +# pragma clang diagnostic ignored "-Wunused-variable" +#elif defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wunused-label" +# pragma GCC diagnostic ignored "-Wunused-macros" +# pragma GCC diagnostic ignored "-Wunused-variable" +#elif defined(_MSC_VER) +# pragma warning(disable:4100) /* unreferenced formal parameter */ +# pragma warning(disable:4101) /* unreferenced variable */ +# pragma warning(disable:4102) /* unreferenced label */ +# pragma warning(disable:4127) /* conditional expression is constant */ +# pragma warning(disable:4706) /* assignment within conditional expression */ +#endif + + + +/*============================================================================== + * MARK: - Version (Public) + *============================================================================*/ + +uint32_t yyjson_version(void) { + return YYJSON_VERSION_HEX; +} + + + +/*============================================================================== + * MARK: - Flags (Private) + *============================================================================*/ + +/* msvc intrinsic */ +#if YYJSON_MSC_VER >= 1400 +# include <intrin.h> +# if defined(_M_AMD64) || defined(_M_ARM64) +# define MSC_HAS_BIT_SCAN_64 1 +# pragma intrinsic(_BitScanForward64) +# pragma intrinsic(_BitScanReverse64) +# else +# define MSC_HAS_BIT_SCAN_64 0 +# endif +# if defined(_M_AMD64) || defined(_M_ARM64) || \ + defined(_M_IX86) || defined(_M_ARM) +# define MSC_HAS_BIT_SCAN 1 +# pragma intrinsic(_BitScanForward) +# pragma intrinsic(_BitScanReverse) +# else +# define MSC_HAS_BIT_SCAN 0 +# endif +# if defined(_M_AMD64) +# define MSC_HAS_UMUL128 1 +# pragma intrinsic(_umul128) +# else +# define MSC_HAS_UMUL128 0 +# endif +#else +# define MSC_HAS_BIT_SCAN_64 0 +# define MSC_HAS_BIT_SCAN 0 +# define MSC_HAS_UMUL128 0 +#endif + +/* gcc builtin */ +#if yyjson_has_builtin(__builtin_clzll) || yyjson_gcc_available(3, 4, 0) +# define GCC_HAS_CLZLL 1 +#else +# define GCC_HAS_CLZLL 0 +#endif + +#if yyjson_has_builtin(__builtin_ctzll) || yyjson_gcc_available(3, 4, 0) +# define GCC_HAS_CTZLL 1 +#else +# define GCC_HAS_CTZLL 0 +#endif + +/* int128 type */ +#if defined(__SIZEOF_INT128__) && (__SIZEOF_INT128__ == 16) && \ + (defined(__GNUC__) || defined(__clang__) || defined(__INTEL_COMPILER)) +# define YYJSON_HAS_INT128 1 +#else +# define YYJSON_HAS_INT128 0 +#endif + +/* IEEE 754 floating-point binary representation */ +#if defined(__STDC_IEC_559__) || defined(__STDC_IEC_60559_BFP__) +# define YYJSON_HAS_IEEE_754 1 +#elif FLT_RADIX == 2 && \ + FLT_MANT_DIG == 24 && FLT_DIG == 6 && \ + FLT_MIN_EXP == -125 && FLT_MAX_EXP == 128 && \ + FLT_MIN_10_EXP == -37 && FLT_MAX_10_EXP == 38 && \ + DBL_MANT_DIG == 53 && DBL_DIG == 15 && \ + DBL_MIN_EXP == -1021 && DBL_MAX_EXP == 1024 && \ + DBL_MIN_10_EXP == -307 && DBL_MAX_10_EXP == 308 +# define YYJSON_HAS_IEEE_754 1 +#else +# define YYJSON_HAS_IEEE_754 0 +# undef YYJSON_DISABLE_FAST_FP_CONV +# define YYJSON_DISABLE_FAST_FP_CONV 1 +#endif + +/* + Correct rounding in double number computations. + + On the x86 architecture, some compilers may use x87 FPU instructions for + floating-point arithmetic. The x87 FPU loads all floating point number as + 80-bit double-extended precision internally, then rounds the result to original + precision, which may produce inaccurate results. For a more detailed + explanation, see the paper: https://arxiv.org/abs/cs/0701192 + + Here are some examples of double precision calculation error: + + 2877.0 / 1e6 == 0.002877, but x87 returns 0.0028770000000000002 + 43683.0 * 1e21 == 4.3683e25, but x87 returns 4.3683000000000004e25 + + Here are some examples of compiler flags to generate x87 instructions on x86: + + clang -m32 -mno-sse + gcc/icc -m32 -mfpmath=387 + msvc /arch:SSE or /arch:IA32 + + If we are sure that there's no similar error described above, we can define the + YYJSON_DOUBLE_MATH_CORRECT as 1 to enable the fast path calculation. This is + not an accurate detection, it's just try to avoid the error at compile-time. + An accurate detection can be done at run-time: + + bool is_double_math_correct(void) { + volatile double r = 43683.0; + r *= 1e21; + return r == 4.3683e25; + } + + See also: utils.h in https://github.com/google/double-conversion/ + */ +#if !defined(FLT_EVAL_METHOD) && defined(__FLT_EVAL_METHOD__) +# define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ +#endif + +#if defined(FLT_EVAL_METHOD) && FLT_EVAL_METHOD != 0 && FLT_EVAL_METHOD != 1 +# define YYJSON_DOUBLE_MATH_CORRECT 0 +#elif defined(i386) || defined(__i386) || defined(__i386__) || \ + defined(_X86_) || defined(__X86__) || defined(_M_IX86) || \ + defined(__I86__) || defined(__IA32__) || defined(__THW_INTEL) +# if (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP == 2) || \ + (defined(__SSE2_MATH__) && __SSE2_MATH__) +# define YYJSON_DOUBLE_MATH_CORRECT 1 +# else +# define YYJSON_DOUBLE_MATH_CORRECT 0 +# endif +#elif defined(__mc68000__) || defined(__pnacl__) || defined(__native_client__) +# define YYJSON_DOUBLE_MATH_CORRECT 0 +#else +# define YYJSON_DOUBLE_MATH_CORRECT 1 +#endif + +/* + Detect the endianness at compile-time. + YYJSON_ENDIAN == YYJSON_BIG_ENDIAN + YYJSON_ENDIAN == YYJSON_LITTLE_ENDIAN + */ +#define YYJSON_BIG_ENDIAN 4321 +#define YYJSON_LITTLE_ENDIAN 1234 + +#if yyjson_has_include(<sys/types.h>) +# include <sys/types.h> /* POSIX */ +#endif +#if yyjson_has_include(<endian.h>) +# include <endian.h> /* Linux */ +#elif yyjson_has_include(<sys/endian.h>) +# include <sys/endian.h> /* BSD, Android */ +#elif yyjson_has_include(<machine/endian.h>) +# include <machine/endian.h> /* BSD, Darwin */ +#endif + +#if defined(BYTE_ORDER) && BYTE_ORDER +# if defined(BIG_ENDIAN) && (BYTE_ORDER == BIG_ENDIAN) +# define YYJSON_ENDIAN YYJSON_BIG_ENDIAN +# elif defined(LITTLE_ENDIAN) && (BYTE_ORDER == LITTLE_ENDIAN) +# define YYJSON_ENDIAN YYJSON_LITTLE_ENDIAN +# endif +#elif defined(__BYTE_ORDER) && __BYTE_ORDER +# if defined(__BIG_ENDIAN) && (__BYTE_ORDER == __BIG_ENDIAN) +# define YYJSON_ENDIAN YYJSON_BIG_ENDIAN +# elif defined(__LITTLE_ENDIAN) && (__BYTE_ORDER == __LITTLE_ENDIAN) +# define YYJSON_ENDIAN YYJSON_LITTLE_ENDIAN +# endif +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ +# if defined(__ORDER_BIG_ENDIAN__) && \ + (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +# define YYJSON_ENDIAN YYJSON_BIG_ENDIAN +# elif defined(__ORDER_LITTLE_ENDIAN__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +# define YYJSON_ENDIAN YYJSON_LITTLE_ENDIAN +# endif +#elif (defined(__LITTLE_ENDIAN__) && __LITTLE_ENDIAN__ == 1) || \ + defined(__i386) || defined(__i386__) || \ + defined(_X86_) || defined(__X86__) || \ + defined(_M_IX86) || defined(__THW_INTEL__) || \ + defined(__x86_64) || defined(__x86_64__) || \ + defined(__amd64) || defined(__amd64__) || \ + defined(_M_AMD64) || defined(_M_X64) || \ + defined(_M_ARM) || defined(_M_ARM64) || \ + defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || \ + defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \ + defined(__EMSCRIPTEN__) || defined(__wasm__) || \ + defined(__loongarch__) +# define YYJSON_ENDIAN YYJSON_LITTLE_ENDIAN +#elif (defined(__BIG_ENDIAN__) && __BIG_ENDIAN__ == 1) || \ + defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \ + defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) || \ + defined(__or1k__) || defined(__OR1K__) +# define YYJSON_ENDIAN YYJSON_BIG_ENDIAN +#else +# define YYJSON_ENDIAN 0 /* unknown endian, detect at run-time */ +#endif + +/* + This macro controls how yyjson handles unaligned memory accesses. + + By default, yyjson uses `memcpy()` for memory copying. This allows the compiler + to optimize the code and emit unaligned memory access instructions when + supported by the target architecture. + + However, on some older compilers or architectures where `memcpy()` is not + well-optimized and may result in unnecessary function calls, defining this + macro as 1 may help. In such cases, yyjson switches to manual byte-by-byte + access, which can potentially improve performance. + + An example of the generated assembly code for ARM can be found here: + https://godbolt.org/z/334jjhxPT + + This flag is already enabled for common architectures in the following code, + so manual configuration is usually unnecessary. If unsure, you can check the + generated assembly or run benchmarks to make an informed decision. + */ +#ifndef YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS +# if defined(__ia64) || defined(_IA64) || defined(__IA64__) || \ + defined(__ia64__) || defined(_M_IA64) || defined(__itanium__) +# define YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS 1 /* Itanium */ +# elif (defined(__arm__) || defined(__arm64__) || defined(__aarch64__)) && \ + (defined(__GNUC__) || defined(__clang__)) && \ + (!defined(__ARM_FEATURE_UNALIGNED) || !__ARM_FEATURE_UNALIGNED) +# define YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS 1 /* ARM */ +# elif defined(__sparc) || defined(__sparc__) +# define YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS 1 /* SPARC */ +# elif defined(__mips) || defined(__mips__) || defined(__MIPS__) +# define YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS 1 /* MIPS */ +# elif defined(__m68k__) || defined(M68000) +# define YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS 1 /* M68K */ +# else +# define YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS 0 +# endif +#endif + +/* + Estimated initial ratio of the JSON data (data_size / value_count). + For example: + + data: {"id":12345678,"name":"Harry"} + data_size: 30 + value_count: 5 + ratio: 6 + + yyjson uses dynamic memory with a growth factor of 1.5 when reading and writing + JSON, the ratios below are used to determine the initial memory size. + + A too large ratio will waste memory, and a too small ratio will cause multiple + memory growths and degrade performance. Currently, these ratios are generated + with some commonly used JSON datasets. + */ +#define YYJSON_READER_ESTIMATED_PRETTY_RATIO 16 +#define YYJSON_READER_ESTIMATED_MINIFY_RATIO 6 +#define YYJSON_WRITER_ESTIMATED_PRETTY_RATIO 32 +#define YYJSON_WRITER_ESTIMATED_MINIFY_RATIO 18 + +/* The initial and maximum size of the memory pool's chunk in yyjson_mut_doc. */ +#define YYJSON_MUT_DOC_STR_POOL_INIT_SIZE 0x100 +#define YYJSON_MUT_DOC_STR_POOL_MAX_SIZE 0x10000000 +#define YYJSON_MUT_DOC_VAL_POOL_INIT_SIZE (0x10 * sizeof(yyjson_mut_val)) +#define YYJSON_MUT_DOC_VAL_POOL_MAX_SIZE (0x1000000 * sizeof(yyjson_mut_val)) + +/* The minimum size of the dynamic allocator's chunk. */ +#define YYJSON_ALC_DYN_MIN_SIZE 0x1000 + +/* Default value for compile-time options. */ +#ifndef YYJSON_DISABLE_READER +#define YYJSON_DISABLE_READER 0 +#endif +#ifndef YYJSON_DISABLE_WRITER +#define YYJSON_DISABLE_WRITER 0 +#endif +#ifndef YYJSON_DISABLE_INCR_READER +#define YYJSON_DISABLE_INCR_READER 0 +#endif +#ifndef YYJSON_DISABLE_UTILS +#define YYJSON_DISABLE_UTILS 0 +#endif +#ifndef YYJSON_DISABLE_FAST_FP_CONV +#define YYJSON_DISABLE_FAST_FP_CONV 0 +#endif +#ifndef YYJSON_DISABLE_NON_STANDARD +#define YYJSON_DISABLE_NON_STANDARD 0 +#endif +#ifndef YYJSON_DISABLE_UTF8_VALIDATION +#define YYJSON_DISABLE_UTF8_VALIDATION 0 +#endif + + + +/*============================================================================== + * MARK: - Macros (Private) + *============================================================================*/ + +/* Macros used for loop unrolling and other purpose. */ +#define repeat2(x) { x x } +#define repeat4(x) { x x x x } +#define repeat8(x) { x x x x x x x x } +#define repeat16(x) { x x x x x x x x x x x x x x x x } + +#define repeat2_incr(x) { x(0) x(1) } +#define repeat4_incr(x) { x(0) x(1) x(2) x(3) } +#define repeat8_incr(x) { x(0) x(1) x(2) x(3) x(4) x(5) x(6) x(7) } +#define repeat16_incr(x) { x(0) x(1) x(2) x(3) x(4) x(5) x(6) x(7) \ + x(8) x(9) x(10) x(11) x(12) x(13) x(14) x(15) } +#define repeat_in_1_18(x) { x(1) x(2) x(3) x(4) x(5) x(6) x(7) x(8) \ + x(9) x(10) x(11) x(12) x(13) x(14) x(15) x(16) \ + x(17) x(18) } + +/* Macros used to provide branch prediction information for compiler. */ +#undef likely +#define likely(x) yyjson_likely(x) +#undef unlikely +#define unlikely(x) yyjson_unlikely(x) + +/* Macros used to provide inline information for compiler. */ +#undef static_inline +#define static_inline static yyjson_inline +#undef static_noinline +#define static_noinline static yyjson_noinline + +/* Macros for min and max. */ +#undef yyjson_min +#define yyjson_min(x, y) ((x) < (y) ? (x) : (y)) +#undef yyjson_max +#define yyjson_max(x, y) ((x) > (y) ? (x) : (y)) + +/* Used to write u64 literal for C89 which doesn't support "ULL" suffix. */ +#undef U64 +#define U64(hi, lo) ((((u64)hi##UL) << 32U) + lo##UL) +#undef U32 +#define U32(hi) ((u32)(hi##UL)) + +/* Used to cast away (remove) const qualifier. */ +#define constcast(type) (type)(void *)(size_t)(const void *) + +/* + Compiler barriers for single variables. + + These macros inform GCC that a read or write access to the given memory + location will occur, preventing certain compiler optimizations or reordering + around the access to 'val'. They do not emit any actual instructions. + + This is useful when GCC's default optimization strategies are suboptimal and + precise control over memory access patterns is required. + These barriers are not needed when using Clang or MSVC. + */ +#if YYJSON_IS_REAL_GCC +# define gcc_load_barrier(val) __asm__ volatile(""::"m"(val)) +# define gcc_store_barrier(val) __asm__ volatile("":"=m"(val)) +# define gcc_full_barrier(val) __asm__ volatile("":"=m"(val):"m"(val)) +#else +# define gcc_load_barrier(val) +# define gcc_store_barrier(val) +# define gcc_full_barrier(val) +#endif + + + +/*============================================================================== + * MARK: - Constants (Private) + *============================================================================*/ + +/* Common error messages. */ +#define MSG_FOPEN "failed to open file" +#define MSG_FREAD "failed to read file" +#define MSG_FWRITE "failed to write file" +#define MSG_FCLOSE "failed to close file" +#define MSG_MALLOC "failed to allocate memory" +#define MSG_CHAR_T "invalid literal, expected 'true'" +#define MSG_CHAR_F "invalid literal, expected 'false'" +#define MSG_CHAR_N "invalid literal, expected 'null'" +#define MSG_CHAR "unexpected character, expected a JSON value" +#define MSG_ARR_END "unexpected character, expected ',' or ']'" +#define MSG_OBJ_KEY "unexpected character, expected a string key" +#define MSG_OBJ_SEP "unexpected character, expected ':' after key" +#define MSG_OBJ_END "unexpected character, expected ',' or '}'" +#define MSG_GARBAGE "unexpected content after document" +#define MSG_NOT_END "unexpected end of data" +#define MSG_COMMENT "unclosed multiline comment" +#define MSG_COMMA "trailing comma is not allowed" +#define MSG_NAN_INF "nan or inf number is not allowed" +#define MSG_ERR_TYPE "invalid JSON value type" +#define MSG_ERR_BOM "UTF-8 byte order mark (BOM) is not supported" +#define MSG_ERR_UTF8 "invalid utf-8 encoding in string" +#define MSG_ERR_UTF16 "UTF-16 encoding is not supported" +#define MSG_ERR_UTF32 "UTF-32 encoding is not supported" + +/* U64 constant values */ +#undef U64_MAX +#define U64_MAX U64(0xFFFFFFFF, 0xFFFFFFFF) +#undef I64_MAX +#define I64_MAX U64(0x7FFFFFFF, 0xFFFFFFFF) +#undef USIZE_MAX +#define USIZE_MAX ((usize)(~(usize)0)) + +/* Maximum number of digits for reading u32/u64/usize safety (not overflow). */ +#undef U32_SAFE_DIG +#define U32_SAFE_DIG 9 /* u32 max is 4294967295, 10 digits */ +#undef U64_SAFE_DIG +#define U64_SAFE_DIG 19 /* u64 max is 18446744073709551615, 20 digits */ +#undef USIZE_SAFE_DIG +#define USIZE_SAFE_DIG (sizeof(usize) == 8 ? U64_SAFE_DIG : U32_SAFE_DIG) + +/* Inf bits (positive) */ +#define F64_BITS_INF U64(0x7FF00000, 0x00000000) + +/* NaN bits (quiet NaN, no payload, no sign) */ +#if defined(__hppa__) || (defined(__mips__) && !defined(__mips_nan2008)) +#define F64_BITS_NAN U64(0x7FF7FFFF, 0xFFFFFFFF) +#else +#define F64_BITS_NAN U64(0x7FF80000, 0x00000000) +#endif + +/* maximum significant digits count in decimal when reading double number */ +#define F64_MAX_DEC_DIG 768 + +/* maximum decimal power of double number (1.7976931348623157e308) */ +#define F64_MAX_DEC_EXP 308 + +/* minimum decimal power of double number (4.9406564584124654e-324) */ +#define F64_MIN_DEC_EXP (-324) + +/* maximum binary power of double number */ +#define F64_MAX_BIN_EXP 1024 + +/* minimum binary power of double number */ +#define F64_MIN_BIN_EXP (-1021) + +/* float/double number bits */ +#define F32_BITS 32 +#define F64_BITS 64 + +/* float/double number exponent part bits */ +#define F32_EXP_BITS 8 +#define F64_EXP_BITS 11 + +/* float/double number significand part bits */ +#define F32_SIG_BITS 23 +#define F64_SIG_BITS 52 + +/* float/double number significand part bits (with 1 hidden bit) */ +#define F32_SIG_FULL_BITS 24 +#define F64_SIG_FULL_BITS 53 + +/* float/double number significand bit mask */ +#define F32_SIG_MASK U32(0x007FFFFF) +#define F64_SIG_MASK U64(0x000FFFFF, 0xFFFFFFFF) + +/* float/double number exponent bit mask */ +#define F32_EXP_MASK U32(0x7F800000) +#define F64_EXP_MASK U64(0x7FF00000, 0x00000000) + +/* float/double number exponent bias */ +#define F32_EXP_BIAS 127 +#define F64_EXP_BIAS 1023 + +/* float/double number significant digits count in decimal */ +#define F32_DEC_DIG 9 +#define F64_DEC_DIG 17 + +/* buffer length required for float/double number writer */ +#define FP_BUF_LEN 40 + +/* maximum length of a number in incremental parsing */ +#define INCR_NUM_MAX_LEN 1024 + + + +/*============================================================================== + * MARK: - Types (Private) + *============================================================================*/ + +/** Type define for primitive types. */ +typedef float f32; +typedef double f64; +typedef int8_t i8; +typedef uint8_t u8; +typedef int16_t i16; +typedef uint16_t u16; +typedef int32_t i32; +typedef uint32_t u32; +typedef int64_t i64; +typedef uint64_t u64; +typedef size_t usize; + +/** 128-bit integer, used by floating-point number reader and writer. */ +#if YYJSON_HAS_INT128 +__extension__ typedef __int128 i128; +__extension__ typedef unsigned __int128 u128; +#endif + +/** 16/32/64-bit vector */ +typedef struct v16 { char c[2]; } v16; +typedef struct v32 { char c[4]; } v32; +typedef struct v64 { char c[8]; } v64; + +/** 16/32/64-bit vector union */ +typedef union v16_uni { v16 v; u16 u; } v16_uni; +typedef union v32_uni { v32 v; u32 u; } v32_uni; +typedef union v64_uni { v64 v; u64 u; } v64_uni; + + + +/*============================================================================== + * MARK: - Load/Store Utils (Private) + *============================================================================*/ + +#define byte_move_idx(x) ((char *)dst)[x] = ((const char *)src)[x]; +#define byte_move_src(x) ((char *)tmp)[x] = ((const char *)src)[x]; +#define byte_move_dst(x) ((char *)dst)[x] = ((const char *)tmp)[x]; + +/** Same as `memcpy(dst, src, 2)`, no overlap. */ +static_inline void byte_copy_2(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + memcpy(dst, src, 2); +#else + repeat2_incr(byte_move_idx) +#endif +} + +/** Same as `memcpy(dst, src, 4)`, no overlap. */ +static_inline void byte_copy_4(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + memcpy(dst, src, 4); +#else + repeat4_incr(byte_move_idx) +#endif +} + +/** Same as `memcpy(dst, src, 8)`, no overlap. */ +static_inline void byte_copy_8(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + memcpy(dst, src, 8); +#else + repeat8_incr(byte_move_idx) +#endif +} + +/** Same as `memcpy(dst, src, 16)`, no overlap. */ +static_inline void byte_copy_16(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + memcpy(dst, src, 16); +#else + repeat16_incr(byte_move_idx) +#endif +} + +/** Same as `memmove(dst, src, 2)`, allows overlap. */ +static_inline void byte_move_2(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + u16 tmp; + memcpy(&tmp, src, 2); + memcpy(dst, &tmp, 2); +#else + char tmp[2]; + repeat2_incr(byte_move_src) + repeat2_incr(byte_move_dst) +#endif +} + +/** Same as `memmove(dst, src, 4)`, allows overlap. */ +static_inline void byte_move_4(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + u32 tmp; + memcpy(&tmp, src, 4); + memcpy(dst, &tmp, 4); +#else + char tmp[4]; + repeat4_incr(byte_move_src) + repeat4_incr(byte_move_dst) +#endif +} + +/** Same as `memmove(dst, src, 8)`, allows overlap. */ +static_inline void byte_move_8(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + u64 tmp; + memcpy(&tmp, src, 8); + memcpy(dst, &tmp, 8); +#else + char tmp[8]; + repeat8_incr(byte_move_src) + repeat8_incr(byte_move_dst) +#endif +} + +/** Same as `memmove(dst, src, 16)`, allows overlap. */ +static_inline void byte_move_16(void *dst, const void *src) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + char *pdst = (char *)dst; + const char *psrc = (const char *)src; + u64 tmp1, tmp2; + memcpy(&tmp1, psrc, 8); + memcpy(&tmp2, psrc + 8, 8); + memcpy(pdst, &tmp1, 8); + memcpy(pdst + 8, &tmp2, 8); +#else + char tmp[16]; + repeat16_incr(byte_move_src) + repeat16_incr(byte_move_dst) +#endif +} + +/** Same as `memmove(dst, src, n)`, but only `dst <= src` and `n <= 16`. */ +static_inline void byte_move_forward(void *dst, void *src, usize n) { + char *d = (char *)dst, *s = (char *)src; + n += (n % 2); /* round up to even */ + if (n == 16) { byte_move_16(d, s); return; } + if (n >= 8) { byte_move_8(d, s); n -= 8; d += 8; s += 8; } + if (n >= 4) { byte_move_4(d, s); n -= 4; d += 4; s += 4; } + if (n >= 2) { byte_move_2(d, s); } +} + +/** Same as `memcmp(buf, pat, 2) == 0`. */ +static_inline bool byte_match_2(void *buf, const char *pat) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + v16_uni u1, u2; + memcpy(&u1, buf, 2); + memcpy(&u2, pat, 2); + return u1.u == u2.u; +#else + return ((char *)buf)[0] == ((const char *)pat)[0] && + ((char *)buf)[1] == ((const char *)pat)[1]; +#endif +} + +/** Same as `memcmp(buf, pat, 4) == 0`. */ +static_inline bool byte_match_4(void *buf, const char *pat) { +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + v32_uni u1, u2; + memcpy(&u1, buf, 4); + memcpy(&u2, pat, 4); + return u1.u == u2.u; +#else + return ((char *)buf)[0] == ((const char *)pat)[0] && + ((char *)buf)[1] == ((const char *)pat)[1] && + ((char *)buf)[2] == ((const char *)pat)[2] && + ((char *)buf)[3] == ((const char *)pat)[3]; +#endif +} + +/** Loads 2 bytes from `src` as a u16 (native-endian). */ +static_inline u16 byte_load_2(const void *src) { + v16_uni uni; +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + memcpy(&uni, src, 2); +#else + uni.v.c[0] = ((const char *)src)[0]; + uni.v.c[1] = ((const char *)src)[1]; +#endif + return uni.u; +} + +/** Loads 3 bytes from `src` as a u32 (native-endian). */ +static_inline u32 byte_load_3(const void *src) { + v32_uni uni; +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + memcpy(&uni, src, 2); + uni.v.c[2] = ((const char *)src)[2]; + uni.v.c[3] = 0; +#else + uni.v.c[0] = ((const char *)src)[0]; + uni.v.c[1] = ((const char *)src)[1]; + uni.v.c[2] = ((const char *)src)[2]; + uni.v.c[3] = 0; +#endif + return uni.u; +} + +/** Loads 4 bytes from `src` as a u32 (native-endian). */ +static_inline u32 byte_load_4(const void *src) { + v32_uni uni; +#if !YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS + memcpy(&uni, src, 4); +#else + uni.v.c[0] = ((const char *)src)[0]; + uni.v.c[1] = ((const char *)src)[1]; + uni.v.c[2] = ((const char *)src)[2]; + uni.v.c[3] = ((const char *)src)[3]; +#endif + return uni.u; +} + + + +/*============================================================================== + * MARK: - Character Utils (Private) + * These lookup tables were generated by `misc/make_tables.c`. + *============================================================================*/ + +/* char_table1 */ +#define CHAR_TYPE_ASCII (1 << 0) /* Except: ["\], [0x00-0x1F, 0x80-0xFF] */ +#define CHAR_TYPE_ASCII_SQ (1 << 1) /* Except: ['\], [0x00-0x1F, 0x80-0xFF] */ +#define CHAR_TYPE_SPACE (1 << 2) /* Whitespace: [ \t\n\r] */ +#define CHAR_TYPE_SPACE_EXT (1 << 3) /* Whitespace: [ \t\n\r\v\f], JSON5 */ +#define CHAR_TYPE_NUM (1 << 4) /* Number: [.-+0-9] */ +#define CHAR_TYPE_COMMENT (1 << 5) /* Comment: [/] */ + +/* char_table2 */ +#define CHAR_TYPE_EOL (1 << 0) /* End of line: [\r\n] */ +#define CHAR_TYPE_EOL_EXT (1 << 1) /* End of line: [\r\n], JSON5 */ +#define CHAR_TYPE_ID_START (1 << 2) /* ID start: [_$A-Za-z\], U+0080+ */ +#define CHAR_TYPE_ID_NEXT (1 << 3) /* ID next: [_$A-Za-z0-9\], U+0080+ */ +#define CHAR_TYPE_ID_ASCII (1 << 4) /* ID next ASCII: [_$A-Za-z0-9] */ + +/* char_table3 */ +#define CHAR_TYPE_SIGN (1 << 0) /* [-+] */ +#define CHAR_TYPE_DIGIT (1 << 1) /* [0-9] */ +#define CHAR_TYPE_NONZERO (1 << 2) /* [1-9] */ +#define CHAR_TYPE_EXP (1 << 3) /* [eE] */ +#define CHAR_TYPE_DOT (1 << 4) /* [.] */ + +static const u8 char_table1[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0C, 0x0C, 0x08, 0x08, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x03, 0x02, 0x03, 0x03, 0x03, 0x03, 0x01, + 0x03, 0x03, 0x03, 0x13, 0x03, 0x13, 0x13, 0x23, + 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, 0x13, + 0x13, 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const u8 char_table2[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x00, 0x0C, 0x00, 0x00, 0x1C, + 0x00, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, + 0x1C, 0x1C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C +}; + +static const u8 char_table3[256] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x10, 0x00, + 0x02, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** Match a whitespace: [ \t\n\r]. */ +static_inline bool char_is_space(u8 c) { + return !!(char_table1[c] & CHAR_TYPE_SPACE); +} + +/** Match an extended whitespace: [ \t\n\r\\v\\f], JSON5 whitespace. */ +static_inline bool char_is_space_ext(u8 c) { + return !!(char_table1[c] & CHAR_TYPE_SPACE_EXT); +} + +/** Match a JSON number: [.-+0-9]. */ +static_inline bool char_is_num(u8 c) { + return !!(char_table1[c] & CHAR_TYPE_NUM); +} + +/** Match an ASCII character in string: ["\], [0x00-0x1F, 0x80-0xFF]. */ +static_inline bool char_is_ascii_skip(u8 c) { + return !!(char_table1[c] & CHAR_TYPE_ASCII); +} + +/** Match an ASCII character single-quoted: ['\], [0x00-0x1F, 0x80-0xFF]. */ +static_inline bool char_is_ascii_skip_sq(u8 c) { + return !!(char_table1[c] & CHAR_TYPE_ASCII_SQ); +} + +/** Match a trivia character: extended whitespace or comment. */ +static_inline bool char_is_trivia(u8 c) { + return !!(char_table1[c] & (CHAR_TYPE_SPACE_EXT | CHAR_TYPE_COMMENT)); +} + +/** Match a line end character: [\r\n]. */ +static_inline bool char_is_eol(u8 c) { + return !!(char_table2[c] & CHAR_TYPE_EOL); +} + +/** Match an extended line end character: [\r\n], JSON5 line terminator. */ +static_inline bool char_is_eol_ext(u8 c) { + return !!(char_table2[c] & CHAR_TYPE_EOL_EXT); +} + +/** Match an identifier name start: [_$A-Za-z\], U+0080+. */ +static_inline bool char_is_id_start(u8 c) { + return !!(char_table2[c] & CHAR_TYPE_ID_START); +} + +/** Match an identifier name next: [_$A-Za-z0-9\], U+0080+. */ +static_inline bool char_is_id_next(u8 c) { + return !!(char_table2[c] & CHAR_TYPE_ID_NEXT); +} + +/** Match an identifier name ASCII: [_$A-Za-z0-9]. */ +static_inline bool char_is_id_ascii(u8 c) { + return !!(char_table2[c] & CHAR_TYPE_ID_ASCII); +} + +/** Match a sign: [+-] */ +static_inline bool char_is_sign(u8 d) { + return !!(char_table3[d] & CHAR_TYPE_SIGN); +} + +/** Match a none-zero digit: [1-9] */ +static_inline bool char_is_nonzero(u8 d) { + return !!(char_table3[d] & CHAR_TYPE_NONZERO); +} + +/** Match a digit: [0-9] */ +static_inline bool char_is_digit(u8 d) { + return !!(char_table3[d] & CHAR_TYPE_DIGIT); +} + +/** Match an exponent sign: [eE]. */ +static_inline bool char_is_exp(u8 d) { + return !!(char_table3[d] & CHAR_TYPE_EXP); +} + +/** Match a floating point indicator: [.eE]. */ +static_inline bool char_is_fp(u8 d) { + return !!(char_table3[d] & (CHAR_TYPE_DOT | CHAR_TYPE_EXP)); +} + +/** Match a digit or floating point indicator: [0-9.eE]. */ +static_inline bool char_is_digit_or_fp(u8 d) { + return !!(char_table3[d] & (CHAR_TYPE_DIGIT | CHAR_TYPE_DOT | + CHAR_TYPE_EXP)); +} + +/** Match a JSON container: `{` or `[`. */ +static_inline bool char_is_ctn(u8 c) { + return (c & 0xDF) == 0x5B; /* '[': 0x5B, '{': 0x7B */ +} + +/** Convert ASCII letter to lowercase; valid only for [A-Za-z]. */ +static_inline u8 char_to_lower(u8 c) { + return c | 0x20; +} + +/** Match UTF-8 byte order mask. */ +static_inline bool is_utf8_bom(const u8 *cur) { + return byte_load_3(cur) == byte_load_3("\xEF\xBB\xBF"); +} + +/** Match UTF-16 byte order mask. */ +static_inline bool is_utf16_bom(const u8 *cur) { + return byte_load_2(cur) == byte_load_2("\xFE\xFF") || + byte_load_2(cur) == byte_load_2("\xFF\xFE"); +} + +/** Match UTF-32 byte order mask, need length check to avoid zero padding. */ +static_inline bool is_utf32_bom(const u8 *cur) { + return byte_load_4(cur) == byte_load_4("\x00\x00\xFE\xFF") || + byte_load_4(cur) == byte_load_4("\xFF\xFE\x00\x00"); +} + +/** Get the extended line end length. Used with `char_is_eol_ext`. */ +static_inline usize ext_eol_len(const u8 *cur) { + if (cur[0] < 0x80) return 1; + if (cur[1] == 0x80 && (cur[2] == 0xA8 || cur[2] == 0xA9)) return 3; + return 0; +} + +/** Get the extended whitespace length. Used with `char_is_space_ext`. */ +static_inline usize ext_space_len(const u8 *cur) { + if (cur[0] < 0x80) { + return 1; + } else if (byte_load_2(cur) == byte_load_2("\xC2\xA0")) { + return 2; + } else if (byte_load_2(cur) == byte_load_2("\xE2\x80")) { + if (cur[2] >= 0x80 && cur[2] <= 0x8A) return 3; + if (cur[2] == 0xA8 || cur[2] == 0xA9 || cur[2] == 0xAF) return 3; + } else { + u32 uni = byte_load_3(cur); + if (uni == byte_load_3("\xE1\x9A\x80") || + uni == byte_load_3("\xE2\x81\x9F") || + uni == byte_load_3("\xE3\x80\x80") || + uni == byte_load_3("\xEF\xBB\xBF")) return 3; + } + return 0; +} + + + +/*============================================================================== + * MARK: - Hex Character Reader (Private) + * This function is used by JSON reader to read escaped characters. + *============================================================================*/ + +/** + This table is used to convert 4 hex character sequence to a number. + A valid hex character [0-9A-Fa-f] will mapped to it's raw number [0x00, 0x0F], + an invalid hex character will mapped to [0xF0]. + (generate with misc/make_tables.c) + */ +static const u8 hex_conv_table[256] = { + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0 +}; + +/** Load 4 hex characters to `u16`, return true on valid input. */ +static_inline bool hex_load_4(const u8 *src, u16 *dst) { + u16 c0 = hex_conv_table[src[0]]; + u16 c1 = hex_conv_table[src[1]]; + u16 c2 = hex_conv_table[src[2]]; + u16 c3 = hex_conv_table[src[3]]; + u16 t0 = (u16)((c0 << 8) | c2); + u16 t1 = (u16)((c1 << 8) | c3); + *dst = (u16)((t0 << 4) | t1); + return ((t0 | t1) & (u16)0xF0F0) == 0; +} + +/** Load 2 hex characters to `u8`, return true on valid input. */ +static_inline bool hex_load_2(const u8 *src, u8 *dst) { + u8 c0 = hex_conv_table[src[0]]; + u8 c1 = hex_conv_table[src[1]]; + *dst = (u8)((c0 << 4) | c1); + return ((c0 | c1) & 0xF0) == 0; +} + +/** Match a hexadecimal numeric character: [0-9a-fA-F]. */ +static_inline bool char_is_hex(u8 c) { + return hex_conv_table[c] != 0xF0; +} + + + +/*============================================================================== + * MARK: - UTF8 Validation (Private) + * Each Unicode code point is encoded using 1 to 4 bytes in UTF-8. + * Validation is performed using a 4-byte mask and pattern-based approach, + * which requires the input data to be padded with four zero bytes at the end. + *============================================================================*/ + +/* Macro for concatenating four u8 into a u32 and keeping the byte order. */ +#if YYJSON_ENDIAN == YYJSON_LITTLE_ENDIAN +# define utf8_seq_def(name, a, b, c, d) \ + static const u32 utf8_seq_##name = 0x##d##c##b##a##UL; +# define utf8_seq(name) utf8_seq_##name +#elif YYJSON_ENDIAN == YYJSON_BIG_ENDIAN +# define utf8_seq_def(name, a, b, c, d) \ + static const u32 utf8_seq_##name = 0x##a##b##c##d##UL; +# define utf8_seq(name) utf8_seq_##name +#else +# define utf8_seq_def(name, a, b, c, d) \ + static const v32_uni utf8_uni_##name = {{ 0x##a, 0x##b, 0x##c, 0x##d }}; +# define utf8_seq(name) utf8_uni_##name.u +#endif + +/* + 1-byte sequence (U+0000 to U+007F) + bit min [.......0] (U+0000) + bit max [.1111111] (U+007F) + bit mask [x.......] (80) + bit pattern [0.......] (00) + */ +utf8_seq_def(b1_mask, 80, 00, 00, 00) +utf8_seq_def(b1_patt, 00, 00, 00, 00) +#define is_utf8_seq1(uni) ( \ + ((uni & utf8_seq(b1_mask)) == utf8_seq(b1_patt)) ) + +/* + 2-byte sequence (U+0080 to U+07FF) + bit min [......10 ..000000] (U+0080) + bit max [...11111 ..111111] (U+07FF) + bit mask [xxx..... xx......] (E0 C0) + bit pattern [110..... 10......] (C0 80) + bit require [...xxxx. ........] (1E 00) + */ +utf8_seq_def(b2_mask, E0, C0, 00, 00) +utf8_seq_def(b2_patt, C0, 80, 00, 00) +utf8_seq_def(b2_requ, 1E, 00, 00, 00) +#define is_utf8_seq2(uni) ( \ + ((uni & utf8_seq(b2_mask)) == utf8_seq(b2_patt)) && \ + ((uni & utf8_seq(b2_requ))) ) + +/* + 3-byte sequence (U+0800 to U+FFFF) + bit min [........ ..100000 ..000000] (U+0800) + bit max [....1111 ..111111 ..111111] (U+FFFF) + bit mask [xxxx.... xx...... xx......] (F0 C0 C0) + bit pattern [1110.... 10...... 10......] (E0 80 80) + bit require [....xxxx ..x..... ........] (0F 20 00) + + 3-byte invalid sequence, reserved for surrogate halves (U+D800 to U+DFFF) + bit min [....1101 ..100000 ..000000] (U+D800) + bit max [....1101 ..111111 ..111111] (U+DFFF) + bit mask [....xxxx ..x..... ........] (0F 20 00) + bit pattern [....1101 ..1..... ........] (0D 20 00) + */ +utf8_seq_def(b3_mask, F0, C0, C0, 00) +utf8_seq_def(b3_patt, E0, 80, 80, 00) +utf8_seq_def(b3_requ, 0F, 20, 00, 00) +utf8_seq_def(b3_erro, 0D, 20, 00, 00) +#define is_utf8_seq3(uni) ( \ + ((uni & utf8_seq(b3_mask)) == utf8_seq(b3_patt)) && \ + ((tmp = (uni & utf8_seq(b3_requ)))) && \ + ((tmp != utf8_seq(b3_erro))) ) + +/* + 4-byte sequence (U+10000 to U+10FFFF) + bit min [........ ...10000 ..000000 ..000000] (U+10000) + bit max [.....100 ..001111 ..111111 ..111111] (U+10FFFF) + bit mask [xxxxx... xx...... xx...... xx......] (F8 C0 C0 C0) + bit pattern [11110... 10...... 10...... 10......] (F0 80 80 80) + bit require [.....xxx ..xx.... ........ ........] (07 30 00 00) + bit require 1 [.....x.. ........ ........ ........] (04 00 00 00) + bit require 2 [......xx ..xx.... ........ ........] (03 30 00 00) + */ +utf8_seq_def(b4_mask, F8, C0, C0, C0) +utf8_seq_def(b4_patt, F0, 80, 80, 80) +utf8_seq_def(b4_requ, 07, 30, 00, 00) +utf8_seq_def(b4_req1, 04, 00, 00, 00) +utf8_seq_def(b4_req2, 03, 30, 00, 00) +#define is_utf8_seq4(uni) ( \ + ((uni & utf8_seq(b4_mask)) == utf8_seq(b4_patt)) && \ + ((tmp = (uni & utf8_seq(b4_requ)))) && \ + ((tmp & utf8_seq(b4_req1)) == 0 || (tmp & utf8_seq(b4_req2)) == 0) ) + + + +/*============================================================================== + * MARK: - Power10 Lookup Table (Private) + * These data are used by the floating-point number reader and writer. + *============================================================================*/ + +#if !YYJSON_DISABLE_FAST_FP_CONV + +/** Maximum pow10 exponent that can be represented exactly as a float64. */ +#define F64_POW10_MAX_EXACT_EXP 22 + +/** Cached pow10 table. */ +static const f64 f64_pow10_table[F64_POW10_MAX_EXACT_EXP + 1] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, + 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22 +}; + +/** Maximum pow10 exponent that can be represented exactly as a uint64. */ +#define U64_POW10_MAX_EXACT_EXP 19 + +/** Table: [ 10^0, ..., 10^19 ] (generate with misc/make_tables.c) */ +static const u64 u64_pow10_table[U64_POW10_MAX_EXACT_EXP + 1] = { + U64(0x00000000, 0x00000001), U64(0x00000000, 0x0000000A), + U64(0x00000000, 0x00000064), U64(0x00000000, 0x000003E8), + U64(0x00000000, 0x00002710), U64(0x00000000, 0x000186A0), + U64(0x00000000, 0x000F4240), U64(0x00000000, 0x00989680), + U64(0x00000000, 0x05F5E100), U64(0x00000000, 0x3B9ACA00), + U64(0x00000002, 0x540BE400), U64(0x00000017, 0x4876E800), + U64(0x000000E8, 0xD4A51000), U64(0x00000918, 0x4E72A000), + U64(0x00005AF3, 0x107A4000), U64(0x00038D7E, 0xA4C68000), + U64(0x002386F2, 0x6FC10000), U64(0x01634578, 0x5D8A0000), + U64(0x0DE0B6B3, 0xA7640000), U64(0x8AC72304, 0x89E80000) +}; + +/** Minimum decimal exponent in pow10_sig_table. */ +#define POW10_SIG_TABLE_MIN_EXP -343 + +/** Maximum decimal exponent in pow10_sig_table. */ +#define POW10_SIG_TABLE_MAX_EXP 324 + +/** Minimum exact decimal exponent in pow10_sig_table */ +#define POW10_SIG_TABLE_MIN_EXACT_EXP 0 + +/** Maximum exact decimal exponent in pow10_sig_table */ +#define POW10_SIG_TABLE_MAX_EXACT_EXP 55 + +/** Normalized significant 128 bits of pow10, no rounded up (size: 10.4KB). + This lookup table is used by both the double number reader and writer. + (generate with misc/make_tables.c) */ +static const u64 pow10_sig_table[] = { + U64(0xBF29DCAB, 0xA82FDEAE), U64(0x7432EE87, 0x3880FC33), /* ~= 10^-343 */ + U64(0xEEF453D6, 0x923BD65A), U64(0x113FAA29, 0x06A13B3F), /* ~= 10^-342 */ + U64(0x9558B466, 0x1B6565F8), U64(0x4AC7CA59, 0xA424C507), /* ~= 10^-341 */ + U64(0xBAAEE17F, 0xA23EBF76), U64(0x5D79BCF0, 0x0D2DF649), /* ~= 10^-340 */ + U64(0xE95A99DF, 0x8ACE6F53), U64(0xF4D82C2C, 0x107973DC), /* ~= 10^-339 */ + U64(0x91D8A02B, 0xB6C10594), U64(0x79071B9B, 0x8A4BE869), /* ~= 10^-338 */ + U64(0xB64EC836, 0xA47146F9), U64(0x9748E282, 0x6CDEE284), /* ~= 10^-337 */ + U64(0xE3E27A44, 0x4D8D98B7), U64(0xFD1B1B23, 0x08169B25), /* ~= 10^-336 */ + U64(0x8E6D8C6A, 0xB0787F72), U64(0xFE30F0F5, 0xE50E20F7), /* ~= 10^-335 */ + U64(0xB208EF85, 0x5C969F4F), U64(0xBDBD2D33, 0x5E51A935), /* ~= 10^-334 */ + U64(0xDE8B2B66, 0xB3BC4723), U64(0xAD2C7880, 0x35E61382), /* ~= 10^-333 */ + U64(0x8B16FB20, 0x3055AC76), U64(0x4C3BCB50, 0x21AFCC31), /* ~= 10^-332 */ + U64(0xADDCB9E8, 0x3C6B1793), U64(0xDF4ABE24, 0x2A1BBF3D), /* ~= 10^-331 */ + U64(0xD953E862, 0x4B85DD78), U64(0xD71D6DAD, 0x34A2AF0D), /* ~= 10^-330 */ + U64(0x87D4713D, 0x6F33AA6B), U64(0x8672648C, 0x40E5AD68), /* ~= 10^-329 */ + U64(0xA9C98D8C, 0xCB009506), U64(0x680EFDAF, 0x511F18C2), /* ~= 10^-328 */ + U64(0xD43BF0EF, 0xFDC0BA48), U64(0x0212BD1B, 0x2566DEF2), /* ~= 10^-327 */ + U64(0x84A57695, 0xFE98746D), U64(0x014BB630, 0xF7604B57), /* ~= 10^-326 */ + U64(0xA5CED43B, 0x7E3E9188), U64(0x419EA3BD, 0x35385E2D), /* ~= 10^-325 */ + U64(0xCF42894A, 0x5DCE35EA), U64(0x52064CAC, 0x828675B9), /* ~= 10^-324 */ + U64(0x818995CE, 0x7AA0E1B2), U64(0x7343EFEB, 0xD1940993), /* ~= 10^-323 */ + U64(0xA1EBFB42, 0x19491A1F), U64(0x1014EBE6, 0xC5F90BF8), /* ~= 10^-322 */ + U64(0xCA66FA12, 0x9F9B60A6), U64(0xD41A26E0, 0x77774EF6), /* ~= 10^-321 */ + U64(0xFD00B897, 0x478238D0), U64(0x8920B098, 0x955522B4), /* ~= 10^-320 */ + U64(0x9E20735E, 0x8CB16382), U64(0x55B46E5F, 0x5D5535B0), /* ~= 10^-319 */ + U64(0xC5A89036, 0x2FDDBC62), U64(0xEB2189F7, 0x34AA831D), /* ~= 10^-318 */ + U64(0xF712B443, 0xBBD52B7B), U64(0xA5E9EC75, 0x01D523E4), /* ~= 10^-317 */ + U64(0x9A6BB0AA, 0x55653B2D), U64(0x47B233C9, 0x2125366E), /* ~= 10^-316 */ + U64(0xC1069CD4, 0xEABE89F8), U64(0x999EC0BB, 0x696E840A), /* ~= 10^-315 */ + U64(0xF148440A, 0x256E2C76), U64(0xC00670EA, 0x43CA250D), /* ~= 10^-314 */ + U64(0x96CD2A86, 0x5764DBCA), U64(0x38040692, 0x6A5E5728), /* ~= 10^-313 */ + U64(0xBC807527, 0xED3E12BC), U64(0xC6050837, 0x04F5ECF2), /* ~= 10^-312 */ + U64(0xEBA09271, 0xE88D976B), U64(0xF7864A44, 0xC633682E), /* ~= 10^-311 */ + U64(0x93445B87, 0x31587EA3), U64(0x7AB3EE6A, 0xFBE0211D), /* ~= 10^-310 */ + U64(0xB8157268, 0xFDAE9E4C), U64(0x5960EA05, 0xBAD82964), /* ~= 10^-309 */ + U64(0xE61ACF03, 0x3D1A45DF), U64(0x6FB92487, 0x298E33BD), /* ~= 10^-308 */ + U64(0x8FD0C162, 0x06306BAB), U64(0xA5D3B6D4, 0x79F8E056), /* ~= 10^-307 */ + U64(0xB3C4F1BA, 0x87BC8696), U64(0x8F48A489, 0x9877186C), /* ~= 10^-306 */ + U64(0xE0B62E29, 0x29ABA83C), U64(0x331ACDAB, 0xFE94DE87), /* ~= 10^-305 */ + U64(0x8C71DCD9, 0xBA0B4925), U64(0x9FF0C08B, 0x7F1D0B14), /* ~= 10^-304 */ + U64(0xAF8E5410, 0x288E1B6F), U64(0x07ECF0AE, 0x5EE44DD9), /* ~= 10^-303 */ + U64(0xDB71E914, 0x32B1A24A), U64(0xC9E82CD9, 0xF69D6150), /* ~= 10^-302 */ + U64(0x892731AC, 0x9FAF056E), U64(0xBE311C08, 0x3A225CD2), /* ~= 10^-301 */ + U64(0xAB70FE17, 0xC79AC6CA), U64(0x6DBD630A, 0x48AAF406), /* ~= 10^-300 */ + U64(0xD64D3D9D, 0xB981787D), U64(0x092CBBCC, 0xDAD5B108), /* ~= 10^-299 */ + U64(0x85F04682, 0x93F0EB4E), U64(0x25BBF560, 0x08C58EA5), /* ~= 10^-298 */ + U64(0xA76C5823, 0x38ED2621), U64(0xAF2AF2B8, 0x0AF6F24E), /* ~= 10^-297 */ + U64(0xD1476E2C, 0x07286FAA), U64(0x1AF5AF66, 0x0DB4AEE1), /* ~= 10^-296 */ + U64(0x82CCA4DB, 0x847945CA), U64(0x50D98D9F, 0xC890ED4D), /* ~= 10^-295 */ + U64(0xA37FCE12, 0x6597973C), U64(0xE50FF107, 0xBAB528A0), /* ~= 10^-294 */ + U64(0xCC5FC196, 0xFEFD7D0C), U64(0x1E53ED49, 0xA96272C8), /* ~= 10^-293 */ + U64(0xFF77B1FC, 0xBEBCDC4F), U64(0x25E8E89C, 0x13BB0F7A), /* ~= 10^-292 */ + U64(0x9FAACF3D, 0xF73609B1), U64(0x77B19161, 0x8C54E9AC), /* ~= 10^-291 */ + U64(0xC795830D, 0x75038C1D), U64(0xD59DF5B9, 0xEF6A2417), /* ~= 10^-290 */ + U64(0xF97AE3D0, 0xD2446F25), U64(0x4B057328, 0x6B44AD1D), /* ~= 10^-289 */ + U64(0x9BECCE62, 0x836AC577), U64(0x4EE367F9, 0x430AEC32), /* ~= 10^-288 */ + U64(0xC2E801FB, 0x244576D5), U64(0x229C41F7, 0x93CDA73F), /* ~= 10^-287 */ + U64(0xF3A20279, 0xED56D48A), U64(0x6B435275, 0x78C1110F), /* ~= 10^-286 */ + U64(0x9845418C, 0x345644D6), U64(0x830A1389, 0x6B78AAA9), /* ~= 10^-285 */ + U64(0xBE5691EF, 0x416BD60C), U64(0x23CC986B, 0xC656D553), /* ~= 10^-284 */ + U64(0xEDEC366B, 0x11C6CB8F), U64(0x2CBFBE86, 0xB7EC8AA8), /* ~= 10^-283 */ + U64(0x94B3A202, 0xEB1C3F39), U64(0x7BF7D714, 0x32F3D6A9), /* ~= 10^-282 */ + U64(0xB9E08A83, 0xA5E34F07), U64(0xDAF5CCD9, 0x3FB0CC53), /* ~= 10^-281 */ + U64(0xE858AD24, 0x8F5C22C9), U64(0xD1B3400F, 0x8F9CFF68), /* ~= 10^-280 */ + U64(0x91376C36, 0xD99995BE), U64(0x23100809, 0xB9C21FA1), /* ~= 10^-279 */ + U64(0xB5854744, 0x8FFFFB2D), U64(0xABD40A0C, 0x2832A78A), /* ~= 10^-278 */ + U64(0xE2E69915, 0xB3FFF9F9), U64(0x16C90C8F, 0x323F516C), /* ~= 10^-277 */ + U64(0x8DD01FAD, 0x907FFC3B), U64(0xAE3DA7D9, 0x7F6792E3), /* ~= 10^-276 */ + U64(0xB1442798, 0xF49FFB4A), U64(0x99CD11CF, 0xDF41779C), /* ~= 10^-275 */ + U64(0xDD95317F, 0x31C7FA1D), U64(0x40405643, 0xD711D583), /* ~= 10^-274 */ + U64(0x8A7D3EEF, 0x7F1CFC52), U64(0x482835EA, 0x666B2572), /* ~= 10^-273 */ + U64(0xAD1C8EAB, 0x5EE43B66), U64(0xDA324365, 0x0005EECF), /* ~= 10^-272 */ + U64(0xD863B256, 0x369D4A40), U64(0x90BED43E, 0x40076A82), /* ~= 10^-271 */ + U64(0x873E4F75, 0xE2224E68), U64(0x5A7744A6, 0xE804A291), /* ~= 10^-270 */ + U64(0xA90DE353, 0x5AAAE202), U64(0x711515D0, 0xA205CB36), /* ~= 10^-269 */ + U64(0xD3515C28, 0x31559A83), U64(0x0D5A5B44, 0xCA873E03), /* ~= 10^-268 */ + U64(0x8412D999, 0x1ED58091), U64(0xE858790A, 0xFE9486C2), /* ~= 10^-267 */ + U64(0xA5178FFF, 0x668AE0B6), U64(0x626E974D, 0xBE39A872), /* ~= 10^-266 */ + U64(0xCE5D73FF, 0x402D98E3), U64(0xFB0A3D21, 0x2DC8128F), /* ~= 10^-265 */ + U64(0x80FA687F, 0x881C7F8E), U64(0x7CE66634, 0xBC9D0B99), /* ~= 10^-264 */ + U64(0xA139029F, 0x6A239F72), U64(0x1C1FFFC1, 0xEBC44E80), /* ~= 10^-263 */ + U64(0xC9874347, 0x44AC874E), U64(0xA327FFB2, 0x66B56220), /* ~= 10^-262 */ + U64(0xFBE91419, 0x15D7A922), U64(0x4BF1FF9F, 0x0062BAA8), /* ~= 10^-261 */ + U64(0x9D71AC8F, 0xADA6C9B5), U64(0x6F773FC3, 0x603DB4A9), /* ~= 10^-260 */ + U64(0xC4CE17B3, 0x99107C22), U64(0xCB550FB4, 0x384D21D3), /* ~= 10^-259 */ + U64(0xF6019DA0, 0x7F549B2B), U64(0x7E2A53A1, 0x46606A48), /* ~= 10^-258 */ + U64(0x99C10284, 0x4F94E0FB), U64(0x2EDA7444, 0xCBFC426D), /* ~= 10^-257 */ + U64(0xC0314325, 0x637A1939), U64(0xFA911155, 0xFEFB5308), /* ~= 10^-256 */ + U64(0xF03D93EE, 0xBC589F88), U64(0x793555AB, 0x7EBA27CA), /* ~= 10^-255 */ + U64(0x96267C75, 0x35B763B5), U64(0x4BC1558B, 0x2F3458DE), /* ~= 10^-254 */ + U64(0xBBB01B92, 0x83253CA2), U64(0x9EB1AAED, 0xFB016F16), /* ~= 10^-253 */ + U64(0xEA9C2277, 0x23EE8BCB), U64(0x465E15A9, 0x79C1CADC), /* ~= 10^-252 */ + U64(0x92A1958A, 0x7675175F), U64(0x0BFACD89, 0xEC191EC9), /* ~= 10^-251 */ + U64(0xB749FAED, 0x14125D36), U64(0xCEF980EC, 0x671F667B), /* ~= 10^-250 */ + U64(0xE51C79A8, 0x5916F484), U64(0x82B7E127, 0x80E7401A), /* ~= 10^-249 */ + U64(0x8F31CC09, 0x37AE58D2), U64(0xD1B2ECB8, 0xB0908810), /* ~= 10^-248 */ + U64(0xB2FE3F0B, 0x8599EF07), U64(0x861FA7E6, 0xDCB4AA15), /* ~= 10^-247 */ + U64(0xDFBDCECE, 0x67006AC9), U64(0x67A791E0, 0x93E1D49A), /* ~= 10^-246 */ + U64(0x8BD6A141, 0x006042BD), U64(0xE0C8BB2C, 0x5C6D24E0), /* ~= 10^-245 */ + U64(0xAECC4991, 0x4078536D), U64(0x58FAE9F7, 0x73886E18), /* ~= 10^-244 */ + U64(0xDA7F5BF5, 0x90966848), U64(0xAF39A475, 0x506A899E), /* ~= 10^-243 */ + U64(0x888F9979, 0x7A5E012D), U64(0x6D8406C9, 0x52429603), /* ~= 10^-242 */ + U64(0xAAB37FD7, 0xD8F58178), U64(0xC8E5087B, 0xA6D33B83), /* ~= 10^-241 */ + U64(0xD5605FCD, 0xCF32E1D6), U64(0xFB1E4A9A, 0x90880A64), /* ~= 10^-240 */ + U64(0x855C3BE0, 0xA17FCD26), U64(0x5CF2EEA0, 0x9A55067F), /* ~= 10^-239 */ + U64(0xA6B34AD8, 0xC9DFC06F), U64(0xF42FAA48, 0xC0EA481E), /* ~= 10^-238 */ + U64(0xD0601D8E, 0xFC57B08B), U64(0xF13B94DA, 0xF124DA26), /* ~= 10^-237 */ + U64(0x823C1279, 0x5DB6CE57), U64(0x76C53D08, 0xD6B70858), /* ~= 10^-236 */ + U64(0xA2CB1717, 0xB52481ED), U64(0x54768C4B, 0x0C64CA6E), /* ~= 10^-235 */ + U64(0xCB7DDCDD, 0xA26DA268), U64(0xA9942F5D, 0xCF7DFD09), /* ~= 10^-234 */ + U64(0xFE5D5415, 0x0B090B02), U64(0xD3F93B35, 0x435D7C4C), /* ~= 10^-233 */ + U64(0x9EFA548D, 0x26E5A6E1), U64(0xC47BC501, 0x4A1A6DAF), /* ~= 10^-232 */ + U64(0xC6B8E9B0, 0x709F109A), U64(0x359AB641, 0x9CA1091B), /* ~= 10^-231 */ + U64(0xF867241C, 0x8CC6D4C0), U64(0xC30163D2, 0x03C94B62), /* ~= 10^-230 */ + U64(0x9B407691, 0xD7FC44F8), U64(0x79E0DE63, 0x425DCF1D), /* ~= 10^-229 */ + U64(0xC2109436, 0x4DFB5636), U64(0x985915FC, 0x12F542E4), /* ~= 10^-228 */ + U64(0xF294B943, 0xE17A2BC4), U64(0x3E6F5B7B, 0x17B2939D), /* ~= 10^-227 */ + U64(0x979CF3CA, 0x6CEC5B5A), U64(0xA705992C, 0xEECF9C42), /* ~= 10^-226 */ + U64(0xBD8430BD, 0x08277231), U64(0x50C6FF78, 0x2A838353), /* ~= 10^-225 */ + U64(0xECE53CEC, 0x4A314EBD), U64(0xA4F8BF56, 0x35246428), /* ~= 10^-224 */ + U64(0x940F4613, 0xAE5ED136), U64(0x871B7795, 0xE136BE99), /* ~= 10^-223 */ + U64(0xB9131798, 0x99F68584), U64(0x28E2557B, 0x59846E3F), /* ~= 10^-222 */ + U64(0xE757DD7E, 0xC07426E5), U64(0x331AEADA, 0x2FE589CF), /* ~= 10^-221 */ + U64(0x9096EA6F, 0x3848984F), U64(0x3FF0D2C8, 0x5DEF7621), /* ~= 10^-220 */ + U64(0xB4BCA50B, 0x065ABE63), U64(0x0FED077A, 0x756B53A9), /* ~= 10^-219 */ + U64(0xE1EBCE4D, 0xC7F16DFB), U64(0xD3E84959, 0x12C62894), /* ~= 10^-218 */ + U64(0x8D3360F0, 0x9CF6E4BD), U64(0x64712DD7, 0xABBBD95C), /* ~= 10^-217 */ + U64(0xB080392C, 0xC4349DEC), U64(0xBD8D794D, 0x96AACFB3), /* ~= 10^-216 */ + U64(0xDCA04777, 0xF541C567), U64(0xECF0D7A0, 0xFC5583A0), /* ~= 10^-215 */ + U64(0x89E42CAA, 0xF9491B60), U64(0xF41686C4, 0x9DB57244), /* ~= 10^-214 */ + U64(0xAC5D37D5, 0xB79B6239), U64(0x311C2875, 0xC522CED5), /* ~= 10^-213 */ + U64(0xD77485CB, 0x25823AC7), U64(0x7D633293, 0x366B828B), /* ~= 10^-212 */ + U64(0x86A8D39E, 0xF77164BC), U64(0xAE5DFF9C, 0x02033197), /* ~= 10^-211 */ + U64(0xA8530886, 0xB54DBDEB), U64(0xD9F57F83, 0x0283FDFC), /* ~= 10^-210 */ + U64(0xD267CAA8, 0x62A12D66), U64(0xD072DF63, 0xC324FD7B), /* ~= 10^-209 */ + U64(0x8380DEA9, 0x3DA4BC60), U64(0x4247CB9E, 0x59F71E6D), /* ~= 10^-208 */ + U64(0xA4611653, 0x8D0DEB78), U64(0x52D9BE85, 0xF074E608), /* ~= 10^-207 */ + U64(0xCD795BE8, 0x70516656), U64(0x67902E27, 0x6C921F8B), /* ~= 10^-206 */ + U64(0x806BD971, 0x4632DFF6), U64(0x00BA1CD8, 0xA3DB53B6), /* ~= 10^-205 */ + U64(0xA086CFCD, 0x97BF97F3), U64(0x80E8A40E, 0xCCD228A4), /* ~= 10^-204 */ + U64(0xC8A883C0, 0xFDAF7DF0), U64(0x6122CD12, 0x8006B2CD), /* ~= 10^-203 */ + U64(0xFAD2A4B1, 0x3D1B5D6C), U64(0x796B8057, 0x20085F81), /* ~= 10^-202 */ + U64(0x9CC3A6EE, 0xC6311A63), U64(0xCBE33036, 0x74053BB0), /* ~= 10^-201 */ + U64(0xC3F490AA, 0x77BD60FC), U64(0xBEDBFC44, 0x11068A9C), /* ~= 10^-200 */ + U64(0xF4F1B4D5, 0x15ACB93B), U64(0xEE92FB55, 0x15482D44), /* ~= 10^-199 */ + U64(0x99171105, 0x2D8BF3C5), U64(0x751BDD15, 0x2D4D1C4A), /* ~= 10^-198 */ + U64(0xBF5CD546, 0x78EEF0B6), U64(0xD262D45A, 0x78A0635D), /* ~= 10^-197 */ + U64(0xEF340A98, 0x172AACE4), U64(0x86FB8971, 0x16C87C34), /* ~= 10^-196 */ + U64(0x9580869F, 0x0E7AAC0E), U64(0xD45D35E6, 0xAE3D4DA0), /* ~= 10^-195 */ + U64(0xBAE0A846, 0xD2195712), U64(0x89748360, 0x59CCA109), /* ~= 10^-194 */ + U64(0xE998D258, 0x869FACD7), U64(0x2BD1A438, 0x703FC94B), /* ~= 10^-193 */ + U64(0x91FF8377, 0x5423CC06), U64(0x7B6306A3, 0x4627DDCF), /* ~= 10^-192 */ + U64(0xB67F6455, 0x292CBF08), U64(0x1A3BC84C, 0x17B1D542), /* ~= 10^-191 */ + U64(0xE41F3D6A, 0x7377EECA), U64(0x20CABA5F, 0x1D9E4A93), /* ~= 10^-190 */ + U64(0x8E938662, 0x882AF53E), U64(0x547EB47B, 0x7282EE9C), /* ~= 10^-189 */ + U64(0xB23867FB, 0x2A35B28D), U64(0xE99E619A, 0x4F23AA43), /* ~= 10^-188 */ + U64(0xDEC681F9, 0xF4C31F31), U64(0x6405FA00, 0xE2EC94D4), /* ~= 10^-187 */ + U64(0x8B3C113C, 0x38F9F37E), U64(0xDE83BC40, 0x8DD3DD04), /* ~= 10^-186 */ + U64(0xAE0B158B, 0x4738705E), U64(0x9624AB50, 0xB148D445), /* ~= 10^-185 */ + U64(0xD98DDAEE, 0x19068C76), U64(0x3BADD624, 0xDD9B0957), /* ~= 10^-184 */ + U64(0x87F8A8D4, 0xCFA417C9), U64(0xE54CA5D7, 0x0A80E5D6), /* ~= 10^-183 */ + U64(0xA9F6D30A, 0x038D1DBC), U64(0x5E9FCF4C, 0xCD211F4C), /* ~= 10^-182 */ + U64(0xD47487CC, 0x8470652B), U64(0x7647C320, 0x0069671F), /* ~= 10^-181 */ + U64(0x84C8D4DF, 0xD2C63F3B), U64(0x29ECD9F4, 0x0041E073), /* ~= 10^-180 */ + U64(0xA5FB0A17, 0xC777CF09), U64(0xF4681071, 0x00525890), /* ~= 10^-179 */ + U64(0xCF79CC9D, 0xB955C2CC), U64(0x7182148D, 0x4066EEB4), /* ~= 10^-178 */ + U64(0x81AC1FE2, 0x93D599BF), U64(0xC6F14CD8, 0x48405530), /* ~= 10^-177 */ + U64(0xA21727DB, 0x38CB002F), U64(0xB8ADA00E, 0x5A506A7C), /* ~= 10^-176 */ + U64(0xCA9CF1D2, 0x06FDC03B), U64(0xA6D90811, 0xF0E4851C), /* ~= 10^-175 */ + U64(0xFD442E46, 0x88BD304A), U64(0x908F4A16, 0x6D1DA663), /* ~= 10^-174 */ + U64(0x9E4A9CEC, 0x15763E2E), U64(0x9A598E4E, 0x043287FE), /* ~= 10^-173 */ + U64(0xC5DD4427, 0x1AD3CDBA), U64(0x40EFF1E1, 0x853F29FD), /* ~= 10^-172 */ + U64(0xF7549530, 0xE188C128), U64(0xD12BEE59, 0xE68EF47C), /* ~= 10^-171 */ + U64(0x9A94DD3E, 0x8CF578B9), U64(0x82BB74F8, 0x301958CE), /* ~= 10^-170 */ + U64(0xC13A148E, 0x3032D6E7), U64(0xE36A5236, 0x3C1FAF01), /* ~= 10^-169 */ + U64(0xF18899B1, 0xBC3F8CA1), U64(0xDC44E6C3, 0xCB279AC1), /* ~= 10^-168 */ + U64(0x96F5600F, 0x15A7B7E5), U64(0x29AB103A, 0x5EF8C0B9), /* ~= 10^-167 */ + U64(0xBCB2B812, 0xDB11A5DE), U64(0x7415D448, 0xF6B6F0E7), /* ~= 10^-166 */ + U64(0xEBDF6617, 0x91D60F56), U64(0x111B495B, 0x3464AD21), /* ~= 10^-165 */ + U64(0x936B9FCE, 0xBB25C995), U64(0xCAB10DD9, 0x00BEEC34), /* ~= 10^-164 */ + U64(0xB84687C2, 0x69EF3BFB), U64(0x3D5D514F, 0x40EEA742), /* ~= 10^-163 */ + U64(0xE65829B3, 0x046B0AFA), U64(0x0CB4A5A3, 0x112A5112), /* ~= 10^-162 */ + U64(0x8FF71A0F, 0xE2C2E6DC), U64(0x47F0E785, 0xEABA72AB), /* ~= 10^-161 */ + U64(0xB3F4E093, 0xDB73A093), U64(0x59ED2167, 0x65690F56), /* ~= 10^-160 */ + U64(0xE0F218B8, 0xD25088B8), U64(0x306869C1, 0x3EC3532C), /* ~= 10^-159 */ + U64(0x8C974F73, 0x83725573), U64(0x1E414218, 0xC73A13FB), /* ~= 10^-158 */ + U64(0xAFBD2350, 0x644EEACF), U64(0xE5D1929E, 0xF90898FA), /* ~= 10^-157 */ + U64(0xDBAC6C24, 0x7D62A583), U64(0xDF45F746, 0xB74ABF39), /* ~= 10^-156 */ + U64(0x894BC396, 0xCE5DA772), U64(0x6B8BBA8C, 0x328EB783), /* ~= 10^-155 */ + U64(0xAB9EB47C, 0x81F5114F), U64(0x066EA92F, 0x3F326564), /* ~= 10^-154 */ + U64(0xD686619B, 0xA27255A2), U64(0xC80A537B, 0x0EFEFEBD), /* ~= 10^-153 */ + U64(0x8613FD01, 0x45877585), U64(0xBD06742C, 0xE95F5F36), /* ~= 10^-152 */ + U64(0xA798FC41, 0x96E952E7), U64(0x2C481138, 0x23B73704), /* ~= 10^-151 */ + U64(0xD17F3B51, 0xFCA3A7A0), U64(0xF75A1586, 0x2CA504C5), /* ~= 10^-150 */ + U64(0x82EF8513, 0x3DE648C4), U64(0x9A984D73, 0xDBE722FB), /* ~= 10^-149 */ + U64(0xA3AB6658, 0x0D5FDAF5), U64(0xC13E60D0, 0xD2E0EBBA), /* ~= 10^-148 */ + U64(0xCC963FEE, 0x10B7D1B3), U64(0x318DF905, 0x079926A8), /* ~= 10^-147 */ + U64(0xFFBBCFE9, 0x94E5C61F), U64(0xFDF17746, 0x497F7052), /* ~= 10^-146 */ + U64(0x9FD561F1, 0xFD0F9BD3), U64(0xFEB6EA8B, 0xEDEFA633), /* ~= 10^-145 */ + U64(0xC7CABA6E, 0x7C5382C8), U64(0xFE64A52E, 0xE96B8FC0), /* ~= 10^-144 */ + U64(0xF9BD690A, 0x1B68637B), U64(0x3DFDCE7A, 0xA3C673B0), /* ~= 10^-143 */ + U64(0x9C1661A6, 0x51213E2D), U64(0x06BEA10C, 0xA65C084E), /* ~= 10^-142 */ + U64(0xC31BFA0F, 0xE5698DB8), U64(0x486E494F, 0xCFF30A62), /* ~= 10^-141 */ + U64(0xF3E2F893, 0xDEC3F126), U64(0x5A89DBA3, 0xC3EFCCFA), /* ~= 10^-140 */ + U64(0x986DDB5C, 0x6B3A76B7), U64(0xF8962946, 0x5A75E01C), /* ~= 10^-139 */ + U64(0xBE895233, 0x86091465), U64(0xF6BBB397, 0xF1135823), /* ~= 10^-138 */ + U64(0xEE2BA6C0, 0x678B597F), U64(0x746AA07D, 0xED582E2C), /* ~= 10^-137 */ + U64(0x94DB4838, 0x40B717EF), U64(0xA8C2A44E, 0xB4571CDC), /* ~= 10^-136 */ + U64(0xBA121A46, 0x50E4DDEB), U64(0x92F34D62, 0x616CE413), /* ~= 10^-135 */ + U64(0xE896A0D7, 0xE51E1566), U64(0x77B020BA, 0xF9C81D17), /* ~= 10^-134 */ + U64(0x915E2486, 0xEF32CD60), U64(0x0ACE1474, 0xDC1D122E), /* ~= 10^-133 */ + U64(0xB5B5ADA8, 0xAAFF80B8), U64(0x0D819992, 0x132456BA), /* ~= 10^-132 */ + U64(0xE3231912, 0xD5BF60E6), U64(0x10E1FFF6, 0x97ED6C69), /* ~= 10^-131 */ + U64(0x8DF5EFAB, 0xC5979C8F), U64(0xCA8D3FFA, 0x1EF463C1), /* ~= 10^-130 */ + U64(0xB1736B96, 0xB6FD83B3), U64(0xBD308FF8, 0xA6B17CB2), /* ~= 10^-129 */ + U64(0xDDD0467C, 0x64BCE4A0), U64(0xAC7CB3F6, 0xD05DDBDE), /* ~= 10^-128 */ + U64(0x8AA22C0D, 0xBEF60EE4), U64(0x6BCDF07A, 0x423AA96B), /* ~= 10^-127 */ + U64(0xAD4AB711, 0x2EB3929D), U64(0x86C16C98, 0xD2C953C6), /* ~= 10^-126 */ + U64(0xD89D64D5, 0x7A607744), U64(0xE871C7BF, 0x077BA8B7), /* ~= 10^-125 */ + U64(0x87625F05, 0x6C7C4A8B), U64(0x11471CD7, 0x64AD4972), /* ~= 10^-124 */ + U64(0xA93AF6C6, 0xC79B5D2D), U64(0xD598E40D, 0x3DD89BCF), /* ~= 10^-123 */ + U64(0xD389B478, 0x79823479), U64(0x4AFF1D10, 0x8D4EC2C3), /* ~= 10^-122 */ + U64(0x843610CB, 0x4BF160CB), U64(0xCEDF722A, 0x585139BA), /* ~= 10^-121 */ + U64(0xA54394FE, 0x1EEDB8FE), U64(0xC2974EB4, 0xEE658828), /* ~= 10^-120 */ + U64(0xCE947A3D, 0xA6A9273E), U64(0x733D2262, 0x29FEEA32), /* ~= 10^-119 */ + U64(0x811CCC66, 0x8829B887), U64(0x0806357D, 0x5A3F525F), /* ~= 10^-118 */ + U64(0xA163FF80, 0x2A3426A8), U64(0xCA07C2DC, 0xB0CF26F7), /* ~= 10^-117 */ + U64(0xC9BCFF60, 0x34C13052), U64(0xFC89B393, 0xDD02F0B5), /* ~= 10^-116 */ + U64(0xFC2C3F38, 0x41F17C67), U64(0xBBAC2078, 0xD443ACE2), /* ~= 10^-115 */ + U64(0x9D9BA783, 0x2936EDC0), U64(0xD54B944B, 0x84AA4C0D), /* ~= 10^-114 */ + U64(0xC5029163, 0xF384A931), U64(0x0A9E795E, 0x65D4DF11), /* ~= 10^-113 */ + U64(0xF64335BC, 0xF065D37D), U64(0x4D4617B5, 0xFF4A16D5), /* ~= 10^-112 */ + U64(0x99EA0196, 0x163FA42E), U64(0x504BCED1, 0xBF8E4E45), /* ~= 10^-111 */ + U64(0xC06481FB, 0x9BCF8D39), U64(0xE45EC286, 0x2F71E1D6), /* ~= 10^-110 */ + U64(0xF07DA27A, 0x82C37088), U64(0x5D767327, 0xBB4E5A4C), /* ~= 10^-109 */ + U64(0x964E858C, 0x91BA2655), U64(0x3A6A07F8, 0xD510F86F), /* ~= 10^-108 */ + U64(0xBBE226EF, 0xB628AFEA), U64(0x890489F7, 0x0A55368B), /* ~= 10^-107 */ + U64(0xEADAB0AB, 0xA3B2DBE5), U64(0x2B45AC74, 0xCCEA842E), /* ~= 10^-106 */ + U64(0x92C8AE6B, 0x464FC96F), U64(0x3B0B8BC9, 0x0012929D), /* ~= 10^-105 */ + U64(0xB77ADA06, 0x17E3BBCB), U64(0x09CE6EBB, 0x40173744), /* ~= 10^-104 */ + U64(0xE5599087, 0x9DDCAABD), U64(0xCC420A6A, 0x101D0515), /* ~= 10^-103 */ + U64(0x8F57FA54, 0xC2A9EAB6), U64(0x9FA94682, 0x4A12232D), /* ~= 10^-102 */ + U64(0xB32DF8E9, 0xF3546564), U64(0x47939822, 0xDC96ABF9), /* ~= 10^-101 */ + U64(0xDFF97724, 0x70297EBD), U64(0x59787E2B, 0x93BC56F7), /* ~= 10^-100 */ + U64(0x8BFBEA76, 0xC619EF36), U64(0x57EB4EDB, 0x3C55B65A), /* ~= 10^-99 */ + U64(0xAEFAE514, 0x77A06B03), U64(0xEDE62292, 0x0B6B23F1), /* ~= 10^-98 */ + U64(0xDAB99E59, 0x958885C4), U64(0xE95FAB36, 0x8E45ECED), /* ~= 10^-97 */ + U64(0x88B402F7, 0xFD75539B), U64(0x11DBCB02, 0x18EBB414), /* ~= 10^-96 */ + U64(0xAAE103B5, 0xFCD2A881), U64(0xD652BDC2, 0x9F26A119), /* ~= 10^-95 */ + U64(0xD59944A3, 0x7C0752A2), U64(0x4BE76D33, 0x46F0495F), /* ~= 10^-94 */ + U64(0x857FCAE6, 0x2D8493A5), U64(0x6F70A440, 0x0C562DDB), /* ~= 10^-93 */ + U64(0xA6DFBD9F, 0xB8E5B88E), U64(0xCB4CCD50, 0x0F6BB952), /* ~= 10^-92 */ + U64(0xD097AD07, 0xA71F26B2), U64(0x7E2000A4, 0x1346A7A7), /* ~= 10^-91 */ + U64(0x825ECC24, 0xC873782F), U64(0x8ED40066, 0x8C0C28C8), /* ~= 10^-90 */ + U64(0xA2F67F2D, 0xFA90563B), U64(0x72890080, 0x2F0F32FA), /* ~= 10^-89 */ + U64(0xCBB41EF9, 0x79346BCA), U64(0x4F2B40A0, 0x3AD2FFB9), /* ~= 10^-88 */ + U64(0xFEA126B7, 0xD78186BC), U64(0xE2F610C8, 0x4987BFA8), /* ~= 10^-87 */ + U64(0x9F24B832, 0xE6B0F436), U64(0x0DD9CA7D, 0x2DF4D7C9), /* ~= 10^-86 */ + U64(0xC6EDE63F, 0xA05D3143), U64(0x91503D1C, 0x79720DBB), /* ~= 10^-85 */ + U64(0xF8A95FCF, 0x88747D94), U64(0x75A44C63, 0x97CE912A), /* ~= 10^-84 */ + U64(0x9B69DBE1, 0xB548CE7C), U64(0xC986AFBE, 0x3EE11ABA), /* ~= 10^-83 */ + U64(0xC24452DA, 0x229B021B), U64(0xFBE85BAD, 0xCE996168), /* ~= 10^-82 */ + U64(0xF2D56790, 0xAB41C2A2), U64(0xFAE27299, 0x423FB9C3), /* ~= 10^-81 */ + U64(0x97C560BA, 0x6B0919A5), U64(0xDCCD879F, 0xC967D41A), /* ~= 10^-80 */ + U64(0xBDB6B8E9, 0x05CB600F), U64(0x5400E987, 0xBBC1C920), /* ~= 10^-79 */ + U64(0xED246723, 0x473E3813), U64(0x290123E9, 0xAAB23B68), /* ~= 10^-78 */ + U64(0x9436C076, 0x0C86E30B), U64(0xF9A0B672, 0x0AAF6521), /* ~= 10^-77 */ + U64(0xB9447093, 0x8FA89BCE), U64(0xF808E40E, 0x8D5B3E69), /* ~= 10^-76 */ + U64(0xE7958CB8, 0x7392C2C2), U64(0xB60B1D12, 0x30B20E04), /* ~= 10^-75 */ + U64(0x90BD77F3, 0x483BB9B9), U64(0xB1C6F22B, 0x5E6F48C2), /* ~= 10^-74 */ + U64(0xB4ECD5F0, 0x1A4AA828), U64(0x1E38AEB6, 0x360B1AF3), /* ~= 10^-73 */ + U64(0xE2280B6C, 0x20DD5232), U64(0x25C6DA63, 0xC38DE1B0), /* ~= 10^-72 */ + U64(0x8D590723, 0x948A535F), U64(0x579C487E, 0x5A38AD0E), /* ~= 10^-71 */ + U64(0xB0AF48EC, 0x79ACE837), U64(0x2D835A9D, 0xF0C6D851), /* ~= 10^-70 */ + U64(0xDCDB1B27, 0x98182244), U64(0xF8E43145, 0x6CF88E65), /* ~= 10^-69 */ + U64(0x8A08F0F8, 0xBF0F156B), U64(0x1B8E9ECB, 0x641B58FF), /* ~= 10^-68 */ + U64(0xAC8B2D36, 0xEED2DAC5), U64(0xE272467E, 0x3D222F3F), /* ~= 10^-67 */ + U64(0xD7ADF884, 0xAA879177), U64(0x5B0ED81D, 0xCC6ABB0F), /* ~= 10^-66 */ + U64(0x86CCBB52, 0xEA94BAEA), U64(0x98E94712, 0x9FC2B4E9), /* ~= 10^-65 */ + U64(0xA87FEA27, 0xA539E9A5), U64(0x3F2398D7, 0x47B36224), /* ~= 10^-64 */ + U64(0xD29FE4B1, 0x8E88640E), U64(0x8EEC7F0D, 0x19A03AAD), /* ~= 10^-63 */ + U64(0x83A3EEEE, 0xF9153E89), U64(0x1953CF68, 0x300424AC), /* ~= 10^-62 */ + U64(0xA48CEAAA, 0xB75A8E2B), U64(0x5FA8C342, 0x3C052DD7), /* ~= 10^-61 */ + U64(0xCDB02555, 0x653131B6), U64(0x3792F412, 0xCB06794D), /* ~= 10^-60 */ + U64(0x808E1755, 0x5F3EBF11), U64(0xE2BBD88B, 0xBEE40BD0), /* ~= 10^-59 */ + U64(0xA0B19D2A, 0xB70E6ED6), U64(0x5B6ACEAE, 0xAE9D0EC4), /* ~= 10^-58 */ + U64(0xC8DE0475, 0x64D20A8B), U64(0xF245825A, 0x5A445275), /* ~= 10^-57 */ + U64(0xFB158592, 0xBE068D2E), U64(0xEED6E2F0, 0xF0D56712), /* ~= 10^-56 */ + U64(0x9CED737B, 0xB6C4183D), U64(0x55464DD6, 0x9685606B), /* ~= 10^-55 */ + U64(0xC428D05A, 0xA4751E4C), U64(0xAA97E14C, 0x3C26B886), /* ~= 10^-54 */ + U64(0xF5330471, 0x4D9265DF), U64(0xD53DD99F, 0x4B3066A8), /* ~= 10^-53 */ + U64(0x993FE2C6, 0xD07B7FAB), U64(0xE546A803, 0x8EFE4029), /* ~= 10^-52 */ + U64(0xBF8FDB78, 0x849A5F96), U64(0xDE985204, 0x72BDD033), /* ~= 10^-51 */ + U64(0xEF73D256, 0xA5C0F77C), U64(0x963E6685, 0x8F6D4440), /* ~= 10^-50 */ + U64(0x95A86376, 0x27989AAD), U64(0xDDE70013, 0x79A44AA8), /* ~= 10^-49 */ + U64(0xBB127C53, 0xB17EC159), U64(0x5560C018, 0x580D5D52), /* ~= 10^-48 */ + U64(0xE9D71B68, 0x9DDE71AF), U64(0xAAB8F01E, 0x6E10B4A6), /* ~= 10^-47 */ + U64(0x92267121, 0x62AB070D), U64(0xCAB39613, 0x04CA70E8), /* ~= 10^-46 */ + U64(0xB6B00D69, 0xBB55C8D1), U64(0x3D607B97, 0xC5FD0D22), /* ~= 10^-45 */ + U64(0xE45C10C4, 0x2A2B3B05), U64(0x8CB89A7D, 0xB77C506A), /* ~= 10^-44 */ + U64(0x8EB98A7A, 0x9A5B04E3), U64(0x77F3608E, 0x92ADB242), /* ~= 10^-43 */ + U64(0xB267ED19, 0x40F1C61C), U64(0x55F038B2, 0x37591ED3), /* ~= 10^-42 */ + U64(0xDF01E85F, 0x912E37A3), U64(0x6B6C46DE, 0xC52F6688), /* ~= 10^-41 */ + U64(0x8B61313B, 0xBABCE2C6), U64(0x2323AC4B, 0x3B3DA015), /* ~= 10^-40 */ + U64(0xAE397D8A, 0xA96C1B77), U64(0xABEC975E, 0x0A0D081A), /* ~= 10^-39 */ + U64(0xD9C7DCED, 0x53C72255), U64(0x96E7BD35, 0x8C904A21), /* ~= 10^-38 */ + U64(0x881CEA14, 0x545C7575), U64(0x7E50D641, 0x77DA2E54), /* ~= 10^-37 */ + U64(0xAA242499, 0x697392D2), U64(0xDDE50BD1, 0xD5D0B9E9), /* ~= 10^-36 */ + U64(0xD4AD2DBF, 0xC3D07787), U64(0x955E4EC6, 0x4B44E864), /* ~= 10^-35 */ + U64(0x84EC3C97, 0xDA624AB4), U64(0xBD5AF13B, 0xEF0B113E), /* ~= 10^-34 */ + U64(0xA6274BBD, 0xD0FADD61), U64(0xECB1AD8A, 0xEACDD58E), /* ~= 10^-33 */ + U64(0xCFB11EAD, 0x453994BA), U64(0x67DE18ED, 0xA5814AF2), /* ~= 10^-32 */ + U64(0x81CEB32C, 0x4B43FCF4), U64(0x80EACF94, 0x8770CED7), /* ~= 10^-31 */ + U64(0xA2425FF7, 0x5E14FC31), U64(0xA1258379, 0xA94D028D), /* ~= 10^-30 */ + U64(0xCAD2F7F5, 0x359A3B3E), U64(0x096EE458, 0x13A04330), /* ~= 10^-29 */ + U64(0xFD87B5F2, 0x8300CA0D), U64(0x8BCA9D6E, 0x188853FC), /* ~= 10^-28 */ + U64(0x9E74D1B7, 0x91E07E48), U64(0x775EA264, 0xCF55347D), /* ~= 10^-27 */ + U64(0xC6120625, 0x76589DDA), U64(0x95364AFE, 0x032A819D), /* ~= 10^-26 */ + U64(0xF79687AE, 0xD3EEC551), U64(0x3A83DDBD, 0x83F52204), /* ~= 10^-25 */ + U64(0x9ABE14CD, 0x44753B52), U64(0xC4926A96, 0x72793542), /* ~= 10^-24 */ + U64(0xC16D9A00, 0x95928A27), U64(0x75B7053C, 0x0F178293), /* ~= 10^-23 */ + U64(0xF1C90080, 0xBAF72CB1), U64(0x5324C68B, 0x12DD6338), /* ~= 10^-22 */ + U64(0x971DA050, 0x74DA7BEE), U64(0xD3F6FC16, 0xEBCA5E03), /* ~= 10^-21 */ + U64(0xBCE50864, 0x92111AEA), U64(0x88F4BB1C, 0xA6BCF584), /* ~= 10^-20 */ + U64(0xEC1E4A7D, 0xB69561A5), U64(0x2B31E9E3, 0xD06C32E5), /* ~= 10^-19 */ + U64(0x9392EE8E, 0x921D5D07), U64(0x3AFF322E, 0x62439FCF), /* ~= 10^-18 */ + U64(0xB877AA32, 0x36A4B449), U64(0x09BEFEB9, 0xFAD487C2), /* ~= 10^-17 */ + U64(0xE69594BE, 0xC44DE15B), U64(0x4C2EBE68, 0x7989A9B3), /* ~= 10^-16 */ + U64(0x901D7CF7, 0x3AB0ACD9), U64(0x0F9D3701, 0x4BF60A10), /* ~= 10^-15 */ + U64(0xB424DC35, 0x095CD80F), U64(0x538484C1, 0x9EF38C94), /* ~= 10^-14 */ + U64(0xE12E1342, 0x4BB40E13), U64(0x2865A5F2, 0x06B06FB9), /* ~= 10^-13 */ + U64(0x8CBCCC09, 0x6F5088CB), U64(0xF93F87B7, 0x442E45D3), /* ~= 10^-12 */ + U64(0xAFEBFF0B, 0xCB24AAFE), U64(0xF78F69A5, 0x1539D748), /* ~= 10^-11 */ + U64(0xDBE6FECE, 0xBDEDD5BE), U64(0xB573440E, 0x5A884D1B), /* ~= 10^-10 */ + U64(0x89705F41, 0x36B4A597), U64(0x31680A88, 0xF8953030), /* ~= 10^-9 */ + U64(0xABCC7711, 0x8461CEFC), U64(0xFDC20D2B, 0x36BA7C3D), /* ~= 10^-8 */ + U64(0xD6BF94D5, 0xE57A42BC), U64(0x3D329076, 0x04691B4C), /* ~= 10^-7 */ + U64(0x8637BD05, 0xAF6C69B5), U64(0xA63F9A49, 0xC2C1B10F), /* ~= 10^-6 */ + U64(0xA7C5AC47, 0x1B478423), U64(0x0FCF80DC, 0x33721D53), /* ~= 10^-5 */ + U64(0xD1B71758, 0xE219652B), U64(0xD3C36113, 0x404EA4A8), /* ~= 10^-4 */ + U64(0x83126E97, 0x8D4FDF3B), U64(0x645A1CAC, 0x083126E9), /* ~= 10^-3 */ + U64(0xA3D70A3D, 0x70A3D70A), U64(0x3D70A3D7, 0x0A3D70A3), /* ~= 10^-2 */ + U64(0xCCCCCCCC, 0xCCCCCCCC), U64(0xCCCCCCCC, 0xCCCCCCCC), /* ~= 10^-1 */ + U64(0x80000000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^0 */ + U64(0xA0000000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^1 */ + U64(0xC8000000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^2 */ + U64(0xFA000000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^3 */ + U64(0x9C400000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^4 */ + U64(0xC3500000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^5 */ + U64(0xF4240000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^6 */ + U64(0x98968000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^7 */ + U64(0xBEBC2000, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^8 */ + U64(0xEE6B2800, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^9 */ + U64(0x9502F900, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^10 */ + U64(0xBA43B740, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^11 */ + U64(0xE8D4A510, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^12 */ + U64(0x9184E72A, 0x00000000), U64(0x00000000, 0x00000000), /* == 10^13 */ + U64(0xB5E620F4, 0x80000000), U64(0x00000000, 0x00000000), /* == 10^14 */ + U64(0xE35FA931, 0xA0000000), U64(0x00000000, 0x00000000), /* == 10^15 */ + U64(0x8E1BC9BF, 0x04000000), U64(0x00000000, 0x00000000), /* == 10^16 */ + U64(0xB1A2BC2E, 0xC5000000), U64(0x00000000, 0x00000000), /* == 10^17 */ + U64(0xDE0B6B3A, 0x76400000), U64(0x00000000, 0x00000000), /* == 10^18 */ + U64(0x8AC72304, 0x89E80000), U64(0x00000000, 0x00000000), /* == 10^19 */ + U64(0xAD78EBC5, 0xAC620000), U64(0x00000000, 0x00000000), /* == 10^20 */ + U64(0xD8D726B7, 0x177A8000), U64(0x00000000, 0x00000000), /* == 10^21 */ + U64(0x87867832, 0x6EAC9000), U64(0x00000000, 0x00000000), /* == 10^22 */ + U64(0xA968163F, 0x0A57B400), U64(0x00000000, 0x00000000), /* == 10^23 */ + U64(0xD3C21BCE, 0xCCEDA100), U64(0x00000000, 0x00000000), /* == 10^24 */ + U64(0x84595161, 0x401484A0), U64(0x00000000, 0x00000000), /* == 10^25 */ + U64(0xA56FA5B9, 0x9019A5C8), U64(0x00000000, 0x00000000), /* == 10^26 */ + U64(0xCECB8F27, 0xF4200F3A), U64(0x00000000, 0x00000000), /* == 10^27 */ + U64(0x813F3978, 0xF8940984), U64(0x40000000, 0x00000000), /* == 10^28 */ + U64(0xA18F07D7, 0x36B90BE5), U64(0x50000000, 0x00000000), /* == 10^29 */ + U64(0xC9F2C9CD, 0x04674EDE), U64(0xA4000000, 0x00000000), /* == 10^30 */ + U64(0xFC6F7C40, 0x45812296), U64(0x4D000000, 0x00000000), /* == 10^31 */ + U64(0x9DC5ADA8, 0x2B70B59D), U64(0xF0200000, 0x00000000), /* == 10^32 */ + U64(0xC5371912, 0x364CE305), U64(0x6C280000, 0x00000000), /* == 10^33 */ + U64(0xF684DF56, 0xC3E01BC6), U64(0xC7320000, 0x00000000), /* == 10^34 */ + U64(0x9A130B96, 0x3A6C115C), U64(0x3C7F4000, 0x00000000), /* == 10^35 */ + U64(0xC097CE7B, 0xC90715B3), U64(0x4B9F1000, 0x00000000), /* == 10^36 */ + U64(0xF0BDC21A, 0xBB48DB20), U64(0x1E86D400, 0x00000000), /* == 10^37 */ + U64(0x96769950, 0xB50D88F4), U64(0x13144480, 0x00000000), /* == 10^38 */ + U64(0xBC143FA4, 0xE250EB31), U64(0x17D955A0, 0x00000000), /* == 10^39 */ + U64(0xEB194F8E, 0x1AE525FD), U64(0x5DCFAB08, 0x00000000), /* == 10^40 */ + U64(0x92EFD1B8, 0xD0CF37BE), U64(0x5AA1CAE5, 0x00000000), /* == 10^41 */ + U64(0xB7ABC627, 0x050305AD), U64(0xF14A3D9E, 0x40000000), /* == 10^42 */ + U64(0xE596B7B0, 0xC643C719), U64(0x6D9CCD05, 0xD0000000), /* == 10^43 */ + U64(0x8F7E32CE, 0x7BEA5C6F), U64(0xE4820023, 0xA2000000), /* == 10^44 */ + U64(0xB35DBF82, 0x1AE4F38B), U64(0xDDA2802C, 0x8A800000), /* == 10^45 */ + U64(0xE0352F62, 0xA19E306E), U64(0xD50B2037, 0xAD200000), /* == 10^46 */ + U64(0x8C213D9D, 0xA502DE45), U64(0x4526F422, 0xCC340000), /* == 10^47 */ + U64(0xAF298D05, 0x0E4395D6), U64(0x9670B12B, 0x7F410000), /* == 10^48 */ + U64(0xDAF3F046, 0x51D47B4C), U64(0x3C0CDD76, 0x5F114000), /* == 10^49 */ + U64(0x88D8762B, 0xF324CD0F), U64(0xA5880A69, 0xFB6AC800), /* == 10^50 */ + U64(0xAB0E93B6, 0xEFEE0053), U64(0x8EEA0D04, 0x7A457A00), /* == 10^51 */ + U64(0xD5D238A4, 0xABE98068), U64(0x72A49045, 0x98D6D880), /* == 10^52 */ + U64(0x85A36366, 0xEB71F041), U64(0x47A6DA2B, 0x7F864750), /* == 10^53 */ + U64(0xA70C3C40, 0xA64E6C51), U64(0x999090B6, 0x5F67D924), /* == 10^54 */ + U64(0xD0CF4B50, 0xCFE20765), U64(0xFFF4B4E3, 0xF741CF6D), /* == 10^55 */ + U64(0x82818F12, 0x81ED449F), U64(0xBFF8F10E, 0x7A8921A4), /* ~= 10^56 */ + U64(0xA321F2D7, 0x226895C7), U64(0xAFF72D52, 0x192B6A0D), /* ~= 10^57 */ + U64(0xCBEA6F8C, 0xEB02BB39), U64(0x9BF4F8A6, 0x9F764490), /* ~= 10^58 */ + U64(0xFEE50B70, 0x25C36A08), U64(0x02F236D0, 0x4753D5B4), /* ~= 10^59 */ + U64(0x9F4F2726, 0x179A2245), U64(0x01D76242, 0x2C946590), /* ~= 10^60 */ + U64(0xC722F0EF, 0x9D80AAD6), U64(0x424D3AD2, 0xB7B97EF5), /* ~= 10^61 */ + U64(0xF8EBAD2B, 0x84E0D58B), U64(0xD2E08987, 0x65A7DEB2), /* ~= 10^62 */ + U64(0x9B934C3B, 0x330C8577), U64(0x63CC55F4, 0x9F88EB2F), /* ~= 10^63 */ + U64(0xC2781F49, 0xFFCFA6D5), U64(0x3CBF6B71, 0xC76B25FB), /* ~= 10^64 */ + U64(0xF316271C, 0x7FC3908A), U64(0x8BEF464E, 0x3945EF7A), /* ~= 10^65 */ + U64(0x97EDD871, 0xCFDA3A56), U64(0x97758BF0, 0xE3CBB5AC), /* ~= 10^66 */ + U64(0xBDE94E8E, 0x43D0C8EC), U64(0x3D52EEED, 0x1CBEA317), /* ~= 10^67 */ + U64(0xED63A231, 0xD4C4FB27), U64(0x4CA7AAA8, 0x63EE4BDD), /* ~= 10^68 */ + U64(0x945E455F, 0x24FB1CF8), U64(0x8FE8CAA9, 0x3E74EF6A), /* ~= 10^69 */ + U64(0xB975D6B6, 0xEE39E436), U64(0xB3E2FD53, 0x8E122B44), /* ~= 10^70 */ + U64(0xE7D34C64, 0xA9C85D44), U64(0x60DBBCA8, 0x7196B616), /* ~= 10^71 */ + U64(0x90E40FBE, 0xEA1D3A4A), U64(0xBC8955E9, 0x46FE31CD), /* ~= 10^72 */ + U64(0xB51D13AE, 0xA4A488DD), U64(0x6BABAB63, 0x98BDBE41), /* ~= 10^73 */ + U64(0xE264589A, 0x4DCDAB14), U64(0xC696963C, 0x7EED2DD1), /* ~= 10^74 */ + U64(0x8D7EB760, 0x70A08AEC), U64(0xFC1E1DE5, 0xCF543CA2), /* ~= 10^75 */ + U64(0xB0DE6538, 0x8CC8ADA8), U64(0x3B25A55F, 0x43294BCB), /* ~= 10^76 */ + U64(0xDD15FE86, 0xAFFAD912), U64(0x49EF0EB7, 0x13F39EBE), /* ~= 10^77 */ + U64(0x8A2DBF14, 0x2DFCC7AB), U64(0x6E356932, 0x6C784337), /* ~= 10^78 */ + U64(0xACB92ED9, 0x397BF996), U64(0x49C2C37F, 0x07965404), /* ~= 10^79 */ + U64(0xD7E77A8F, 0x87DAF7FB), U64(0xDC33745E, 0xC97BE906), /* ~= 10^80 */ + U64(0x86F0AC99, 0xB4E8DAFD), U64(0x69A028BB, 0x3DED71A3), /* ~= 10^81 */ + U64(0xA8ACD7C0, 0x222311BC), U64(0xC40832EA, 0x0D68CE0C), /* ~= 10^82 */ + U64(0xD2D80DB0, 0x2AABD62B), U64(0xF50A3FA4, 0x90C30190), /* ~= 10^83 */ + U64(0x83C7088E, 0x1AAB65DB), U64(0x792667C6, 0xDA79E0FA), /* ~= 10^84 */ + U64(0xA4B8CAB1, 0xA1563F52), U64(0x577001B8, 0x91185938), /* ~= 10^85 */ + U64(0xCDE6FD5E, 0x09ABCF26), U64(0xED4C0226, 0xB55E6F86), /* ~= 10^86 */ + U64(0x80B05E5A, 0xC60B6178), U64(0x544F8158, 0x315B05B4), /* ~= 10^87 */ + U64(0xA0DC75F1, 0x778E39D6), U64(0x696361AE, 0x3DB1C721), /* ~= 10^88 */ + U64(0xC913936D, 0xD571C84C), U64(0x03BC3A19, 0xCD1E38E9), /* ~= 10^89 */ + U64(0xFB587849, 0x4ACE3A5F), U64(0x04AB48A0, 0x4065C723), /* ~= 10^90 */ + U64(0x9D174B2D, 0xCEC0E47B), U64(0x62EB0D64, 0x283F9C76), /* ~= 10^91 */ + U64(0xC45D1DF9, 0x42711D9A), U64(0x3BA5D0BD, 0x324F8394), /* ~= 10^92 */ + U64(0xF5746577, 0x930D6500), U64(0xCA8F44EC, 0x7EE36479), /* ~= 10^93 */ + U64(0x9968BF6A, 0xBBE85F20), U64(0x7E998B13, 0xCF4E1ECB), /* ~= 10^94 */ + U64(0xBFC2EF45, 0x6AE276E8), U64(0x9E3FEDD8, 0xC321A67E), /* ~= 10^95 */ + U64(0xEFB3AB16, 0xC59B14A2), U64(0xC5CFE94E, 0xF3EA101E), /* ~= 10^96 */ + U64(0x95D04AEE, 0x3B80ECE5), U64(0xBBA1F1D1, 0x58724A12), /* ~= 10^97 */ + U64(0xBB445DA9, 0xCA61281F), U64(0x2A8A6E45, 0xAE8EDC97), /* ~= 10^98 */ + U64(0xEA157514, 0x3CF97226), U64(0xF52D09D7, 0x1A3293BD), /* ~= 10^99 */ + U64(0x924D692C, 0xA61BE758), U64(0x593C2626, 0x705F9C56), /* ~= 10^100 */ + U64(0xB6E0C377, 0xCFA2E12E), U64(0x6F8B2FB0, 0x0C77836C), /* ~= 10^101 */ + U64(0xE498F455, 0xC38B997A), U64(0x0B6DFB9C, 0x0F956447), /* ~= 10^102 */ + U64(0x8EDF98B5, 0x9A373FEC), U64(0x4724BD41, 0x89BD5EAC), /* ~= 10^103 */ + U64(0xB2977EE3, 0x00C50FE7), U64(0x58EDEC91, 0xEC2CB657), /* ~= 10^104 */ + U64(0xDF3D5E9B, 0xC0F653E1), U64(0x2F2967B6, 0x6737E3ED), /* ~= 10^105 */ + U64(0x8B865B21, 0x5899F46C), U64(0xBD79E0D2, 0x0082EE74), /* ~= 10^106 */ + U64(0xAE67F1E9, 0xAEC07187), U64(0xECD85906, 0x80A3AA11), /* ~= 10^107 */ + U64(0xDA01EE64, 0x1A708DE9), U64(0xE80E6F48, 0x20CC9495), /* ~= 10^108 */ + U64(0x884134FE, 0x908658B2), U64(0x3109058D, 0x147FDCDD), /* ~= 10^109 */ + U64(0xAA51823E, 0x34A7EEDE), U64(0xBD4B46F0, 0x599FD415), /* ~= 10^110 */ + U64(0xD4E5E2CD, 0xC1D1EA96), U64(0x6C9E18AC, 0x7007C91A), /* ~= 10^111 */ + U64(0x850FADC0, 0x9923329E), U64(0x03E2CF6B, 0xC604DDB0), /* ~= 10^112 */ + U64(0xA6539930, 0xBF6BFF45), U64(0x84DB8346, 0xB786151C), /* ~= 10^113 */ + U64(0xCFE87F7C, 0xEF46FF16), U64(0xE6126418, 0x65679A63), /* ~= 10^114 */ + U64(0x81F14FAE, 0x158C5F6E), U64(0x4FCB7E8F, 0x3F60C07E), /* ~= 10^115 */ + U64(0xA26DA399, 0x9AEF7749), U64(0xE3BE5E33, 0x0F38F09D), /* ~= 10^116 */ + U64(0xCB090C80, 0x01AB551C), U64(0x5CADF5BF, 0xD3072CC5), /* ~= 10^117 */ + U64(0xFDCB4FA0, 0x02162A63), U64(0x73D9732F, 0xC7C8F7F6), /* ~= 10^118 */ + U64(0x9E9F11C4, 0x014DDA7E), U64(0x2867E7FD, 0xDCDD9AFA), /* ~= 10^119 */ + U64(0xC646D635, 0x01A1511D), U64(0xB281E1FD, 0x541501B8), /* ~= 10^120 */ + U64(0xF7D88BC2, 0x4209A565), U64(0x1F225A7C, 0xA91A4226), /* ~= 10^121 */ + U64(0x9AE75759, 0x6946075F), U64(0x3375788D, 0xE9B06958), /* ~= 10^122 */ + U64(0xC1A12D2F, 0xC3978937), U64(0x0052D6B1, 0x641C83AE), /* ~= 10^123 */ + U64(0xF209787B, 0xB47D6B84), U64(0xC0678C5D, 0xBD23A49A), /* ~= 10^124 */ + U64(0x9745EB4D, 0x50CE6332), U64(0xF840B7BA, 0x963646E0), /* ~= 10^125 */ + U64(0xBD176620, 0xA501FBFF), U64(0xB650E5A9, 0x3BC3D898), /* ~= 10^126 */ + U64(0xEC5D3FA8, 0xCE427AFF), U64(0xA3E51F13, 0x8AB4CEBE), /* ~= 10^127 */ + U64(0x93BA47C9, 0x80E98CDF), U64(0xC66F336C, 0x36B10137), /* ~= 10^128 */ + U64(0xB8A8D9BB, 0xE123F017), U64(0xB80B0047, 0x445D4184), /* ~= 10^129 */ + U64(0xE6D3102A, 0xD96CEC1D), U64(0xA60DC059, 0x157491E5), /* ~= 10^130 */ + U64(0x9043EA1A, 0xC7E41392), U64(0x87C89837, 0xAD68DB2F), /* ~= 10^131 */ + U64(0xB454E4A1, 0x79DD1877), U64(0x29BABE45, 0x98C311FB), /* ~= 10^132 */ + U64(0xE16A1DC9, 0xD8545E94), U64(0xF4296DD6, 0xFEF3D67A), /* ~= 10^133 */ + U64(0x8CE2529E, 0x2734BB1D), U64(0x1899E4A6, 0x5F58660C), /* ~= 10^134 */ + U64(0xB01AE745, 0xB101E9E4), U64(0x5EC05DCF, 0xF72E7F8F), /* ~= 10^135 */ + U64(0xDC21A117, 0x1D42645D), U64(0x76707543, 0xF4FA1F73), /* ~= 10^136 */ + U64(0x899504AE, 0x72497EBA), U64(0x6A06494A, 0x791C53A8), /* ~= 10^137 */ + U64(0xABFA45DA, 0x0EDBDE69), U64(0x0487DB9D, 0x17636892), /* ~= 10^138 */ + U64(0xD6F8D750, 0x9292D603), U64(0x45A9D284, 0x5D3C42B6), /* ~= 10^139 */ + U64(0x865B8692, 0x5B9BC5C2), U64(0x0B8A2392, 0xBA45A9B2), /* ~= 10^140 */ + U64(0xA7F26836, 0xF282B732), U64(0x8E6CAC77, 0x68D7141E), /* ~= 10^141 */ + U64(0xD1EF0244, 0xAF2364FF), U64(0x3207D795, 0x430CD926), /* ~= 10^142 */ + U64(0x8335616A, 0xED761F1F), U64(0x7F44E6BD, 0x49E807B8), /* ~= 10^143 */ + U64(0xA402B9C5, 0xA8D3A6E7), U64(0x5F16206C, 0x9C6209A6), /* ~= 10^144 */ + U64(0xCD036837, 0x130890A1), U64(0x36DBA887, 0xC37A8C0F), /* ~= 10^145 */ + U64(0x80222122, 0x6BE55A64), U64(0xC2494954, 0xDA2C9789), /* ~= 10^146 */ + U64(0xA02AA96B, 0x06DEB0FD), U64(0xF2DB9BAA, 0x10B7BD6C), /* ~= 10^147 */ + U64(0xC83553C5, 0xC8965D3D), U64(0x6F928294, 0x94E5ACC7), /* ~= 10^148 */ + U64(0xFA42A8B7, 0x3ABBF48C), U64(0xCB772339, 0xBA1F17F9), /* ~= 10^149 */ + U64(0x9C69A972, 0x84B578D7), U64(0xFF2A7604, 0x14536EFB), /* ~= 10^150 */ + U64(0xC38413CF, 0x25E2D70D), U64(0xFEF51385, 0x19684ABA), /* ~= 10^151 */ + U64(0xF46518C2, 0xEF5B8CD1), U64(0x7EB25866, 0x5FC25D69), /* ~= 10^152 */ + U64(0x98BF2F79, 0xD5993802), U64(0xEF2F773F, 0xFBD97A61), /* ~= 10^153 */ + U64(0xBEEEFB58, 0x4AFF8603), U64(0xAAFB550F, 0xFACFD8FA), /* ~= 10^154 */ + U64(0xEEAABA2E, 0x5DBF6784), U64(0x95BA2A53, 0xF983CF38), /* ~= 10^155 */ + U64(0x952AB45C, 0xFA97A0B2), U64(0xDD945A74, 0x7BF26183), /* ~= 10^156 */ + U64(0xBA756174, 0x393D88DF), U64(0x94F97111, 0x9AEEF9E4), /* ~= 10^157 */ + U64(0xE912B9D1, 0x478CEB17), U64(0x7A37CD56, 0x01AAB85D), /* ~= 10^158 */ + U64(0x91ABB422, 0xCCB812EE), U64(0xAC62E055, 0xC10AB33A), /* ~= 10^159 */ + U64(0xB616A12B, 0x7FE617AA), U64(0x577B986B, 0x314D6009), /* ~= 10^160 */ + U64(0xE39C4976, 0x5FDF9D94), U64(0xED5A7E85, 0xFDA0B80B), /* ~= 10^161 */ + U64(0x8E41ADE9, 0xFBEBC27D), U64(0x14588F13, 0xBE847307), /* ~= 10^162 */ + U64(0xB1D21964, 0x7AE6B31C), U64(0x596EB2D8, 0xAE258FC8), /* ~= 10^163 */ + U64(0xDE469FBD, 0x99A05FE3), U64(0x6FCA5F8E, 0xD9AEF3BB), /* ~= 10^164 */ + U64(0x8AEC23D6, 0x80043BEE), U64(0x25DE7BB9, 0x480D5854), /* ~= 10^165 */ + U64(0xADA72CCC, 0x20054AE9), U64(0xAF561AA7, 0x9A10AE6A), /* ~= 10^166 */ + U64(0xD910F7FF, 0x28069DA4), U64(0x1B2BA151, 0x8094DA04), /* ~= 10^167 */ + U64(0x87AA9AFF, 0x79042286), U64(0x90FB44D2, 0xF05D0842), /* ~= 10^168 */ + U64(0xA99541BF, 0x57452B28), U64(0x353A1607, 0xAC744A53), /* ~= 10^169 */ + U64(0xD3FA922F, 0x2D1675F2), U64(0x42889B89, 0x97915CE8), /* ~= 10^170 */ + U64(0x847C9B5D, 0x7C2E09B7), U64(0x69956135, 0xFEBADA11), /* ~= 10^171 */ + U64(0xA59BC234, 0xDB398C25), U64(0x43FAB983, 0x7E699095), /* ~= 10^172 */ + U64(0xCF02B2C2, 0x1207EF2E), U64(0x94F967E4, 0x5E03F4BB), /* ~= 10^173 */ + U64(0x8161AFB9, 0x4B44F57D), U64(0x1D1BE0EE, 0xBAC278F5), /* ~= 10^174 */ + U64(0xA1BA1BA7, 0x9E1632DC), U64(0x6462D92A, 0x69731732), /* ~= 10^175 */ + U64(0xCA28A291, 0x859BBF93), U64(0x7D7B8F75, 0x03CFDCFE), /* ~= 10^176 */ + U64(0xFCB2CB35, 0xE702AF78), U64(0x5CDA7352, 0x44C3D43E), /* ~= 10^177 */ + U64(0x9DEFBF01, 0xB061ADAB), U64(0x3A088813, 0x6AFA64A7), /* ~= 10^178 */ + U64(0xC56BAEC2, 0x1C7A1916), U64(0x088AAA18, 0x45B8FDD0), /* ~= 10^179 */ + U64(0xF6C69A72, 0xA3989F5B), U64(0x8AAD549E, 0x57273D45), /* ~= 10^180 */ + U64(0x9A3C2087, 0xA63F6399), U64(0x36AC54E2, 0xF678864B), /* ~= 10^181 */ + U64(0xC0CB28A9, 0x8FCF3C7F), U64(0x84576A1B, 0xB416A7DD), /* ~= 10^182 */ + U64(0xF0FDF2D3, 0xF3C30B9F), U64(0x656D44A2, 0xA11C51D5), /* ~= 10^183 */ + U64(0x969EB7C4, 0x7859E743), U64(0x9F644AE5, 0xA4B1B325), /* ~= 10^184 */ + U64(0xBC4665B5, 0x96706114), U64(0x873D5D9F, 0x0DDE1FEE), /* ~= 10^185 */ + U64(0xEB57FF22, 0xFC0C7959), U64(0xA90CB506, 0xD155A7EA), /* ~= 10^186 */ + U64(0x9316FF75, 0xDD87CBD8), U64(0x09A7F124, 0x42D588F2), /* ~= 10^187 */ + U64(0xB7DCBF53, 0x54E9BECE), U64(0x0C11ED6D, 0x538AEB2F), /* ~= 10^188 */ + U64(0xE5D3EF28, 0x2A242E81), U64(0x8F1668C8, 0xA86DA5FA), /* ~= 10^189 */ + U64(0x8FA47579, 0x1A569D10), U64(0xF96E017D, 0x694487BC), /* ~= 10^190 */ + U64(0xB38D92D7, 0x60EC4455), U64(0x37C981DC, 0xC395A9AC), /* ~= 10^191 */ + U64(0xE070F78D, 0x3927556A), U64(0x85BBE253, 0xF47B1417), /* ~= 10^192 */ + U64(0x8C469AB8, 0x43B89562), U64(0x93956D74, 0x78CCEC8E), /* ~= 10^193 */ + U64(0xAF584166, 0x54A6BABB), U64(0x387AC8D1, 0x970027B2), /* ~= 10^194 */ + U64(0xDB2E51BF, 0xE9D0696A), U64(0x06997B05, 0xFCC0319E), /* ~= 10^195 */ + U64(0x88FCF317, 0xF22241E2), U64(0x441FECE3, 0xBDF81F03), /* ~= 10^196 */ + U64(0xAB3C2FDD, 0xEEAAD25A), U64(0xD527E81C, 0xAD7626C3), /* ~= 10^197 */ + U64(0xD60B3BD5, 0x6A5586F1), U64(0x8A71E223, 0xD8D3B074), /* ~= 10^198 */ + U64(0x85C70565, 0x62757456), U64(0xF6872D56, 0x67844E49), /* ~= 10^199 */ + U64(0xA738C6BE, 0xBB12D16C), U64(0xB428F8AC, 0x016561DB), /* ~= 10^200 */ + U64(0xD106F86E, 0x69D785C7), U64(0xE13336D7, 0x01BEBA52), /* ~= 10^201 */ + U64(0x82A45B45, 0x0226B39C), U64(0xECC00246, 0x61173473), /* ~= 10^202 */ + U64(0xA34D7216, 0x42B06084), U64(0x27F002D7, 0xF95D0190), /* ~= 10^203 */ + U64(0xCC20CE9B, 0xD35C78A5), U64(0x31EC038D, 0xF7B441F4), /* ~= 10^204 */ + U64(0xFF290242, 0xC83396CE), U64(0x7E670471, 0x75A15271), /* ~= 10^205 */ + U64(0x9F79A169, 0xBD203E41), U64(0x0F0062C6, 0xE984D386), /* ~= 10^206 */ + U64(0xC75809C4, 0x2C684DD1), U64(0x52C07B78, 0xA3E60868), /* ~= 10^207 */ + U64(0xF92E0C35, 0x37826145), U64(0xA7709A56, 0xCCDF8A82), /* ~= 10^208 */ + U64(0x9BBCC7A1, 0x42B17CCB), U64(0x88A66076, 0x400BB691), /* ~= 10^209 */ + U64(0xC2ABF989, 0x935DDBFE), U64(0x6ACFF893, 0xD00EA435), /* ~= 10^210 */ + U64(0xF356F7EB, 0xF83552FE), U64(0x0583F6B8, 0xC4124D43), /* ~= 10^211 */ + U64(0x98165AF3, 0x7B2153DE), U64(0xC3727A33, 0x7A8B704A), /* ~= 10^212 */ + U64(0xBE1BF1B0, 0x59E9A8D6), U64(0x744F18C0, 0x592E4C5C), /* ~= 10^213 */ + U64(0xEDA2EE1C, 0x7064130C), U64(0x1162DEF0, 0x6F79DF73), /* ~= 10^214 */ + U64(0x9485D4D1, 0xC63E8BE7), U64(0x8ADDCB56, 0x45AC2BA8), /* ~= 10^215 */ + U64(0xB9A74A06, 0x37CE2EE1), U64(0x6D953E2B, 0xD7173692), /* ~= 10^216 */ + U64(0xE8111C87, 0xC5C1BA99), U64(0xC8FA8DB6, 0xCCDD0437), /* ~= 10^217 */ + U64(0x910AB1D4, 0xDB9914A0), U64(0x1D9C9892, 0x400A22A2), /* ~= 10^218 */ + U64(0xB54D5E4A, 0x127F59C8), U64(0x2503BEB6, 0xD00CAB4B), /* ~= 10^219 */ + U64(0xE2A0B5DC, 0x971F303A), U64(0x2E44AE64, 0x840FD61D), /* ~= 10^220 */ + U64(0x8DA471A9, 0xDE737E24), U64(0x5CEAECFE, 0xD289E5D2), /* ~= 10^221 */ + U64(0xB10D8E14, 0x56105DAD), U64(0x7425A83E, 0x872C5F47), /* ~= 10^222 */ + U64(0xDD50F199, 0x6B947518), U64(0xD12F124E, 0x28F77719), /* ~= 10^223 */ + U64(0x8A5296FF, 0xE33CC92F), U64(0x82BD6B70, 0xD99AAA6F), /* ~= 10^224 */ + U64(0xACE73CBF, 0xDC0BFB7B), U64(0x636CC64D, 0x1001550B), /* ~= 10^225 */ + U64(0xD8210BEF, 0xD30EFA5A), U64(0x3C47F7E0, 0x5401AA4E), /* ~= 10^226 */ + U64(0x8714A775, 0xE3E95C78), U64(0x65ACFAEC, 0x34810A71), /* ~= 10^227 */ + U64(0xA8D9D153, 0x5CE3B396), U64(0x7F1839A7, 0x41A14D0D), /* ~= 10^228 */ + U64(0xD31045A8, 0x341CA07C), U64(0x1EDE4811, 0x1209A050), /* ~= 10^229 */ + U64(0x83EA2B89, 0x2091E44D), U64(0x934AED0A, 0xAB460432), /* ~= 10^230 */ + U64(0xA4E4B66B, 0x68B65D60), U64(0xF81DA84D, 0x5617853F), /* ~= 10^231 */ + U64(0xCE1DE406, 0x42E3F4B9), U64(0x36251260, 0xAB9D668E), /* ~= 10^232 */ + U64(0x80D2AE83, 0xE9CE78F3), U64(0xC1D72B7C, 0x6B426019), /* ~= 10^233 */ + U64(0xA1075A24, 0xE4421730), U64(0xB24CF65B, 0x8612F81F), /* ~= 10^234 */ + U64(0xC94930AE, 0x1D529CFC), U64(0xDEE033F2, 0x6797B627), /* ~= 10^235 */ + U64(0xFB9B7CD9, 0xA4A7443C), U64(0x169840EF, 0x017DA3B1), /* ~= 10^236 */ + U64(0x9D412E08, 0x06E88AA5), U64(0x8E1F2895, 0x60EE864E), /* ~= 10^237 */ + U64(0xC491798A, 0x08A2AD4E), U64(0xF1A6F2BA, 0xB92A27E2), /* ~= 10^238 */ + U64(0xF5B5D7EC, 0x8ACB58A2), U64(0xAE10AF69, 0x6774B1DB), /* ~= 10^239 */ + U64(0x9991A6F3, 0xD6BF1765), U64(0xACCA6DA1, 0xE0A8EF29), /* ~= 10^240 */ + U64(0xBFF610B0, 0xCC6EDD3F), U64(0x17FD090A, 0x58D32AF3), /* ~= 10^241 */ + U64(0xEFF394DC, 0xFF8A948E), U64(0xDDFC4B4C, 0xEF07F5B0), /* ~= 10^242 */ + U64(0x95F83D0A, 0x1FB69CD9), U64(0x4ABDAF10, 0x1564F98E), /* ~= 10^243 */ + U64(0xBB764C4C, 0xA7A4440F), U64(0x9D6D1AD4, 0x1ABE37F1), /* ~= 10^244 */ + U64(0xEA53DF5F, 0xD18D5513), U64(0x84C86189, 0x216DC5ED), /* ~= 10^245 */ + U64(0x92746B9B, 0xE2F8552C), U64(0x32FD3CF5, 0xB4E49BB4), /* ~= 10^246 */ + U64(0xB7118682, 0xDBB66A77), U64(0x3FBC8C33, 0x221DC2A1), /* ~= 10^247 */ + U64(0xE4D5E823, 0x92A40515), U64(0x0FABAF3F, 0xEAA5334A), /* ~= 10^248 */ + U64(0x8F05B116, 0x3BA6832D), U64(0x29CB4D87, 0xF2A7400E), /* ~= 10^249 */ + U64(0xB2C71D5B, 0xCA9023F8), U64(0x743E20E9, 0xEF511012), /* ~= 10^250 */ + U64(0xDF78E4B2, 0xBD342CF6), U64(0x914DA924, 0x6B255416), /* ~= 10^251 */ + U64(0x8BAB8EEF, 0xB6409C1A), U64(0x1AD089B6, 0xC2F7548E), /* ~= 10^252 */ + U64(0xAE9672AB, 0xA3D0C320), U64(0xA184AC24, 0x73B529B1), /* ~= 10^253 */ + U64(0xDA3C0F56, 0x8CC4F3E8), U64(0xC9E5D72D, 0x90A2741E), /* ~= 10^254 */ + U64(0x88658996, 0x17FB1871), U64(0x7E2FA67C, 0x7A658892), /* ~= 10^255 */ + U64(0xAA7EEBFB, 0x9DF9DE8D), U64(0xDDBB901B, 0x98FEEAB7), /* ~= 10^256 */ + U64(0xD51EA6FA, 0x85785631), U64(0x552A7422, 0x7F3EA565), /* ~= 10^257 */ + U64(0x8533285C, 0x936B35DE), U64(0xD53A8895, 0x8F87275F), /* ~= 10^258 */ + U64(0xA67FF273, 0xB8460356), U64(0x8A892ABA, 0xF368F137), /* ~= 10^259 */ + U64(0xD01FEF10, 0xA657842C), U64(0x2D2B7569, 0xB0432D85), /* ~= 10^260 */ + U64(0x8213F56A, 0x67F6B29B), U64(0x9C3B2962, 0x0E29FC73), /* ~= 10^261 */ + U64(0xA298F2C5, 0x01F45F42), U64(0x8349F3BA, 0x91B47B8F), /* ~= 10^262 */ + U64(0xCB3F2F76, 0x42717713), U64(0x241C70A9, 0x36219A73), /* ~= 10^263 */ + U64(0xFE0EFB53, 0xD30DD4D7), U64(0xED238CD3, 0x83AA0110), /* ~= 10^264 */ + U64(0x9EC95D14, 0x63E8A506), U64(0xF4363804, 0x324A40AA), /* ~= 10^265 */ + U64(0xC67BB459, 0x7CE2CE48), U64(0xB143C605, 0x3EDCD0D5), /* ~= 10^266 */ + U64(0xF81AA16F, 0xDC1B81DA), U64(0xDD94B786, 0x8E94050A), /* ~= 10^267 */ + U64(0x9B10A4E5, 0xE9913128), U64(0xCA7CF2B4, 0x191C8326), /* ~= 10^268 */ + U64(0xC1D4CE1F, 0x63F57D72), U64(0xFD1C2F61, 0x1F63A3F0), /* ~= 10^269 */ + U64(0xF24A01A7, 0x3CF2DCCF), U64(0xBC633B39, 0x673C8CEC), /* ~= 10^270 */ + U64(0x976E4108, 0x8617CA01), U64(0xD5BE0503, 0xE085D813), /* ~= 10^271 */ + U64(0xBD49D14A, 0xA79DBC82), U64(0x4B2D8644, 0xD8A74E18), /* ~= 10^272 */ + U64(0xEC9C459D, 0x51852BA2), U64(0xDDF8E7D6, 0x0ED1219E), /* ~= 10^273 */ + U64(0x93E1AB82, 0x52F33B45), U64(0xCABB90E5, 0xC942B503), /* ~= 10^274 */ + U64(0xB8DA1662, 0xE7B00A17), U64(0x3D6A751F, 0x3B936243), /* ~= 10^275 */ + U64(0xE7109BFB, 0xA19C0C9D), U64(0x0CC51267, 0x0A783AD4), /* ~= 10^276 */ + U64(0x906A617D, 0x450187E2), U64(0x27FB2B80, 0x668B24C5), /* ~= 10^277 */ + U64(0xB484F9DC, 0x9641E9DA), U64(0xB1F9F660, 0x802DEDF6), /* ~= 10^278 */ + U64(0xE1A63853, 0xBBD26451), U64(0x5E7873F8, 0xA0396973), /* ~= 10^279 */ + U64(0x8D07E334, 0x55637EB2), U64(0xDB0B487B, 0x6423E1E8), /* ~= 10^280 */ + U64(0xB049DC01, 0x6ABC5E5F), U64(0x91CE1A9A, 0x3D2CDA62), /* ~= 10^281 */ + U64(0xDC5C5301, 0xC56B75F7), U64(0x7641A140, 0xCC7810FB), /* ~= 10^282 */ + U64(0x89B9B3E1, 0x1B6329BA), U64(0xA9E904C8, 0x7FCB0A9D), /* ~= 10^283 */ + U64(0xAC2820D9, 0x623BF429), U64(0x546345FA, 0x9FBDCD44), /* ~= 10^284 */ + U64(0xD732290F, 0xBACAF133), U64(0xA97C1779, 0x47AD4095), /* ~= 10^285 */ + U64(0x867F59A9, 0xD4BED6C0), U64(0x49ED8EAB, 0xCCCC485D), /* ~= 10^286 */ + U64(0xA81F3014, 0x49EE8C70), U64(0x5C68F256, 0xBFFF5A74), /* ~= 10^287 */ + U64(0xD226FC19, 0x5C6A2F8C), U64(0x73832EEC, 0x6FFF3111), /* ~= 10^288 */ + U64(0x83585D8F, 0xD9C25DB7), U64(0xC831FD53, 0xC5FF7EAB), /* ~= 10^289 */ + U64(0xA42E74F3, 0xD032F525), U64(0xBA3E7CA8, 0xB77F5E55), /* ~= 10^290 */ + U64(0xCD3A1230, 0xC43FB26F), U64(0x28CE1BD2, 0xE55F35EB), /* ~= 10^291 */ + U64(0x80444B5E, 0x7AA7CF85), U64(0x7980D163, 0xCF5B81B3), /* ~= 10^292 */ + U64(0xA0555E36, 0x1951C366), U64(0xD7E105BC, 0xC332621F), /* ~= 10^293 */ + U64(0xC86AB5C3, 0x9FA63440), U64(0x8DD9472B, 0xF3FEFAA7), /* ~= 10^294 */ + U64(0xFA856334, 0x878FC150), U64(0xB14F98F6, 0xF0FEB951), /* ~= 10^295 */ + U64(0x9C935E00, 0xD4B9D8D2), U64(0x6ED1BF9A, 0x569F33D3), /* ~= 10^296 */ + U64(0xC3B83581, 0x09E84F07), U64(0x0A862F80, 0xEC4700C8), /* ~= 10^297 */ + U64(0xF4A642E1, 0x4C6262C8), U64(0xCD27BB61, 0x2758C0FA), /* ~= 10^298 */ + U64(0x98E7E9CC, 0xCFBD7DBD), U64(0x8038D51C, 0xB897789C), /* ~= 10^299 */ + U64(0xBF21E440, 0x03ACDD2C), U64(0xE0470A63, 0xE6BD56C3), /* ~= 10^300 */ + U64(0xEEEA5D50, 0x04981478), U64(0x1858CCFC, 0xE06CAC74), /* ~= 10^301 */ + U64(0x95527A52, 0x02DF0CCB), U64(0x0F37801E, 0x0C43EBC8), /* ~= 10^302 */ + U64(0xBAA718E6, 0x8396CFFD), U64(0xD3056025, 0x8F54E6BA), /* ~= 10^303 */ + U64(0xE950DF20, 0x247C83FD), U64(0x47C6B82E, 0xF32A2069), /* ~= 10^304 */ + U64(0x91D28B74, 0x16CDD27E), U64(0x4CDC331D, 0x57FA5441), /* ~= 10^305 */ + U64(0xB6472E51, 0x1C81471D), U64(0xE0133FE4, 0xADF8E952), /* ~= 10^306 */ + U64(0xE3D8F9E5, 0x63A198E5), U64(0x58180FDD, 0xD97723A6), /* ~= 10^307 */ + U64(0x8E679C2F, 0x5E44FF8F), U64(0x570F09EA, 0xA7EA7648), /* ~= 10^308 */ + U64(0xB201833B, 0x35D63F73), U64(0x2CD2CC65, 0x51E513DA), /* ~= 10^309 */ + U64(0xDE81E40A, 0x034BCF4F), U64(0xF8077F7E, 0xA65E58D1), /* ~= 10^310 */ + U64(0x8B112E86, 0x420F6191), U64(0xFB04AFAF, 0x27FAF782), /* ~= 10^311 */ + U64(0xADD57A27, 0xD29339F6), U64(0x79C5DB9A, 0xF1F9B563), /* ~= 10^312 */ + U64(0xD94AD8B1, 0xC7380874), U64(0x18375281, 0xAE7822BC), /* ~= 10^313 */ + U64(0x87CEC76F, 0x1C830548), U64(0x8F229391, 0x0D0B15B5), /* ~= 10^314 */ + U64(0xA9C2794A, 0xE3A3C69A), U64(0xB2EB3875, 0x504DDB22), /* ~= 10^315 */ + U64(0xD433179D, 0x9C8CB841), U64(0x5FA60692, 0xA46151EB), /* ~= 10^316 */ + U64(0x849FEEC2, 0x81D7F328), U64(0xDBC7C41B, 0xA6BCD333), /* ~= 10^317 */ + U64(0xA5C7EA73, 0x224DEFF3), U64(0x12B9B522, 0x906C0800), /* ~= 10^318 */ + U64(0xCF39E50F, 0xEAE16BEF), U64(0xD768226B, 0x34870A00), /* ~= 10^319 */ + U64(0x81842F29, 0xF2CCE375), U64(0xE6A11583, 0x00D46640), /* ~= 10^320 */ + U64(0xA1E53AF4, 0x6F801C53), U64(0x60495AE3, 0xC1097FD0), /* ~= 10^321 */ + U64(0xCA5E89B1, 0x8B602368), U64(0x385BB19C, 0xB14BDFC4), /* ~= 10^322 */ + U64(0xFCF62C1D, 0xEE382C42), U64(0x46729E03, 0xDD9ED7B5), /* ~= 10^323 */ + U64(0x9E19DB92, 0xB4E31BA9), U64(0x6C07A2C2, 0x6A8346D1) /* ~= 10^324 */ +}; + +/** + Get the cached pow10 value from `pow10_sig_table`. + @param exp10 The exponent of pow(10, e). This value must in range + `POW10_SIG_TABLE_MIN_EXP` to `POW10_SIG_TABLE_MAX_EXP`. + @param hi The highest 64 bits of pow(10, e). + @param lo The lower 64 bits after `hi`. + */ +static_inline void pow10_table_get_sig(i32 exp10, u64 *hi, u64 *lo) { + i32 idx = exp10 - (POW10_SIG_TABLE_MIN_EXP); + *hi = pow10_sig_table[idx * 2]; + *lo = pow10_sig_table[idx * 2 + 1]; +} + +/** + Get the exponent (base 2) for highest 64 bits significand in `pow10_sig_table`. + */ +static_inline void pow10_table_get_exp(i32 exp10, i32 *exp2) { + /* e2 = floor(log2(pow(10, e))) - 64 + 1 */ + /* = floor(e * log2(10) - 63) */ + *exp2 = (exp10 * 217706 - 4128768) >> 16; +} + +#endif + + + +/*============================================================================== + * MARK: - Number and Bit Utils (Private) + *============================================================================*/ + +/** Convert bits to double. */ +static_inline f64 f64_from_bits(u64 u) { + f64 f; + memcpy(&f, &u, sizeof(u)); + return f; +} + +/** Convert double to bits. */ +static_inline u64 f64_to_bits(f64 f) { + u64 u; + memcpy(&u, &f, sizeof(u)); + return u; +} + +/** Convert double to bits. */ +static_inline u32 f32_to_bits(f32 f) { + u32 u; + memcpy(&u, &f, sizeof(u)); + return u; +} + +/** Get 'infinity' bits with sign. */ +static_inline u64 f64_bits_inf(bool sign) { +#if YYJSON_HAS_IEEE_754 + return F64_BITS_INF | ((u64)sign << 63); +#elif defined(INFINITY) + return f64_to_bits(sign ? -INFINITY : INFINITY); +#else + return f64_to_bits(sign ? -HUGE_VAL : HUGE_VAL); +#endif +} + +/** Get 'nan' bits with sign. */ +static_inline u64 f64_bits_nan(bool sign) { +#if YYJSON_HAS_IEEE_754 + return F64_BITS_NAN | ((u64)sign << 63); +#elif defined(NAN) + return f64_to_bits(sign ? (f64)-NAN : (f64)NAN); +#else + return f64_to_bits((sign ? -0.0 : 0.0) / 0.0); +#endif +} + +/** Casting double to float, allow overflow. */ +#if yyjson_has_attribute(no_sanitize) +__attribute__((no_sanitize("undefined"))) +#elif yyjson_gcc_available(4, 9, 0) +__attribute__((__no_sanitize_undefined__)) +#endif +static_inline f32 f64_to_f32(f64 val) { + return (f32)val; +} + +/** Returns the number of leading 0-bits in value (input should not be 0). */ +static_inline u32 u64_lz_bits(u64 v) { +#if GCC_HAS_CLZLL + return (u32)__builtin_clzll(v); +#elif MSC_HAS_BIT_SCAN_64 + unsigned long r; + _BitScanReverse64(&r, v); + return (u32)63 - (u32)r; +#elif MSC_HAS_BIT_SCAN + unsigned long hi, lo; + bool hi_set = _BitScanReverse(&hi, (u32)(v >> 32)) != 0; + _BitScanReverse(&lo, (u32)v); + hi |= 32; + return (u32)63 - (u32)(hi_set ? hi : lo); +#else + /* branchless, use De Bruijn sequence */ + /* see: https://www.chessprogramming.org/BitScan */ + const u8 table[64] = { + 63, 16, 62, 7, 15, 36, 61, 3, 6, 14, 22, 26, 35, 47, 60, 2, + 9, 5, 28, 11, 13, 21, 42, 19, 25, 31, 34, 40, 46, 52, 59, 1, + 17, 8, 37, 4, 23, 27, 48, 10, 29, 12, 43, 20, 32, 41, 53, 18, + 38, 24, 49, 30, 44, 33, 54, 39, 50, 45, 55, 51, 56, 57, 58, 0 + }; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return table[(v * U64(0x03F79D71, 0xB4CB0A89)) >> 58]; +#endif +} + +/** Returns the number of trailing 0-bits in value (input should not be 0). */ +static_inline u32 u64_tz_bits(u64 v) { +#if GCC_HAS_CTZLL + return (u32)__builtin_ctzll(v); +#elif MSC_HAS_BIT_SCAN_64 + unsigned long r; + _BitScanForward64(&r, v); + return (u32)r; +#elif MSC_HAS_BIT_SCAN + unsigned long lo, hi; + bool lo_set = _BitScanForward(&lo, (u32)(v)) != 0; + _BitScanForward(&hi, (u32)(v >> 32)); + hi += 32; + return lo_set ? lo : hi; +#else + /* branchless, use De Bruijn sequence */ + /* see: https://www.chessprogramming.org/BitScan */ + const u8 table[64] = { + 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, + 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, + 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, + 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 + }; + return table[((v & (~v + 1)) * U64(0x022FDD63, 0xCC95386D)) >> 58]; +#endif +} + +/** Multiplies two 64-bit unsigned integers (a * b), + returns the 128-bit result as 'hi' and 'lo'. */ +static_inline void u128_mul(u64 a, u64 b, u64 *hi, u64 *lo) { +#if YYJSON_HAS_INT128 + u128 m = (u128)a * b; + *hi = (u64)(m >> 64); + *lo = (u64)(m); +#elif MSC_HAS_UMUL128 + *lo = _umul128(a, b, hi); +#else + u32 a0 = (u32)(a), a1 = (u32)(a >> 32); + u32 b0 = (u32)(b), b1 = (u32)(b >> 32); + u64 p00 = (u64)a0 * b0, p01 = (u64)a0 * b1; + u64 p10 = (u64)a1 * b0, p11 = (u64)a1 * b1; + u64 m0 = p01 + (p00 >> 32); + u32 m00 = (u32)(m0), m01 = (u32)(m0 >> 32); + u64 m1 = p10 + m00; + u32 m10 = (u32)(m1), m11 = (u32)(m1 >> 32); + *hi = p11 + m01 + m11; + *lo = ((u64)m10 << 32) | (u32)p00; +#endif +} + +/** Multiplies two 64-bit unsigned integers and add a value (a * b + c), + returns the 128-bit result as 'hi' and 'lo'. */ +static_inline void u128_mul_add(u64 a, u64 b, u64 c, u64 *hi, u64 *lo) { +#if YYJSON_HAS_INT128 + u128 m = (u128)a * b + c; + *hi = (u64)(m >> 64); + *lo = (u64)(m); +#else + u64 h, l, t; + u128_mul(a, b, &h, &l); + t = l + c; + h += (u64)(((t < l) | (t < c))); + *hi = h; + *lo = t; +#endif +} + + + +/*============================================================================== + * MARK: - File Utils (Private) + * These functions are used to read and write JSON files. + *============================================================================*/ + +#define YYJSON_FOPEN_E +#if !defined(_MSC_VER) && defined(__GLIBC__) && defined(__GLIBC_PREREQ) +# if __GLIBC_PREREQ(2, 7) +# undef YYJSON_FOPEN_E +# define YYJSON_FOPEN_E "e" /* glibc extension to enable O_CLOEXEC */ +# endif +#endif + +static_inline FILE *fopen_safe(const char *path, const char *mode) { +#if YYJSON_MSC_VER >= 1400 + FILE *file = NULL; + if (fopen_s(&file, path, mode) != 0) return NULL; + return file; +#else + return fopen(path, mode); +#endif +} + +static_inline FILE *fopen_readonly(const char *path) { + return fopen_safe(path, "rb" YYJSON_FOPEN_E); +} + +static_inline FILE *fopen_writeonly(const char *path) { + return fopen_safe(path, "wb" YYJSON_FOPEN_E); +} + +static_inline usize fread_safe(void *buf, usize size, FILE *file) { +#if YYJSON_MSC_VER >= 1400 + return fread_s(buf, size, 1, size, file); +#else + return fread(buf, 1, size, file); +#endif +} + + + +/*============================================================================== + * MARK: - Size Utils (Private) + * These functions are used for memory allocation. + *============================================================================*/ + +/** Returns whether the size is overflow after increment. */ +static_inline bool size_add_is_overflow(usize size, usize add) { + return size > (size + add); +} + +/** Returns whether the size is power of 2 (size should not be 0). */ +static_inline bool size_is_pow2(usize size) { + return (size & (size - 1)) == 0; +} + +/** Align size upwards (may overflow). */ +static_inline usize size_align_up(usize size, usize align) { + if (size_is_pow2(align)) { + return (size + (align - 1)) & ~(align - 1); + } else { + return size + align - (size + align - 1) % align - 1; + } +} + +/** Align size downwards. */ +static_inline usize size_align_down(usize size, usize align) { + if (size_is_pow2(align)) { + return size & ~(align - 1); + } else { + return size - (size % align); + } +} + +/** Align address upwards (may overflow). */ +static_inline void *mem_align_up(void *mem, usize align) { + usize size; + memcpy(&size, &mem, sizeof(usize)); + size = size_align_up(size, align); + memcpy(&mem, &size, sizeof(usize)); + return mem; +} + + + +/*============================================================================== + * MARK: - Default Memory Allocator (Private) + * This is a simple libc memory allocator wrapper. + *============================================================================*/ + +static void *default_malloc(void *ctx, usize size) { + return malloc(size); +} + +static void *default_realloc(void *ctx, void *ptr, usize old_size, usize size) { + return realloc(ptr, size); +} + +static void default_free(void *ctx, void *ptr) { + free(ptr); +} + +static const yyjson_alc YYJSON_DEFAULT_ALC = { + default_malloc, default_realloc, default_free, NULL +}; + + + +/*============================================================================== + * MARK: - Null Memory Allocator (Private) + * This allocator is just a placeholder to ensure that the internal + * malloc/realloc/free function pointers are not null. + *============================================================================*/ + +static void *null_malloc(void *ctx, usize size) { + return NULL; +} + +static void *null_realloc(void *ctx, void *ptr, usize old_size, usize size) { + return NULL; +} + +static void null_free(void *ctx, void *ptr) { + return; +} + +static const yyjson_alc YYJSON_NULL_ALC = { + null_malloc, null_realloc, null_free, NULL +}; + + + +/*============================================================================== + * MARK: - Pool Memory Allocator (Public) + * This allocator is initialized with a fixed-size buffer. + * The buffer is split into multiple memory chunks for memory allocation. + *============================================================================*/ + +/** memory chunk header */ +typedef struct pool_chunk { + usize size; /* chunk memory size, include chunk header */ + struct pool_chunk *next; /* linked list, nullable */ + /* char mem[]; flexible array member */ +} pool_chunk; + +/** allocator ctx header */ +typedef struct pool_ctx { + usize size; /* total memory size, include ctx header */ + pool_chunk *free_list; /* linked list, nullable */ + /* pool_chunk chunks[]; flexible array member */ +} pool_ctx; + +/** align up the input size to chunk size */ +static_inline void pool_size_align(usize *size) { + *size = size_align_up(*size, sizeof(pool_chunk)) + sizeof(pool_chunk); +} + +static void *pool_malloc(void *ctx_ptr, usize size) { + /* assert(size != 0) */ + pool_ctx *ctx = (pool_ctx *)ctx_ptr; + pool_chunk *next, *prev = NULL, *cur = ctx->free_list; + + if (unlikely(size >= ctx->size)) return NULL; + pool_size_align(&size); + + while (cur) { + if (cur->size < size) { + /* not enough space, try next chunk */ + prev = cur; + cur = cur->next; + continue; + } + if (cur->size >= size + sizeof(pool_chunk) * 2) { + /* too much space, split this chunk */ + next = (pool_chunk *)(void *)((u8 *)cur + size); + next->size = cur->size - size; + next->next = cur->next; + cur->size = size; + } else { + /* just enough space, use whole chunk */ + next = cur->next; + } + if (prev) prev->next = next; + else ctx->free_list = next; + return (void *)(cur + 1); + } + return NULL; +} + +static void pool_free(void *ctx_ptr, void *ptr) { + /* assert(ptr != NULL) */ + pool_ctx *ctx = (pool_ctx *)ctx_ptr; + pool_chunk *cur = ((pool_chunk *)ptr) - 1; + pool_chunk *prev = NULL, *next = ctx->free_list; + + while (next && next < cur) { + prev = next; + next = next->next; + } + if (prev) prev->next = cur; + else ctx->free_list = cur; + cur->next = next; + + if (next && ((u8 *)cur + cur->size) == (u8 *)next) { + /* merge cur to higher chunk */ + cur->size += next->size; + cur->next = next->next; + } + if (prev && ((u8 *)prev + prev->size) == (u8 *)cur) { + /* merge cur to lower chunk */ + prev->size += cur->size; + prev->next = cur->next; + } +} + +static void *pool_realloc(void *ctx_ptr, void *ptr, + usize old_size, usize size) { + /* assert(ptr != NULL && size != 0 && old_size < size) */ + pool_ctx *ctx = (pool_ctx *)ctx_ptr; + pool_chunk *cur = ((pool_chunk *)ptr) - 1, *prev, *next, *tmp; + + /* check size */ + if (unlikely(size >= ctx->size)) return NULL; + pool_size_align(&old_size); + pool_size_align(&size); + if (unlikely(old_size == size)) return ptr; + + /* find next and prev chunk */ + prev = NULL; + next = ctx->free_list; + while (next && next < cur) { + prev = next; + next = next->next; + } + + if ((u8 *)cur + cur->size == (u8 *)next && cur->size + next->size >= size) { + /* merge to higher chunk if they are contiguous */ + usize free_size = cur->size + next->size - size; + if (free_size > sizeof(pool_chunk) * 2) { + tmp = (pool_chunk *)(void *)((u8 *)cur + size); + if (prev) prev->next = tmp; + else ctx->free_list = tmp; + tmp->next = next->next; + tmp->size = free_size; + cur->size = size; + } else { + if (prev) prev->next = next->next; + else ctx->free_list = next->next; + cur->size += next->size; + } + return ptr; + } else { + /* fallback to malloc and memcpy */ + void *new_ptr = pool_malloc(ctx_ptr, size - sizeof(pool_chunk)); + if (new_ptr) { + memcpy(new_ptr, ptr, cur->size - sizeof(pool_chunk)); + pool_free(ctx_ptr, ptr); + } + return new_ptr; + } +} + +bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, usize size) { + pool_chunk *chunk; + pool_ctx *ctx; + + if (unlikely(!alc)) return false; + *alc = YYJSON_NULL_ALC; + if (size < sizeof(pool_ctx) * 4) return false; + ctx = (pool_ctx *)mem_align_up(buf, sizeof(pool_ctx)); + if (unlikely(!ctx)) return false; + size -= (usize)((u8 *)ctx - (u8 *)buf); + size = size_align_down(size, sizeof(pool_ctx)); + + chunk = (pool_chunk *)(ctx + 1); + chunk->size = size - sizeof(pool_ctx); + chunk->next = NULL; + ctx->size = size; + ctx->free_list = chunk; + + alc->malloc = pool_malloc; + alc->realloc = pool_realloc; + alc->free = pool_free; + alc->ctx = (void *)ctx; + return true; +} + + + +/*============================================================================== + * MARK: - Dynamic Memory Allocator (Public) + * This allocator allocates memory on demand and does not immediately release + * unused memory. Instead, it places the unused memory into a freelist for + * potential reuse in the future. It is only when the entire allocator is + * destroyed that all previously allocated memory is released at once. + *============================================================================*/ + +/** memory chunk header */ +typedef struct dyn_chunk { + usize size; /* chunk size, include header */ + struct dyn_chunk *next; + /* char mem[]; flexible array member */ +} dyn_chunk; + +/** allocator ctx header */ +typedef struct { + dyn_chunk free_list; /* dummy header, sorted from small to large */ + dyn_chunk used_list; /* dummy header */ +} dyn_ctx; + +/** align up the input size to chunk size */ +static_inline bool dyn_size_align(usize *size) { + usize alc_size = *size + sizeof(dyn_chunk); + alc_size = size_align_up(alc_size, YYJSON_ALC_DYN_MIN_SIZE); + if (unlikely(alc_size < *size)) return false; /* overflow */ + *size = alc_size; + return true; +} + +/** remove a chunk from list (the chunk must already be in the list) */ +static_inline void dyn_chunk_list_remove(dyn_chunk *list, dyn_chunk *chunk) { + dyn_chunk *prev = list, *cur; + for (cur = prev->next; cur; cur = cur->next) { + if (cur == chunk) { + prev->next = cur->next; + cur->next = NULL; + return; + } + prev = cur; + } +} + +/** add a chunk to list header (the chunk must not be in the list) */ +static_inline void dyn_chunk_list_add(dyn_chunk *list, dyn_chunk *chunk) { + chunk->next = list->next; + list->next = chunk; +} + +static void *dyn_malloc(void *ctx_ptr, usize size) { + /* assert(size != 0) */ + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *chunk, *prev; + if (unlikely(!dyn_size_align(&size))) return NULL; + + /* freelist is empty, create new chunk */ + if (!ctx->free_list.next) { + chunk = (dyn_chunk *)def.malloc(def.ctx, size); + if (unlikely(!chunk)) return NULL; + chunk->size = size; + chunk->next = NULL; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + + /* find a large enough chunk, or resize the largest chunk */ + prev = &ctx->free_list; + while (true) { + chunk = prev->next; + if (chunk->size >= size) { /* enough size, reuse this chunk */ + prev->next = chunk->next; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + if (!chunk->next) { /* resize the largest chunk */ + chunk = (dyn_chunk *)def.realloc(def.ctx, chunk, chunk->size, size); + if (unlikely(!chunk)) return NULL; + prev->next = NULL; + chunk->size = size; + dyn_chunk_list_add(&ctx->used_list, chunk); + return (void *)(chunk + 1); + } + prev = chunk; + } +} + +static void *dyn_realloc(void *ctx_ptr, void *ptr, + usize old_size, usize size) { + /* assert(ptr != NULL && size != 0 && old_size < size) */ + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *new_chunk, *chunk = (dyn_chunk *)ptr - 1; + if (unlikely(!dyn_size_align(&size))) return NULL; + if (chunk->size >= size) return ptr; + + dyn_chunk_list_remove(&ctx->used_list, chunk); + new_chunk = (dyn_chunk *)def.realloc(def.ctx, chunk, chunk->size, size); + if (likely(new_chunk)) { + new_chunk->size = size; + chunk = new_chunk; + } + dyn_chunk_list_add(&ctx->used_list, chunk); + return new_chunk ? (void *)(new_chunk + 1) : NULL; +} + +static void dyn_free(void *ctx_ptr, void *ptr) { + /* assert(ptr != NULL) */ + dyn_ctx *ctx = (dyn_ctx *)ctx_ptr; + dyn_chunk *chunk = (dyn_chunk *)ptr - 1, *prev; + + dyn_chunk_list_remove(&ctx->used_list, chunk); + for (prev = &ctx->free_list; prev; prev = prev->next) { + if (!prev->next || prev->next->size >= chunk->size) { + chunk->next = prev->next; + prev->next = chunk; + break; + } + } +} + +yyjson_alc *yyjson_alc_dyn_new(void) { + const yyjson_alc def = YYJSON_DEFAULT_ALC; + usize hdr_len = sizeof(yyjson_alc) + sizeof(dyn_ctx); + yyjson_alc *alc = (yyjson_alc *)def.malloc(def.ctx, hdr_len); + dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + if (unlikely(!alc)) return NULL; + alc->malloc = dyn_malloc; + alc->realloc = dyn_realloc; + alc->free = dyn_free; + alc->ctx = alc + 1; + memset(ctx, 0, sizeof(*ctx)); + return alc; +} + +void yyjson_alc_dyn_free(yyjson_alc *alc) { + const yyjson_alc def = YYJSON_DEFAULT_ALC; + dyn_ctx *ctx = (dyn_ctx *)(void *)(alc + 1); + dyn_chunk *chunk, *next; + if (unlikely(!alc)) return; + for (chunk = ctx->free_list.next; chunk; chunk = next) { + next = chunk->next; + def.free(def.ctx, chunk); + } + for (chunk = ctx->used_list.next; chunk; chunk = next) { + next = chunk->next; + def.free(def.ctx, chunk); + } + def.free(def.ctx, alc); +} + + + +/*============================================================================== + * MARK: - JSON Struct Utils (Public) + * These functions are used for creating, copying, releasing, and comparing + * JSON documents and values. They are widely used throughout this library. + *============================================================================*/ + +static_inline void unsafe_yyjson_str_pool_release(yyjson_str_pool *pool, + yyjson_alc *alc) { + yyjson_str_chunk *chunk = pool->chunks, *next; + while (chunk) { + next = chunk->next; + alc->free(alc->ctx, chunk); + chunk = next; + } +} + +static_inline void unsafe_yyjson_val_pool_release(yyjson_val_pool *pool, + yyjson_alc *alc) { + yyjson_val_chunk *chunk = pool->chunks, *next; + while (chunk) { + next = chunk->next; + alc->free(alc->ctx, chunk); + chunk = next; + } +} + +bool unsafe_yyjson_str_pool_grow(yyjson_str_pool *pool, + const yyjson_alc *alc, usize len) { + yyjson_str_chunk *chunk; + usize size, max_len; + + /* create a new chunk */ + max_len = USIZE_MAX - sizeof(yyjson_str_chunk); + if (unlikely(len > max_len)) return false; + size = len + sizeof(yyjson_str_chunk); + size = yyjson_max(pool->chunk_size, size); + chunk = (yyjson_str_chunk *)alc->malloc(alc->ctx, size); + if (unlikely(!chunk)) return false; + + /* insert the new chunk as the head of the linked list */ + chunk->next = pool->chunks; + chunk->chunk_size = size; + pool->chunks = chunk; + pool->cur = (char *)chunk + sizeof(yyjson_str_chunk); + pool->end = (char *)chunk + size; + + /* the next chunk is twice the size of the current one */ + size = yyjson_min(pool->chunk_size * 2, pool->chunk_size_max); + if (size < pool->chunk_size) size = pool->chunk_size_max; /* overflow */ + pool->chunk_size = size; + return true; +} + +bool unsafe_yyjson_val_pool_grow(yyjson_val_pool *pool, + const yyjson_alc *alc, usize count) { + yyjson_val_chunk *chunk; + usize size, max_count; + + /* create a new chunk */ + max_count = USIZE_MAX / sizeof(yyjson_mut_val) - 1; + if (unlikely(count > max_count)) return false; + size = (count + 1) * sizeof(yyjson_mut_val); + size = yyjson_max(pool->chunk_size, size); + chunk = (yyjson_val_chunk *)alc->malloc(alc->ctx, size); + if (unlikely(!chunk)) return false; + + /* insert the new chunk as the head of the linked list */ + chunk->next = pool->chunks; + chunk->chunk_size = size; + pool->chunks = chunk; + pool->cur = (yyjson_mut_val *)(void *)((u8 *)chunk) + 1; + pool->end = (yyjson_mut_val *)(void *)((u8 *)chunk + size); + + /* the next chunk is twice the size of the current one */ + size = yyjson_min(pool->chunk_size * 2, pool->chunk_size_max); + if (size < pool->chunk_size) size = pool->chunk_size_max; /* overflow */ + pool->chunk_size = size; + return true; +} + +bool yyjson_mut_doc_set_str_pool_size(yyjson_mut_doc *doc, size_t len) { + usize max_size = USIZE_MAX - sizeof(yyjson_str_chunk); + if (!doc || !len || len > max_size) return false; + doc->str_pool.chunk_size = len + sizeof(yyjson_str_chunk); + return true; +} + +bool yyjson_mut_doc_set_val_pool_size(yyjson_mut_doc *doc, size_t count) { + usize max_count = USIZE_MAX / sizeof(yyjson_mut_val) - 1; + if (!doc || !count || count > max_count) return false; + doc->val_pool.chunk_size = (count + 1) * sizeof(yyjson_mut_val); + return true; +} + +void yyjson_mut_doc_free(yyjson_mut_doc *doc) { + if (doc) { + yyjson_alc alc = doc->alc; + memset(&doc->alc, 0, sizeof(alc)); + unsafe_yyjson_str_pool_release(&doc->str_pool, &alc); + unsafe_yyjson_val_pool_release(&doc->val_pool, &alc); + alc.free(alc.ctx, doc); + } +} + +yyjson_mut_doc *yyjson_mut_doc_new(const yyjson_alc *alc) { + yyjson_mut_doc *doc; + if (!alc) alc = &YYJSON_DEFAULT_ALC; + doc = (yyjson_mut_doc *)alc->malloc(alc->ctx, sizeof(yyjson_mut_doc)); + if (!doc) return NULL; + memset(doc, 0, sizeof(yyjson_mut_doc)); + + doc->alc = *alc; + doc->str_pool.chunk_size = YYJSON_MUT_DOC_STR_POOL_INIT_SIZE; + doc->str_pool.chunk_size_max = YYJSON_MUT_DOC_STR_POOL_MAX_SIZE; + doc->val_pool.chunk_size = YYJSON_MUT_DOC_VAL_POOL_INIT_SIZE; + doc->val_pool.chunk_size_max = YYJSON_MUT_DOC_VAL_POOL_MAX_SIZE; + return doc; +} + +yyjson_mut_doc *yyjson_doc_mut_copy(yyjson_doc *doc, const yyjson_alc *alc) { + yyjson_mut_doc *m_doc; + yyjson_mut_val *m_val; + + if (!doc || !doc->root) return NULL; + m_doc = yyjson_mut_doc_new(alc); + if (!m_doc) return NULL; + m_val = yyjson_val_mut_copy(m_doc, doc->root); + if (!m_val) { + yyjson_mut_doc_free(m_doc); + return NULL; + } + yyjson_mut_doc_set_root(m_doc, m_val); + return m_doc; +} + +yyjson_mut_doc *yyjson_mut_doc_mut_copy(yyjson_mut_doc *doc, + const yyjson_alc *alc) { + yyjson_mut_doc *m_doc; + yyjson_mut_val *m_val; + + if (!doc) return NULL; + if (!doc->root) return yyjson_mut_doc_new(alc); + + m_doc = yyjson_mut_doc_new(alc); + if (!m_doc) return NULL; + m_val = yyjson_mut_val_mut_copy(m_doc, doc->root); + if (!m_val) { + yyjson_mut_doc_free(m_doc); + return NULL; + } + yyjson_mut_doc_set_root(m_doc, m_val); + return m_doc; +} + +yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *m_doc, + yyjson_val *i_vals) { + /* + The immutable object or array stores all sub-values in a contiguous memory, + We copy them to another contiguous memory as mutable values, + then reconnect the mutable values with the original relationship. + */ + usize i_vals_len; + yyjson_mut_val *m_vals, *m_val; + yyjson_val *i_val, *i_end; + + if (!m_doc || !i_vals) return NULL; + i_end = unsafe_yyjson_get_next(i_vals); + i_vals_len = (usize)(unsafe_yyjson_get_next(i_vals) - i_vals); + m_vals = unsafe_yyjson_mut_val(m_doc, i_vals_len); + if (!m_vals) return NULL; + i_val = i_vals; + m_val = m_vals; + + for (; i_val < i_end; i_val++, m_val++) { + yyjson_type type = unsafe_yyjson_get_type(i_val); + m_val->tag = i_val->tag; + m_val->uni.u64 = i_val->uni.u64; + if (type == YYJSON_TYPE_STR || type == YYJSON_TYPE_RAW) { + const char *str = i_val->uni.str; + usize str_len = unsafe_yyjson_get_len(i_val); + m_val->uni.str = unsafe_yyjson_mut_strncpy(m_doc, str, str_len); + if (!m_val->uni.str) return NULL; + } else if (type == YYJSON_TYPE_ARR) { + usize len = unsafe_yyjson_get_len(i_val); + if (len > 0) { + yyjson_val *ii_val = i_val + 1, *ii_next; + yyjson_mut_val *mm_val = m_val + 1, *mm_ctn = m_val, *mm_next; + while (len-- > 1) { + ii_next = unsafe_yyjson_get_next(ii_val); + mm_next = mm_val + (ii_next - ii_val); + mm_val->next = mm_next; + ii_val = ii_next; + mm_val = mm_next; + } + mm_val->next = mm_ctn + 1; + mm_ctn->uni.ptr = mm_val; + } + } else if (type == YYJSON_TYPE_OBJ) { + usize len = unsafe_yyjson_get_len(i_val); + if (len > 0) { + yyjson_val *ii_key = i_val + 1, *ii_nextkey; + yyjson_mut_val *mm_key = m_val + 1, *mm_ctn = m_val; + yyjson_mut_val *mm_nextkey; + while (len-- > 1) { + ii_nextkey = unsafe_yyjson_get_next(ii_key + 1); + mm_nextkey = mm_key + (ii_nextkey - ii_key); + mm_key->next = mm_key + 1; + mm_key->next->next = mm_nextkey; + ii_key = ii_nextkey; + mm_key = mm_nextkey; + } + mm_key->next = mm_key + 1; + mm_key->next->next = mm_ctn + 1; + mm_ctn->uni.ptr = mm_key; + } + } + } + return m_vals; +} + +static yyjson_mut_val *unsafe_yyjson_mut_val_mut_copy(yyjson_mut_doc *m_doc, + yyjson_mut_val *m_vals) { + /* + The mutable object or array stores all sub-values in a circular linked + list, so we can traverse them in the same loop. The traversal starts from + the last item, continues with the first item in a list, and ends with the + second to last item, which needs to be linked to the last item to close the + circle. + */ + yyjson_mut_val *m_val = unsafe_yyjson_mut_val(m_doc, 1); + if (unlikely(!m_val)) return NULL; + m_val->tag = m_vals->tag; + + switch (unsafe_yyjson_get_type(m_vals)) { + case YYJSON_TYPE_OBJ: + case YYJSON_TYPE_ARR: + if (unsafe_yyjson_get_len(m_vals) > 0) { + yyjson_mut_val *last = (yyjson_mut_val *)m_vals->uni.ptr; + yyjson_mut_val *next = last->next, *prev; + prev = unsafe_yyjson_mut_val_mut_copy(m_doc, last); + if (!prev) return NULL; + m_val->uni.ptr = (void *)prev; + while (next != last) { + prev->next = unsafe_yyjson_mut_val_mut_copy(m_doc, next); + if (!prev->next) return NULL; + prev = prev->next; + next = next->next; + } + prev->next = (yyjson_mut_val *)m_val->uni.ptr; + } + break; + case YYJSON_TYPE_RAW: + case YYJSON_TYPE_STR: { + const char *str = m_vals->uni.str; + usize str_len = unsafe_yyjson_get_len(m_vals); + m_val->uni.str = unsafe_yyjson_mut_strncpy(m_doc, str, str_len); + if (!m_val->uni.str) return NULL; + break; + } + default: + m_val->uni = m_vals->uni; + break; + } + return m_val; +} + +yyjson_mut_val *yyjson_mut_val_mut_copy(yyjson_mut_doc *doc, + yyjson_mut_val *val) { + if (doc && val) return unsafe_yyjson_mut_val_mut_copy(doc, val); + return NULL; +} + +/* Count the number of values and the total length of the strings. */ +static void yyjson_mut_stat(yyjson_mut_val *val, + usize *val_sum, usize *str_sum) { + yyjson_type type = unsafe_yyjson_get_type(val); + *val_sum += 1; + if (type == YYJSON_TYPE_ARR || type == YYJSON_TYPE_OBJ) { + yyjson_mut_val *child = (yyjson_mut_val *)val->uni.ptr; + usize len = unsafe_yyjson_get_len(val), i; + len <<= (u8)(type == YYJSON_TYPE_OBJ); + *val_sum += len; + for (i = 0; i < len; i++) { + yyjson_type stype = unsafe_yyjson_get_type(child); + if (stype == YYJSON_TYPE_STR || stype == YYJSON_TYPE_RAW) { + *str_sum += unsafe_yyjson_get_len(child) + 1; + } else if (stype == YYJSON_TYPE_ARR || stype == YYJSON_TYPE_OBJ) { + yyjson_mut_stat(child, val_sum, str_sum); + *val_sum -= 1; + } + child = child->next; + } + } else if (type == YYJSON_TYPE_STR || type == YYJSON_TYPE_RAW) { + *str_sum += unsafe_yyjson_get_len(val) + 1; + } +} + +/* Copy mutable values to immutable value pool. */ +static usize yyjson_imut_copy(yyjson_val **val_ptr, char **buf_ptr, + yyjson_mut_val *mval) { + yyjson_val *val = *val_ptr; + yyjson_type type = unsafe_yyjson_get_type(mval); + if (type == YYJSON_TYPE_ARR || type == YYJSON_TYPE_OBJ) { + yyjson_mut_val *child = (yyjson_mut_val *)mval->uni.ptr; + usize len = unsafe_yyjson_get_len(mval), i; + usize val_sum = 1; + if (type == YYJSON_TYPE_OBJ) { + if (len) child = child->next->next; + len <<= 1; + } else { + if (len) child = child->next; + } + *val_ptr = val + 1; + for (i = 0; i < len; i++) { + val_sum += yyjson_imut_copy(val_ptr, buf_ptr, child); + child = child->next; + } + val->tag = mval->tag; + val->uni.ofs = val_sum * sizeof(yyjson_val); + return val_sum; + } else if (type == YYJSON_TYPE_STR || type == YYJSON_TYPE_RAW) { + char *buf = *buf_ptr; + usize len = unsafe_yyjson_get_len(mval); + memcpy((void *)buf, (const void *)mval->uni.str, len); + buf[len] = '\0'; + val->tag = mval->tag; + val->uni.str = buf; + *val_ptr = val + 1; + *buf_ptr = buf + len + 1; + return 1; + } else { + val->tag = mval->tag; + val->uni = mval->uni; + *val_ptr = val + 1; + return 1; + } +} + +yyjson_doc *yyjson_mut_doc_imut_copy(yyjson_mut_doc *mdoc, + const yyjson_alc *alc) { + if (!mdoc) return NULL; + return yyjson_mut_val_imut_copy(mdoc->root, alc); +} + +yyjson_doc *yyjson_mut_val_imut_copy(yyjson_mut_val *mval, + const yyjson_alc *alc) { + usize val_num = 0, str_sum = 0, hdr_size, buf_size; + yyjson_doc *doc = NULL; + yyjson_val *val_hdr = NULL; + + /* This value should be NULL here. Setting a non-null value suppresses + warning from the clang analyzer. */ + char *str_hdr = (char *)(void *)&str_sum; + if (!mval) return NULL; + if (!alc) alc = &YYJSON_DEFAULT_ALC; + + /* traverse the input value to get pool size */ + yyjson_mut_stat(mval, &val_num, &str_sum); + + /* create doc and val pool */ + hdr_size = size_align_up(sizeof(yyjson_doc), sizeof(yyjson_val)); + buf_size = hdr_size + val_num * sizeof(yyjson_val); + doc = (yyjson_doc *)alc->malloc(alc->ctx, buf_size); + if (!doc) return NULL; + memset(doc, 0, sizeof(yyjson_doc)); + val_hdr = (yyjson_val *)(void *)((char *)(void *)doc + hdr_size); + doc->root = val_hdr; + doc->alc = *alc; + + /* create str pool */ + if (str_sum > 0) { + str_hdr = (char *)alc->malloc(alc->ctx, str_sum); + doc->str_pool = str_hdr; + if (!str_hdr) { + alc->free(alc->ctx, (void *)doc); + return NULL; + } + } + + /* copy vals and strs */ + doc->val_read = yyjson_imut_copy(&val_hdr, &str_hdr, mval); + doc->dat_read = str_sum + 1; + return doc; +} + +static_inline bool unsafe_yyjson_num_equals(void *lhs, void *rhs) { + yyjson_val_uni *luni = &((yyjson_val *)lhs)->uni; + yyjson_val_uni *runi = &((yyjson_val *)rhs)->uni; + yyjson_subtype lt = unsafe_yyjson_get_subtype(lhs); + yyjson_subtype rt = unsafe_yyjson_get_subtype(rhs); + if (lt == rt) return luni->u64 == runi->u64; + if (lt == YYJSON_SUBTYPE_SINT && rt == YYJSON_SUBTYPE_UINT) { + return luni->i64 >= 0 && luni->u64 == runi->u64; + } + if (lt == YYJSON_SUBTYPE_UINT && rt == YYJSON_SUBTYPE_SINT) { + return runi->i64 >= 0 && luni->u64 == runi->u64; + } + return false; +} + +static_inline bool unsafe_yyjson_str_equals(void *lhs, void *rhs) { + usize len = unsafe_yyjson_get_len(lhs); + if (len != unsafe_yyjson_get_len(rhs)) return false; + return !memcmp(unsafe_yyjson_get_str(lhs), + unsafe_yyjson_get_str(rhs), len); +} + +bool unsafe_yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { + yyjson_type type = unsafe_yyjson_get_type(lhs); + if (type != unsafe_yyjson_get_type(rhs)) return false; + + switch (type) { + case YYJSON_TYPE_OBJ: { + usize len = unsafe_yyjson_get_len(lhs); + if (len != unsafe_yyjson_get_len(rhs)) return false; + if (len > 0) { + yyjson_obj_iter iter; + yyjson_obj_iter_init(rhs, &iter); + lhs = unsafe_yyjson_get_first(lhs); + while (len-- > 0) { + rhs = yyjson_obj_iter_getn(&iter, lhs->uni.str, + unsafe_yyjson_get_len(lhs)); + if (!rhs) return false; + if (!unsafe_yyjson_equals(lhs + 1, rhs)) return false; + lhs = unsafe_yyjson_get_next(lhs + 1); + } + } + /* yyjson allows duplicate keys, so the check may be inaccurate */ + return true; + } + + case YYJSON_TYPE_ARR: { + usize len = unsafe_yyjson_get_len(lhs); + if (len != unsafe_yyjson_get_len(rhs)) return false; + if (len > 0) { + lhs = unsafe_yyjson_get_first(lhs); + rhs = unsafe_yyjson_get_first(rhs); + while (len-- > 0) { + if (!unsafe_yyjson_equals(lhs, rhs)) return false; + lhs = unsafe_yyjson_get_next(lhs); + rhs = unsafe_yyjson_get_next(rhs); + } + } + return true; + } + + case YYJSON_TYPE_NUM: + return unsafe_yyjson_num_equals(lhs, rhs); + + case YYJSON_TYPE_RAW: + case YYJSON_TYPE_STR: + return unsafe_yyjson_str_equals(lhs, rhs); + + case YYJSON_TYPE_NULL: + case YYJSON_TYPE_BOOL: + return lhs->tag == rhs->tag; + + default: + return false; + } +} + +bool unsafe_yyjson_mut_equals(yyjson_mut_val *lhs, yyjson_mut_val *rhs) { + yyjson_type type = unsafe_yyjson_get_type(lhs); + if (type != unsafe_yyjson_get_type(rhs)) return false; + + switch (type) { + case YYJSON_TYPE_OBJ: { + usize len = unsafe_yyjson_get_len(lhs); + if (len != unsafe_yyjson_get_len(rhs)) return false; + if (len > 0) { + yyjson_mut_obj_iter iter; + yyjson_mut_obj_iter_init(rhs, &iter); + lhs = (yyjson_mut_val *)lhs->uni.ptr; + while (len-- > 0) { + rhs = yyjson_mut_obj_iter_getn(&iter, lhs->uni.str, + unsafe_yyjson_get_len(lhs)); + if (!rhs) return false; + if (!unsafe_yyjson_mut_equals(lhs->next, rhs)) return false; + lhs = lhs->next->next; + } + } + /* yyjson allows duplicate keys, so the check may be inaccurate */ + return true; + } + + case YYJSON_TYPE_ARR: { + usize len = unsafe_yyjson_get_len(lhs); + if (len != unsafe_yyjson_get_len(rhs)) return false; + if (len > 0) { + lhs = (yyjson_mut_val *)lhs->uni.ptr; + rhs = (yyjson_mut_val *)rhs->uni.ptr; + while (len-- > 0) { + if (!unsafe_yyjson_mut_equals(lhs, rhs)) return false; + lhs = lhs->next; + rhs = rhs->next; + } + } + return true; + } + + case YYJSON_TYPE_NUM: + return unsafe_yyjson_num_equals(lhs, rhs); + + case YYJSON_TYPE_RAW: + case YYJSON_TYPE_STR: + return unsafe_yyjson_str_equals(lhs, rhs); + + case YYJSON_TYPE_NULL: + case YYJSON_TYPE_BOOL: + return lhs->tag == rhs->tag; + + default: + return false; + } +} + +bool yyjson_locate_pos(const char *str, size_t len, size_t pos, + size_t *line, size_t *col, size_t *chr) { + usize line_sum = 0, line_pos = 0, chr_sum = 0; + const u8 *cur = (const u8 *)str; + const u8 *end = cur + pos; + + if (!str || pos > len) { + if (line) *line = 0; + if (col) *col = 0; + if (chr) *chr = 0; + return false; + } + + if (pos >= 3 && is_utf8_bom(cur)) cur += 3; /* don't count BOM */ + while (cur < end) { + u8 c = *cur; + chr_sum += 1; + if (likely(c < 0x80)) { /* 0xxxxxxx (0x00-0x7F) ASCII */ + if (c == '\n') { + line_sum += 1; + line_pos = chr_sum; + } + cur += 1; + } + else if (c < 0xC0) cur += 1; /* 10xxxxxx (0x80-0xBF) Invalid */ + else if (c < 0xE0) cur += 2; /* 110xxxxx (0xC0-0xDF) 2-byte UTF-8 */ + else if (c < 0xF0) cur += 3; /* 1110xxxx (0xE0-0xEF) 3-byte UTF-8 */ + else if (c < 0xF8) cur += 4; /* 11110xxx (0xF0-0xF7) 4-byte UTF-8 */ + else cur += 1; /* 11111xxx (0xF8-0xFF) Invalid */ + } + if (line) *line = line_sum + 1; + if (col) *col = chr_sum - line_pos + 1; + if (chr) *chr = chr_sum; + return true; +} + + + +#if !YYJSON_DISABLE_READER /* reader begin */ + +/* Check read flag, avoids `always false` warning when disabled. */ +#define has_flg(_flg) unlikely(has_rflag(flg, YYJSON_READ_##_flg, 0)) +#define has_allow(_flg) unlikely(has_rflag(flg, YYJSON_READ_ALLOW_##_flg, 1)) +#define YYJSON_READ_ALLOW_TRIVIA (YYJSON_READ_ALLOW_COMMENTS | \ + YYJSON_READ_ALLOW_EXT_WHITESPACE) +static_inline bool has_rflag(yyjson_read_flag flg, yyjson_read_flag chk, + bool non_standard) { +#if YYJSON_DISABLE_NON_STANDARD + if (non_standard) return false; +#endif + return (flg & chk) != 0; +} + + + +/*============================================================================== + * MARK: - JSON Reader Utils (Private) + * These functions are used by JSON reader to read literals and comments. + *============================================================================*/ + +/** Read `true` literal, `*ptr[0]` should be `t`. */ +static_inline bool read_true(u8 **ptr, yyjson_val *val) { + u8 *cur = *ptr; + if (likely(byte_match_4(cur, "true"))) { + val->tag = YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_TRUE; + *ptr = cur + 4; + return true; + } + return false; +} + +/** Read `false` literal, `*ptr[0]` should be `f`. */ +static_inline bool read_false(u8 **ptr, yyjson_val *val) { + u8 *cur = *ptr; + if (likely(byte_match_4(cur + 1, "alse"))) { + val->tag = YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_FALSE; + *ptr = cur + 5; + return true; + } + return false; +} + +/** Read `null` literal, `*ptr[0]` should be `n`. */ +static_inline bool read_null(u8 **ptr, yyjson_val *val) { + u8 *cur = *ptr; + if (likely(byte_match_4(cur, "null"))) { + val->tag = YYJSON_TYPE_NULL; + *ptr = cur + 4; + return true; + } + return false; +} + +/** Read `Inf` or `Infinity` literal (ignoring case). */ +static_inline bool read_inf(u8 **ptr, u8 **pre, + yyjson_read_flag flg, yyjson_val *val) { + u8 *hdr = *ptr; + u8 *cur = *ptr; + u8 **end = ptr; + bool sign = (*cur == '-'); + if (*cur == '+' && !has_allow(EXT_NUMBER)) return false; + cur += char_is_sign(*cur); + if (char_to_lower(cur[0]) == 'i' && + char_to_lower(cur[1]) == 'n' && + char_to_lower(cur[2]) == 'f') { + if (char_to_lower(cur[3]) == 'i') { + if (char_to_lower(cur[4]) == 'n' && + char_to_lower(cur[5]) == 'i' && + char_to_lower(cur[6]) == 't' && + char_to_lower(cur[7]) == 'y') { + cur += 8; + } else { + return false; + } + } else { + cur += 3; + } + *end = cur; + if (has_flg(NUMBER_AS_RAW)) { + **pre = '\0'; /* add null-terminator for previous raw string */ + *pre = cur; /* save end position for current raw string */ + val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; + val->uni.str = (const char *)hdr; + } else { + val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; + val->uni.u64 = f64_bits_inf(sign); + } + return true; + } + return false; +} + +/** Read `NaN` literal (ignoring case). */ +static_inline bool read_nan(u8 **ptr, u8 **pre, + yyjson_read_flag flg, yyjson_val *val) { + u8 *hdr = *ptr; + u8 *cur = *ptr; + u8 **end = ptr; + bool sign = (*cur == '-'); + if (*cur == '+' && !has_allow(EXT_NUMBER)) return false; + cur += char_is_sign(*cur); + if (char_to_lower(cur[0]) == 'n' && + char_to_lower(cur[1]) == 'a' && + char_to_lower(cur[2]) == 'n') { + cur += 3; + *end = cur; + if (has_flg(NUMBER_AS_RAW)) { + **pre = '\0'; /* add null-terminator for previous raw string */ + *pre = cur; /* save end position for current raw string */ + val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; + val->uni.str = (const char *)hdr; + } else { + val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; + val->uni.u64 = f64_bits_nan(sign); + } + return true; + } + return false; +} + +/** Read `Inf`, `Infinity` or `NaN` literal (ignoring case). */ +static_inline bool read_inf_or_nan(u8 **ptr, u8 **pre, + yyjson_read_flag flg, yyjson_val *val) { + if (read_inf(ptr, pre, flg, val)) return true; + if (read_nan(ptr, pre, flg, val)) return true; + return false; +} + +/** Read a JSON number as raw string. */ +static_noinline bool read_num_raw(u8 **ptr, u8 **pre, yyjson_read_flag flg, + yyjson_val *val, const char **msg) { +#define return_err(_pos, _msg) do { \ + *msg = _msg; *end = _pos; return false; \ +} while (false) + +#define return_raw() do { \ + val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; \ + val->uni.str = (const char *)hdr; \ + **pre = '\0'; *pre = cur; *end = cur; return true; \ +} while (false) + + u8 *hdr = *ptr; + u8 *cur = *ptr; + u8 **end = ptr; + + /* skip sign */ + cur += (*cur == '-'); + + /* read first digit, check leading zero */ + while (unlikely(!char_is_digit(*cur))) { + if (has_allow(EXT_NUMBER)) { + if (*cur == '+' && cur == hdr) { /* leading `+` sign */ + cur++; + continue; + } + if (*cur == '.' && char_is_digit(cur[1])) { /* e.g. '.123' */ + goto read_double; + } + } + if (has_allow(INF_AND_NAN)) { + if (read_inf_or_nan(ptr, pre, flg, val)) return true; + } + return_err(cur, "no digit after sign"); + } + + /* read integral part */ + if (*cur == '0') { + cur++; + if (unlikely(char_is_digit(*cur))) { + return_err(cur - 1, "number with leading zero is not allowed"); + } + if (!char_is_fp(*cur)) { + if (has_allow(EXT_NUMBER) && char_to_lower(*cur) == 'x') { /* hex */ + if (!char_is_hex(*++cur)) return_err(cur, "invalid hex number"); + while(char_is_hex(*cur)) cur++; + } + return_raw(); + } + } else { + while (char_is_digit(*cur)) cur++; + if (!char_is_fp(*cur)) return_raw(); + } + +read_double: + /* read fraction part */ + if (*cur == '.') { + cur++; + if (!char_is_digit(*cur)) { + if (has_allow(EXT_NUMBER)) { + if (!char_is_exp(*cur)) return_raw(); + } else { + return_err(cur, "no digit after decimal point"); + } + } + while (char_is_digit(*cur)) cur++; + } + + /* read exponent part */ + if (char_is_exp(*cur)) { + cur += 1 + char_is_sign(cur[1]); + if (!char_is_digit(*cur++)) { + return_err(cur, "no digit after exponent sign"); + } + while (char_is_digit(*cur)) cur++; + } + + return_raw(); + +#undef return_err +#undef return_raw +} + +/** Read a hex number. */ +static_noinline bool read_num_hex(u8 **ptr, u8 **pre, yyjson_read_flag flg, + yyjson_val *val, const char **msg) { + u8 *hdr = *ptr; + u8 *cur = *ptr; + u8 **end = ptr; + u64 sig = 0, i = 0; + bool sign; + + /* skip sign and '0x' */ + sign = (*cur == '-'); + cur += (*cur == '-' || *cur == '+') + 2; + + /* read hex */ + for(; i < 16; i++) { + u8 c = hex_conv_table[cur[i]]; + if (c == 0xF0) break; + sig <<= 4; + sig |= c; + } + + /* check error */ + if (unlikely(i == 0)) { + *msg = "invalid hex number"; + return false; + } + + /* check overflow */ + if (unlikely(i == 16)) { + if (char_is_hex(cur[16]) || (sign && sig > ((u64)1 << 63))) { + if (!has_flg(BIGNUM_AS_RAW)) { + *msg = "hex number overflow"; + return false; + } + cur += 16; + while (char_is_hex(*cur)) cur++; + **pre = '\0'; + val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; + val->uni.str = (const char *)hdr; + *pre = cur; *end = cur; + return true; + } + } + + val->tag = YYJSON_TYPE_NUM | (u64)((u8)sign << 3); + val->uni.u64 = (u64)(sign ? (u64)(~(sig) + 1) : (u64)(sig)); + *end = cur + i; + return true; +} + +/** + Skip trivia (whitespace and comments). + This function should be used only when `char_is_trivia()` returns true. + @param ptr (inout) Input current position, output end position. + @param eof JSON end position. + @param flg JSON read flags. + @return true if at least one character was skipped. + false if no characters were skipped, + or if a multi-line comment is unterminated; + in the latter case, `ptr` will be set to `eof`. + */ +static_noinline bool skip_trivia(u8 **ptr, u8 *eof, yyjson_read_flag flg) { + u8 *hdr = *ptr, *cur = *ptr; + usize len; + + while (cur < eof) { + u8 *loop_begin = cur; + + /* skip standard whitespace */ + while(char_is_space(*cur)) cur++; + + /* skip extended whitespace */ + if (has_allow(EXT_WHITESPACE)) { + while (char_is_space_ext(*cur)) { + cur += (len = ext_space_len(cur)); + if (!len) break; + } + } + + /* skip comment, do not validate encoding */ + if (has_allow(COMMENTS) && cur[0] == '/') { + if (cur[1] == '/') { /* single-line comment */ + cur += 2; + if (has_allow(EXT_WHITESPACE)) { + while (cur < eof) { + if (char_is_eol_ext(*cur)) { + cur += (len = ext_eol_len(cur)); + if (len) break; + } + cur++; + } + } else { + while (cur < eof && !char_is_eol(*cur)) cur++; + } + } else if (cur[1] == '*') { /* multi-line comment */ + cur += 2; + while (!byte_match_2(cur, "*/") && cur < eof) cur++; + if (cur == eof) { + *ptr = eof; + return false; /* unclosed comment */ + } + cur += 2; + } + } + if (cur == loop_begin) break; + } + *ptr = cur; + return cur > hdr; +} + +/** + Check truncated UTF-8 character. + Return true if `cur` starts a valid UTF-8 sequence that is truncated. + */ +static bool is_truncated_utf8(u8 *cur, u8 *eof) { + u8 c0, c1, c2; + usize len = (usize)(eof - cur); + if (cur >= eof || len >= 4) return false; + c0 = cur[0]; c1 = cur[1]; c2 = cur[2]; + /* 1-byte UTF-8, not truncated */ + if (c0 < 0x80) return false; + if (len == 1) { + /* 2-byte UTF-8, truncated */ + if ((c0 & 0xE0) == 0xC0 && (c0 & 0x1E) != 0x00) return true; + /* 3-byte UTF-8, truncated */ + if ((c0 & 0xF0) == 0xE0) return true; + /* 4-byte UTF-8, truncated */ + if ((c0 & 0xF8) == 0xF0 && (c0 & 0x07) <= 0x04) return true; + } else if (len == 2) { + /* 3-byte UTF-8, truncated */ + if ((c0 & 0xF0) == 0xE0 && (c1 & 0xC0) == 0x80) { + u8 t = (u8)(((c0 & 0x0F) << 1) | ((c1 & 0x20) >> 5)); + return 0x01 <= t && t != 0x1B; + } + /* 4-byte UTF-8, truncated */ + if ((c0 & 0xF8) == 0xF0 && (c1 & 0xC0) == 0x80) { + u8 t = (u8)(((c0 & 0x07) << 2) | ((c1 & 0x30) >> 4)); + return 0x01 <= t && t <= 0x10; + } + } else if (len == 3) { + /* 4 bytes UTF-8, truncated */ + if ((c0 & 0xF8) == 0xF0 && (c1 & 0xC0) == 0x80 && (c2 & 0xC0) == 0x80) { + u8 t = (u8)(((c0 & 0x07) << 2) | ((c1 & 0x30) >> 4)); + return 0x01 <= t && t <= 0x10; + } + } + return false; +} + +/** + Check truncated string. + Returns true if `cur` match `str` but is truncated. + The `str` should be lowercase ASCII letters. + */ +static bool is_truncated_str(u8 *cur, u8 *eof, const char *str, + bool case_sensitive) { + usize len = strlen(str); + if (cur + len <= eof || eof <= cur) return false; + if (case_sensitive) { + return memcmp(cur, str, (usize)(eof - cur)) == 0; + } + for (; cur < eof; cur++, str++) { + if (char_to_lower(*cur) != *(const u8 *)str) return false; + } + return true; +} + +/** + Check truncated JSON on parsing errors. + Returns true if the input is valid but truncated. + */ +static_noinline bool is_truncated_end(u8 *hdr, u8 *cur, u8 *eof, + yyjson_read_code code, + yyjson_read_flag flg) { + if (cur >= eof) return true; + if (code == YYJSON_READ_ERROR_LITERAL) { + if (is_truncated_str(cur, eof, "true", true) || + is_truncated_str(cur, eof, "false", true) || + is_truncated_str(cur, eof, "null", true)) { + return true; + } + } + if (code == YYJSON_READ_ERROR_UNEXPECTED_CHARACTER || + code == YYJSON_READ_ERROR_INVALID_NUMBER || + code == YYJSON_READ_ERROR_LITERAL) { + if (has_allow(INF_AND_NAN)) { + if (*cur == '-') cur++; + if (is_truncated_str(cur, eof, "infinity", false) || + is_truncated_str(cur, eof, "nan", false)) { + return true; + } + } + } + if (code == YYJSON_READ_ERROR_UNEXPECTED_CONTENT) { + if (has_allow(INF_AND_NAN)) { + if (hdr + 3 <= cur && + is_truncated_str(cur - 3, eof, "infinity", false)) { + return true; /* e.g. infin would be read as inf + in */ + } + } + } + if (code == YYJSON_READ_ERROR_INVALID_STRING) { + usize len = (usize)(eof - cur); + + /* unicode escape sequence */ + if (*cur == '\\') { + if (len == 1) return true; + if (len <= 5) { + if (*++cur != 'u') return false; + for (++cur; cur < eof; cur++) { + if (!char_is_hex(*cur)) return false; + } + return true; + } else if (len <= 11) { + /* incomplete surrogate pair? */ + u16 hi; + if (*++cur != 'u') return false; + if (!hex_load_4(++cur, &hi)) return false; + if ((hi & 0xF800) != 0xD800) return false; + cur += 4; + if (cur >= eof) return true; + /* valid low surrogate is DC00...DFFF */ + if (*cur != '\\') return false; + if (++cur >= eof) return true; + if (*cur != 'u') return false; + if (++cur >= eof) return true; + if (*cur != 'd' && *cur != 'D') return false; + if (++cur >= eof) return true; + if ((*cur < 'c' || *cur > 'f') && (*cur < 'C' || *cur > 'F')) + return false; + if (++cur >= eof) return true; + if (!char_is_hex(*cur)) return false; + return true; + } + return false; + } + + /* 2 to 4 bytes UTF-8 */ + if (is_truncated_utf8(cur, eof)) { + return true; + } + } + if (has_allow(COMMENTS)) { + if (code == YYJSON_READ_ERROR_INVALID_COMMENT) { + /* unclosed multiline comment */ + return true; + } + if (code == YYJSON_READ_ERROR_UNEXPECTED_CHARACTER && + *cur == '/' && cur + 1 == eof) { + /* truncated beginning of comment */ + return true; + } + } + if (code == YYJSON_READ_ERROR_UNEXPECTED_CHARACTER && + has_allow(BOM)) { + /* truncated UTF-8 BOM */ + usize len = (usize)(eof - cur); + if (cur == hdr && len < 3 && !memcmp(hdr, "\xEF\xBB\xBF", len)) { + return true; + } + } + return false; +} + + + +#if !YYJSON_DISABLE_FAST_FP_CONV /* FP_READER */ + +/*============================================================================== + * MARK: - BigInt For Floating Point Number Reader (Private) + * + * The bigint algorithm is used by floating-point number reader to get correctly + * rounded result for numbers with lots of digits. This part of code is rarely + * used for common numbers. + *============================================================================*/ + +/** Unsigned arbitrarily large integer */ +typedef struct bigint { + u32 used; /* used chunks count, should not be 0 */ + u64 bits[64]; /* chunks (58 is enough here) */ +} bigint; + +/** + Evaluate 'big += val'. + @param big A big number (can be 0). + @param val An unsigned integer (can be 0). + */ +static_inline void bigint_add_u64(bigint *big, u64 val) { + u32 idx, max; + u64 num = big->bits[0]; + u64 add = num + val; + big->bits[0] = add; + if (likely((add >= num) || (add >= val))) return; + for ((void)(idx = 1), max = big->used; idx < max; idx++) { + if (likely(big->bits[idx] != U64_MAX)) { + big->bits[idx] += 1; + return; + } + big->bits[idx] = 0; + } + big->bits[big->used++] = 1; +} + +/** + Evaluate 'big *= val'. + @param big A big number (can be 0). + @param val An unsigned integer (cannot be 0). + */ +static_inline void bigint_mul_u64(bigint *big, u64 val) { + u32 idx = 0, max = big->used; + u64 hi, lo, carry = 0; + for (; idx < max; idx++) { + if (big->bits[idx]) break; + } + for (; idx < max; idx++) { + u128_mul_add(big->bits[idx], val, carry, &hi, &lo); + big->bits[idx] = lo; + carry = hi; + } + if (carry) big->bits[big->used++] = carry; +} + +/** + Evaluate 'big *= 2^exp'. + @param big A big number (can be 0). + @param exp An exponent integer (can be 0). + */ +static_inline void bigint_mul_pow2(bigint *big, u32 exp) { + u32 shft = exp % 64; + u32 move = exp / 64; + u32 idx = big->used; + if (unlikely(shft == 0)) { + for (; idx > 0; idx--) { + big->bits[idx + move - 1] = big->bits[idx - 1]; + } + big->used += move; + while (move) big->bits[--move] = 0; + } else { + big->bits[idx] = 0; + for (; idx > 0; idx--) { + u64 num = big->bits[idx] << shft; + num |= big->bits[idx - 1] >> (64 - shft); + big->bits[idx + move] = num; + } + big->bits[move] = big->bits[0] << shft; + big->used += move + (big->bits[big->used + move] > 0); + while (move) big->bits[--move] = 0; + } +} + +/** + Evaluate 'big *= 10^exp'. + @param big A big number (can be 0). + @param exp An exponent integer (cannot be 0). + */ +static_inline void bigint_mul_pow10(bigint *big, i32 exp) { + for (; exp >= U64_POW10_MAX_EXACT_EXP; exp -= U64_POW10_MAX_EXACT_EXP) { + bigint_mul_u64(big, u64_pow10_table[U64_POW10_MAX_EXACT_EXP]); + } + if (exp) { + bigint_mul_u64(big, u64_pow10_table[exp]); + } +} + +/** + Compare two bigint. + @return -1 if 'a < b', +1 if 'a > b', 0 if 'a == b'. + */ +static_inline i32 bigint_cmp(bigint *a, bigint *b) { + u32 idx = a->used; + if (a->used < b->used) return -1; + if (a->used > b->used) return +1; + while (idx-- > 0) { + u64 av = a->bits[idx]; + u64 bv = b->bits[idx]; + if (av < bv) return -1; + if (av > bv) return +1; + } + return 0; +} + +/** + Evaluate 'big = val'. + @param big A big number (can be 0). + @param val An unsigned integer (can be 0). + */ +static_inline void bigint_set_u64(bigint *big, u64 val) { + big->used = 1; + big->bits[0] = val; +} + +/** Set a bigint with floating point number string. */ +static_noinline void bigint_set_buf(bigint *big, u64 sig, i32 *exp, + u8 *sig_cut, u8 *sig_end, u8 *dot_pos) { + + if (unlikely(!sig_cut)) { + /* no digit cut, set significant part only */ + bigint_set_u64(big, sig); + return; + + } else { + /* some digits were cut, read them from 'sig_cut' to 'sig_end' */ + u8 *hdr = sig_cut; + u8 *cur = hdr; + u32 len = 0; + u64 val = 0; + bool dig_big_cut = false; + bool has_dot = (hdr < dot_pos) & (dot_pos < sig_end); + u32 dig_len_total = U64_SAFE_DIG + (u32)(sig_end - hdr) - has_dot; + + sig -= (*sig_cut >= '5'); /* sig was rounded before */ + if (dig_len_total > F64_MAX_DEC_DIG) { + dig_big_cut = true; + sig_end -= dig_len_total - (F64_MAX_DEC_DIG + 1); + sig_end -= (dot_pos + 1 == sig_end); + dig_len_total = (F64_MAX_DEC_DIG + 1); + } + *exp -= (i32)dig_len_total - U64_SAFE_DIG; + + big->used = 1; + big->bits[0] = sig; + while (cur < sig_end) { + if (likely(cur != dot_pos)) { + val = val * 10 + (u8)(*cur++ - '0'); + len++; + if (unlikely(cur == sig_end && dig_big_cut)) { + /* The last digit must be non-zero, */ + /* set it to '1' for correct rounding. */ + val = val - (val % 10) + 1; + } + if (len == U64_SAFE_DIG || cur == sig_end) { + bigint_mul_pow10(big, (i32)len); + bigint_add_u64(big, val); + val = 0; + len = 0; + } + } else { + cur++; + } + } + } +} + + + +/*============================================================================== + * MARK: - Diy Floating Point (Private) + *============================================================================*/ + +/** "Do It Yourself Floating Point" struct. */ +typedef struct diy_fp { + u64 sig; /* significand */ + i32 exp; /* exponent, base 2 */ + i32 pad; /* padding, useless */ +} diy_fp; + +/** Get cached rounded diy_fp with pow(10, e) The input value must in range + [POW10_SIG_TABLE_MIN_EXP, POW10_SIG_TABLE_MAX_EXP]. */ +static_inline diy_fp diy_fp_get_cached_pow10(i32 exp10) { + diy_fp fp; + u64 sig_ext; + pow10_table_get_sig(exp10, &fp.sig, &sig_ext); + pow10_table_get_exp(exp10, &fp.exp); + fp.sig += (sig_ext >> 63); + return fp; +} + +/** Returns fp * fp2. */ +static_inline diy_fp diy_fp_mul(diy_fp fp, diy_fp fp2) { + u64 hi, lo; + u128_mul(fp.sig, fp2.sig, &hi, &lo); + fp.sig = hi + (lo >> 63); + fp.exp += fp2.exp + 64; + return fp; +} + +/** Convert diy_fp to IEEE-754 raw value. */ +static_inline u64 diy_fp_to_ieee_raw(diy_fp fp) { + u64 sig = fp.sig; + i32 exp = fp.exp; + u32 lz_bits; + if (unlikely(fp.sig == 0)) return 0; + + lz_bits = u64_lz_bits(sig); + sig <<= lz_bits; + sig >>= F64_BITS - F64_SIG_FULL_BITS; + exp -= (i32)lz_bits; + exp += F64_BITS - F64_SIG_FULL_BITS; + exp += F64_SIG_BITS; + + if (unlikely(exp >= F64_MAX_BIN_EXP)) { + /* overflow */ + return F64_BITS_INF; + } else if (likely(exp >= F64_MIN_BIN_EXP - 1)) { + /* normal */ + exp += F64_EXP_BIAS; + return ((u64)exp << F64_SIG_BITS) | (sig & F64_SIG_MASK); + } else if (likely(exp >= F64_MIN_BIN_EXP - F64_SIG_FULL_BITS)) { + /* subnormal */ + return sig >> (F64_MIN_BIN_EXP - exp - 1); + } else { + /* underflow */ + return 0; + } +} + + + +/*============================================================================== + * MARK: - Number Reader (Private) + *============================================================================*/ + +/** + Read a JSON number. + + 1. This function assume that the floating-point number is in IEEE-754 format. + 2. This function support uint64/int64/double number. If an integer number + cannot fit in uint64/int64, it will returns as a double number. If a double + number is infinite, the return value is based on flag. + 3. This function (with inline attribute) may generate a lot of instructions. + */ +static_inline bool read_num(u8 **ptr, u8 **pre, yyjson_read_flag flg, + yyjson_val *val, const char **msg) { +#define return_err(_pos, _msg) do { \ + *msg = _msg; \ + *end = _pos; \ + return false; \ +} while (false) + +#define return_0() do { \ + val->tag = YYJSON_TYPE_NUM | (u8)((u8)sign << 3); \ + val->uni.u64 = 0; \ + *end = cur; return true; \ +} while (false) + +#define return_i64(_v) do { \ + val->tag = YYJSON_TYPE_NUM | (u8)((u8)sign << 3); \ + val->uni.u64 = (u64)(sign ? (u64)(~(_v) + 1) : (u64)(_v)); \ + *end = cur; return true; \ +} while (false) + +#define return_f64(_v) do { \ + val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; \ + val->uni.f64 = sign ? -(f64)(_v) : (f64)(_v); \ + *end = cur; return true; \ +} while (false) + +#define return_f64_bin(_v) do { \ + val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; \ + val->uni.u64 = ((u64)sign << 63) | (u64)(_v); \ + *end = cur; return true; \ +} while (false) + +#define return_inf() do { \ + if (has_flg(BIGNUM_AS_RAW)) return_raw(); \ + if (has_allow(INF_AND_NAN)) return_f64_bin(F64_BITS_INF); \ + else return_err(hdr, "number is infinity when parsed as double"); \ +} while (false) + +#define return_raw() do { \ + **pre = '\0'; /* add null-terminator for previous raw string */ \ + val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; \ + val->uni.str = (const char *)hdr; \ + *pre = cur; *end = cur; return true; \ +} while (false) + + u8 *sig_cut = NULL; /* significant part cutting position for long number */ + u8 *sig_end = NULL; /* significant part ending position */ + u8 *dot_pos = NULL; /* decimal point position */ + + u64 sig = 0; /* significant part of the number */ + i32 exp = 0; /* exponent part of the number */ + + bool exp_sign; /* temporary exponent sign from literal part */ + i64 exp_sig = 0; /* temporary exponent number from significant part */ + i64 exp_lit = 0; /* temporary exponent number from exponent literal part */ + u64 num; /* temporary number for reading */ + u8 *tmp; /* temporary cursor for reading */ + + u8 *hdr = *ptr; + u8 *cur = *ptr; + u8 **end = ptr; + bool sign; + + /* read number as raw string if has `YYJSON_READ_NUMBER_AS_RAW` flag */ + if (has_flg(NUMBER_AS_RAW)) { + return read_num_raw(ptr, pre, flg, val, msg); + } + + sign = (*hdr == '-'); + cur += sign; + + /* begin with a leading zero or non-digit */ + while (unlikely(!char_is_nonzero(*cur))) { /* 0 or non-digit char */ + if (unlikely(*cur != '0')) { /* non-digit char */ + if (has_allow(EXT_NUMBER)) { + if (*cur == '+' && cur == hdr) { /* leading `+` sign */ + cur++; + continue; + } + if (*cur == '.' && char_is_digit(cur[1])) { /* e.g. '.123' */ + goto leading_dot; + } + } + if (has_allow(INF_AND_NAN)) { + if (read_inf_or_nan(ptr, pre, flg, val)) return true; + } + return_err(cur, "no digit after sign"); + } + /* begin with 0 */ + if (likely(!char_is_digit_or_fp(*++cur))) { + if (has_allow(EXT_NUMBER) && char_to_lower(*cur) == 'x') { /* hex */ + return read_num_hex(ptr, pre, flg, val, msg); + } + return_0(); + } + if (likely(*cur == '.')) { +leading_dot: + dot_pos = cur++; + if (unlikely(!char_is_digit(*cur))) { + if (has_allow(EXT_NUMBER)) { + if (char_is_exp(*cur)) { + goto digi_exp_more; + } else { + return_f64_bin(0); + } + } + return_err(cur, "no digit after decimal point"); + } + while (unlikely(*cur == '0')) cur++; + if (likely(char_is_digit(*cur))) { + /* first non-zero digit after decimal point */ + sig = (u64)(*cur - '0'); /* read first digit */ + cur--; + goto digi_frac_1; /* continue read fraction part */ + } + } + if (unlikely(char_is_digit(*cur))) { + return_err(cur - 1, "number with leading zero is not allowed"); + } + if (unlikely(char_is_exp(*cur))) { /* 0 with any exponent is still 0 */ + cur += (usize)1 + char_is_sign(cur[1]); + if (unlikely(!char_is_digit(*cur))) { + return_err(cur, "no digit after exponent sign"); + } + while (char_is_digit(*++cur)); + } + return_f64_bin(0); + } + + /* begin with non-zero digit */ + sig = (u64)(*cur - '0'); + + /* + Read integral part, same as the following code. + + for (int i = 1; i <= 18; i++) { + num = cur[i] - '0'; + if (num <= 9) sig = num + sig * 10; + else goto digi_sepr_i; + } + */ +#define expr_intg(i) \ + if (likely((num = (u64)(cur[i] - (u8)'0')) <= 9)) sig = num + sig * 10; \ + else { goto digi_sepr_##i; } + repeat_in_1_18(expr_intg) +#undef expr_intg + + + cur += 19; /* skip continuous 19 digits */ + if (!char_is_digit_or_fp(*cur)) { + /* this number is an integer consisting of 19 digits */ + if (sign && (sig > ((u64)1 << 63))) { /* overflow */ + if (has_flg(BIGNUM_AS_RAW)) return_raw(); + return_f64(unsafe_yyjson_u64_to_f64(sig)); + } + return_i64(sig); + } + goto digi_intg_more; /* read more digits in integral part */ + + + /* process first non-digit character */ +#define expr_sepr(i) \ + digi_sepr_##i: \ + if (likely(!char_is_fp(cur[i]))) { cur += i; return_i64(sig); } \ + dot_pos = cur + i; \ + if (likely(cur[i] == '.')) goto digi_frac_##i; \ + cur += i; sig_end = cur; goto digi_exp_more; + repeat_in_1_18(expr_sepr) +#undef expr_sepr + + + /* read fraction part */ +#define expr_frac(i) \ + digi_frac_##i: \ + if (likely((num = (u64)(cur[i + 1] - (u8)'0')) <= 9)) \ + sig = num + sig * 10; \ + else { goto digi_stop_##i; } + repeat_in_1_18(expr_frac) +#undef expr_frac + + cur += 20; /* skip 19 digits and 1 decimal point */ + if (!char_is_digit(*cur)) goto digi_frac_end; /* fraction part end */ + goto digi_frac_more; /* read more digits in fraction part */ + + + /* significant part end */ +#define expr_stop(i) \ + digi_stop_##i: \ + cur += i + 1; \ + goto digi_frac_end; + repeat_in_1_18(expr_stop) +#undef expr_stop + + + /* read more digits in integral part */ +digi_intg_more: + if (char_is_digit(*cur)) { + if (!char_is_digit_or_fp(cur[1])) { + /* this number is an integer consisting of 20 digits */ + num = (u64)(*cur - '0'); + if ((sig < (U64_MAX / 10)) || + (sig == (U64_MAX / 10) && num <= (U64_MAX % 10))) { + sig = num + sig * 10; + cur++; + /* convert to double if overflow */ + if (sign) { + if (has_flg(BIGNUM_AS_RAW)) return_raw(); + return_f64(unsafe_yyjson_u64_to_f64(sig)); + } + return_i64(sig); + } + } + } + + if (char_is_exp(*cur)) { + dot_pos = cur; + goto digi_exp_more; + } + + if (*cur == '.') { + dot_pos = cur++; + if (unlikely(!char_is_digit(*cur))) { + if (has_allow(EXT_NUMBER)) { + goto digi_frac_end; + } + return_err(cur, "no digit after decimal point"); + } + } + + + /* read more digits in fraction part */ +digi_frac_more: + sig_cut = cur; /* too large to fit in u64, excess digits need to be cut */ + sig += (*cur >= '5'); /* round */ + while (char_is_digit(*++cur)); + if (!dot_pos) { + if (!char_is_fp(*cur) && has_flg(BIGNUM_AS_RAW)) { + return_raw(); /* it's a large integer */ + } + dot_pos = cur; + if (*cur == '.') { + if (unlikely(!char_is_digit(*++cur))) { + if (!has_allow(EXT_NUMBER)) { + return_err(cur, "no digit after decimal point"); + } + } + while (char_is_digit(*cur)) cur++; + } + } + exp_sig = (i64)(dot_pos - sig_cut); + exp_sig += (dot_pos < sig_cut); + + /* ignore trailing zeros */ + tmp = cur - 1; + while ((*tmp == '0' || *tmp == '.') && tmp > hdr) tmp--; + if (tmp < sig_cut) { + sig_cut = NULL; + } else { + sig_end = cur; + } + + if (char_is_exp(*cur)) goto digi_exp_more; + goto digi_exp_finish; + + + /* fraction part end */ +digi_frac_end: + if (unlikely(dot_pos + 1 == cur)) { + if (!has_allow(EXT_NUMBER)) { + return_err(cur, "no digit after decimal point"); + } + } + sig_end = cur; + exp_sig = -(i64)((u64)(cur - dot_pos) - 1); + if (likely(!char_is_exp(*cur))) { + if (unlikely(exp_sig < F64_MIN_DEC_EXP - 19)) { + return_f64_bin(0); /* underflow */ + } + exp = (i32)exp_sig; + goto digi_finish; + } else { + goto digi_exp_more; + } + + + /* read exponent part */ +digi_exp_more: + exp_sign = (*++cur == '-'); + cur += char_is_sign(*cur); + if (unlikely(!char_is_digit(*cur))) { + return_err(cur, "no digit after exponent sign"); + } + while (*cur == '0') cur++; + + /* read exponent literal */ + tmp = cur; + while (char_is_digit(*cur)) { + exp_lit = (i64)((u8)(*cur++ - '0') + (u64)exp_lit * 10); + } + if (unlikely(cur - tmp >= U64_SAFE_DIG)) { + if (exp_sign) { + return_f64_bin(0); /* underflow */ + } else { + return_inf(); /* overflow */ + } + } + exp_sig += exp_sign ? -exp_lit : exp_lit; + + + /* validate exponent value */ +digi_exp_finish: + if (unlikely(exp_sig < F64_MIN_DEC_EXP - 19)) { + return_f64_bin(0); /* underflow */ + } + if (unlikely(exp_sig > F64_MAX_DEC_EXP)) { + return_inf(); /* overflow */ + } + exp = (i32)exp_sig; + + + /* all digit read finished */ +digi_finish: + + /* + Fast path 1: + + 1. The floating-point number calculation should be accurate, see the + comments of macro `YYJSON_DOUBLE_MATH_CORRECT`. + 2. Correct rounding should be performed (fegetround() == FE_TONEAREST). + 3. The input of floating point number calculation does not lose precision, + which means: 64 - leading_zero(input) - trailing_zero(input) < 53. + + We don't check all available inputs here, because that would make the code + more complicated, and not friendly to branch predictor. + */ +#if YYJSON_DOUBLE_MATH_CORRECT + if (sig < ((u64)1 << 53) && + exp >= -F64_POW10_MAX_EXACT_EXP && + exp <= +F64_POW10_MAX_EXACT_EXP) { + f64 dbl = (f64)sig; + if (exp < 0) { + dbl /= f64_pow10_table[-exp]; + } else { + dbl *= f64_pow10_table[+exp]; + } + return_f64(dbl); + } +#endif + + /* + Fast path 2: + + To keep it simple, we only accept normal number here, + let the slow path to handle subnormal and infinity number. + */ + if (likely(!sig_cut && + exp > -F64_MAX_DEC_EXP + 1 && + exp < +F64_MAX_DEC_EXP - 20)) { + /* + The result value is exactly equal to (sig * 10^exp), + the exponent part (10^exp) can be converted to (sig2 * 2^exp2). + + The sig2 can be an infinite length number, only the highest 128 bits + is cached in the pow10_sig_table. + + Now we have these bits: + sig1 (normalized 64bit) : aaaaaaaa + sig2 (higher 64bit) : bbbbbbbb + sig2_ext (lower 64bit) : cccccccc + sig2_cut (extra unknown bits) : dddddddddddd.... + + And the calculation process is: + ---------------------------------------- + aaaaaaaa * + bbbbbbbbccccccccdddddddddddd.... + ---------------------------------------- + abababababababab + + acacacacacacacac + + adadadadadadadadadad.... + ---------------------------------------- + [hi____][lo____] + + [hi2___][lo2___] + + [unknown___________....] + ---------------------------------------- + + The addition with carry may affect higher bits, but if there is a 0 + in higher bits, the bits higher than 0 will not be affected. + + `lo2` + `unknown` may get a carry bit and may affect `hi2`, the max + value of `hi2` is 0xFFFFFFFFFFFFFFFE, so `hi2` will not overflow. + + `lo` + `hi2` may also get a carry bit and may affect `hi`, but only + the highest significant 53 bits of `hi` is needed. If there is a 0 + in the lower bits of `hi`, then all the following bits can be dropped. + + To convert the result to IEEE-754 double number, we need to perform + correct rounding: + 1. if bit 54 is 0, round down, + 2. if bit 54 is 1 and any bit beyond bit 54 is 1, round up, + 3. if bit 54 is 1 and all bits beyond bit 54 are 0, round to even, + as the extra bits is unknown, this case will not be handled here. + */ + + u64 raw; + u64 sig1, sig2, sig2_ext, hi, lo, hi2, lo2, add, bits; + i32 exp2; + u32 lz; + bool exact = false, carry, round_up; + + /* convert (10^exp) to (sig2 * 2^exp2) */ + pow10_table_get_sig(exp, &sig2, &sig2_ext); + pow10_table_get_exp(exp, &exp2); + + /* normalize and multiply */ + lz = u64_lz_bits(sig); + sig1 = sig << lz; + exp2 -= (i32)lz; + u128_mul(sig1, sig2, &hi, &lo); + + /* + The `hi` is in range [0x4000000000000000, 0xFFFFFFFFFFFFFFFE], + To get normalized value, `hi` should be shifted to the left by 0 or 1. + + The highest significant 53 bits is used by IEEE-754 double number, + and the bit 54 is used to detect rounding direction. + + The lowest (64 - 54 - 1) bits is used to check whether it contains 0. + */ + bits = hi & (((u64)1 << (64 - 54 - 1)) - 1); + if (bits - 1 < (((u64)1 << (64 - 54 - 1)) - 2)) { + /* + (bits != 0 && bits != 0x1FF) => (bits - 1 < 0x1FF - 1) + The `bits` is not zero, so we don't need to check `round to even` + case. The `bits` contains bit `0`, so we can drop the extra bits + after `0`. + */ + exact = true; + + } else { + /* + (bits == 0 || bits == 0x1FF) + The `bits` is filled with all `0` or all `1`, so we need to check + lower bits with another 64-bit multiplication. + */ + u128_mul(sig1, sig2_ext, &hi2, &lo2); + + add = lo + hi2; + if (add + 1 > (u64)1) { + /* + (add != 0 && add != U64_MAX) => (add + 1 > 1) + The `add` is not zero, so we don't need to check `round to + even` case. The `add` contains bit `0`, so we can drop the + extra bits after `0`. The `hi` cannot be U64_MAX, so it will + not overflow. + */ + carry = add < lo || add < hi2; + hi += carry; + exact = true; + } + } + + if (exact) { + /* normalize */ + lz = hi < ((u64)1 << 63); + hi <<= lz; + exp2 -= (i32)lz; + exp2 += 64; + + /* test the bit 54 and get rounding direction */ + round_up = (hi & ((u64)1 << (64 - 54))) > (u64)0; + hi += (round_up ? ((u64)1 << (64 - 54)) : (u64)0); + + /* test overflow */ + if (hi < ((u64)1 << (64 - 54))) { + hi = ((u64)1 << 63); + exp2 += 1; + } + + /* This is a normal number, convert it to IEEE-754 format. */ + hi >>= F64_BITS - F64_SIG_FULL_BITS; + exp2 += F64_BITS - F64_SIG_FULL_BITS + F64_SIG_BITS; + exp2 += F64_EXP_BIAS; + raw = ((u64)exp2 << F64_SIG_BITS) | (hi & F64_SIG_MASK); + return_f64_bin(raw); + } + } + + /* + Slow path: read double number exactly with diyfp. + 1. Use cached diyfp to get an approximation value. + 2. Use bigcomp to check the approximation value if needed. + + This algorithm refers to google's double-conversion project: + https://github.com/google/double-conversion + */ + { + const i32 ERR_ULP_LOG = 3; + const i32 ERR_ULP = 1 << ERR_ULP_LOG; + const i32 ERR_CACHED_POW = ERR_ULP / 2; + const i32 ERR_MUL_FIXED = ERR_ULP / 2; + const i32 DIY_SIG_BITS = 64; + const i32 EXP_BIAS = F64_EXP_BIAS + F64_SIG_BITS; + const i32 EXP_SUBNORMAL = -EXP_BIAS + 1; + + u64 fp_err; + u32 bits; + i32 order_of_magnitude; + i32 effective_significand_size; + i32 precision_digits_count; + u64 precision_bits; + u64 half_way; + + u64 raw; + diy_fp fp, fp_upper; + bigint big_full, big_comp; + i32 cmp; + + fp.sig = sig; + fp.exp = 0; + fp_err = sig_cut ? (u64)(ERR_ULP / 2) : (u64)0; + + /* normalize */ + bits = u64_lz_bits(fp.sig); + fp.sig <<= bits; + fp.exp -= (i32)bits; + fp_err <<= bits; + + /* multiply and add error */ + fp = diy_fp_mul(fp, diy_fp_get_cached_pow10(exp)); + fp_err += (u64)ERR_CACHED_POW + (fp_err != 0) + (u64)ERR_MUL_FIXED; + + /* normalize */ + bits = u64_lz_bits(fp.sig); + fp.sig <<= bits; + fp.exp -= (i32)bits; + fp_err <<= bits; + + /* effective significand */ + order_of_magnitude = DIY_SIG_BITS + fp.exp; + if (likely(order_of_magnitude >= EXP_SUBNORMAL + F64_SIG_FULL_BITS)) { + effective_significand_size = F64_SIG_FULL_BITS; + } else if (order_of_magnitude <= EXP_SUBNORMAL) { + effective_significand_size = 0; + } else { + effective_significand_size = order_of_magnitude - EXP_SUBNORMAL; + } + + /* precision digits count */ + precision_digits_count = DIY_SIG_BITS - effective_significand_size; + if (unlikely(precision_digits_count + ERR_ULP_LOG >= DIY_SIG_BITS)) { + i32 shr = (precision_digits_count + ERR_ULP_LOG) - DIY_SIG_BITS + 1; + fp.sig >>= shr; + fp.exp += shr; + fp_err = (fp_err >> shr) + 1 + (u32)ERR_ULP; + precision_digits_count -= shr; + } + + /* half way */ + precision_bits = fp.sig & (((u64)1 << precision_digits_count) - 1); + precision_bits *= (u32)ERR_ULP; + half_way = (u64)1 << (precision_digits_count - 1); + half_way *= (u32)ERR_ULP; + + /* rounding */ + fp.sig >>= precision_digits_count; + fp.sig += (precision_bits >= half_way + fp_err); + fp.exp += precision_digits_count; + + /* get IEEE double raw value */ + raw = diy_fp_to_ieee_raw(fp); + if (unlikely(raw == F64_BITS_INF)) return_inf(); + if (likely(precision_bits <= half_way - fp_err || + precision_bits >= half_way + fp_err)) { + return_f64_bin(raw); /* number is accurate */ + } + /* now the number is the correct value, or the next lower value */ + + /* upper boundary */ + if (raw & F64_EXP_MASK) { + fp_upper.sig = (raw & F64_SIG_MASK) + ((u64)1 << F64_SIG_BITS); + fp_upper.exp = (i32)((raw & F64_EXP_MASK) >> F64_SIG_BITS); + } else { + fp_upper.sig = (raw & F64_SIG_MASK); + fp_upper.exp = 1; + } + fp_upper.exp -= F64_EXP_BIAS + F64_SIG_BITS; + fp_upper.sig <<= 1; + fp_upper.exp -= 1; + fp_upper.sig += 1; /* add half ulp */ + + /* compare with bigint */ + bigint_set_buf(&big_full, sig, &exp, sig_cut, sig_end, dot_pos); + bigint_set_u64(&big_comp, fp_upper.sig); + if (exp >= 0) { + bigint_mul_pow10(&big_full, +exp); + } else { + bigint_mul_pow10(&big_comp, -exp); + } + if (fp_upper.exp > 0) { + bigint_mul_pow2(&big_comp, (u32)+fp_upper.exp); + } else { + bigint_mul_pow2(&big_full, (u32)-fp_upper.exp); + } + cmp = bigint_cmp(&big_full, &big_comp); + if (likely(cmp != 0)) { + /* round down or round up */ + raw += (cmp > 0); + } else { + /* falls midway, round to even */ + raw += (raw & 1); + } + + if (unlikely(raw == F64_BITS_INF)) return_inf(); + return_f64_bin(raw); + } + +#undef return_err +#undef return_inf +#undef return_0 +#undef return_i64 +#undef return_f64 +#undef return_f64_bin +#undef return_raw +} + + + +#else /* FP_READER */ + +/** + Read a JSON number. + This is a fallback function if the custom number reader is disabled. + This function use libc's strtod() to read floating-point number. + */ +static_inline bool read_num(u8 **ptr, u8 **pre, yyjson_read_flag flg, + yyjson_val *val, const char **msg) { +#define return_err(_pos, _msg) do { \ + *msg = _msg; \ + *end = _pos; \ + return false; \ +} while (false) + +#define return_0() do { \ + val->tag = YYJSON_TYPE_NUM | (u64)((u8)sign << 3); \ + val->uni.u64 = 0; \ + *end = cur; return true; \ +} while (false) + +#define return_i64(_v) do { \ + val->tag = YYJSON_TYPE_NUM | (u64)((u8)sign << 3); \ + val->uni.u64 = (u64)(sign ? (u64)(~(_v) + 1) : (u64)(_v)); \ + *end = cur; return true; \ +} while (false) + +#define return_f64(_v) do { \ + val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; \ + val->uni.f64 = sign ? -(f64)(_v) : (f64)(_v); \ + *end = cur; return true; \ +} while (false) + +#define return_f64_bin(_v) do { \ + val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; \ + val->uni.u64 = ((u64)sign << 63) | (u64)(_v); \ + *end = cur; return true; \ +} while (false) + +#define return_inf() do { \ + if (has_flg(BIGNUM_AS_RAW)) return_raw(); \ + if (has_allow(INF_AND_NAN)) return_f64_bin(F64_BITS_INF); \ + else return_err(hdr, "number is infinity when parsed as double"); \ +} while (false) + +#define return_raw() do { \ + val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; \ + val->uni.str = (const char *)hdr; \ + **pre = '\0'; *pre = cur; *end = cur; return true; \ +} while (false) + + u64 sig, num; + u8 *hdr = *ptr; + u8 *cur = *ptr; + u8 **end = ptr; + u8 *dot = NULL; + u8 *f64_end = NULL; + bool sign; + + /* read number as raw string if has `YYJSON_READ_NUMBER_AS_RAW` flag */ + if (has_flg(NUMBER_AS_RAW)) { + return read_num_raw(ptr, pre, flg, val, msg); + } + + sign = (*hdr == '-'); + cur += sign; + sig = (u8)(*cur - '0'); + + /* read first digit, check leading zero */ + while (unlikely(!char_is_digit(*cur))) { + if (has_allow(EXT_NUMBER)) { + if (*cur == '+' && cur == hdr) { /* leading `+` sign */ + cur++; + sig = (u8)(*cur - '0'); + continue; + } + if (*cur == '.' && char_is_num(cur[1])) { /* no integer part */ + goto read_double; /* e.g. '.123' */ + } + } + if (has_allow(INF_AND_NAN)) { + if (read_inf_or_nan(ptr, pre, flg, val)) return true; + } + return_err(cur, "no digit after sign"); + } + if (*cur == '0') { + cur++; + if (unlikely(char_is_digit(*cur))) { + return_err(cur - 1, "number with leading zero is not allowed"); + } + if (!char_is_fp(*cur)) { + if (has_allow(EXT_NUMBER) && + (*cur == 'x' || *cur == 'X')) { /* hex integer */ + return read_num_hex(ptr, pre, flg, val, msg); + } + return_0(); + } + goto read_double; + } + + /* read continuous digits, up to 19 characters */ +#define expr_intg(i) \ + if (likely((num = (u64)(cur[i] - (u8)'0')) <= 9)) sig = num + sig * 10; \ + else { cur += i; goto intg_end; } + repeat_in_1_18(expr_intg) +#undef expr_intg + + /* here are 19 continuous digits, skip them */ + cur += 19; + if (char_is_digit(cur[0]) && !char_is_digit_or_fp(cur[1])) { + /* this number is an integer consisting of 20 digits */ + num = (u8)(*cur - '0'); + if ((sig < (U64_MAX / 10)) || + (sig == (U64_MAX / 10) && num <= (U64_MAX % 10))) { + sig = num + sig * 10; + cur++; + if (sign) { + if (has_flg(BIGNUM_AS_RAW)) return_raw(); + return_f64(unsafe_yyjson_u64_to_f64(sig)); + } + return_i64(sig); + } + } + +intg_end: + /* continuous digits ended */ + if (!char_is_digit_or_fp(*cur)) { + /* this number is an integer consisting of 1 to 19 digits */ + if (sign && (sig > ((u64)1 << 63))) { + if (has_flg(BIGNUM_AS_RAW)) return_raw(); + return_f64(unsafe_yyjson_u64_to_f64(sig)); + } + return_i64(sig); + } + +read_double: + /* this number should be read as double */ + while (char_is_digit(*cur)) cur++; + if (!char_is_fp(*cur) && has_flg(BIGNUM_AS_RAW)) { + return_raw(); /* it's a large integer */ + } + while (*cur == '.') { + /* skip fraction part */ + dot = cur; + cur++; + if (!char_is_digit(*cur)) { + if (has_allow(EXT_NUMBER)) { + break; + } else { + return_err(cur, "no digit after decimal point"); + } + } + cur++; + while (char_is_digit(*cur)) cur++; + break; + } + if (char_is_exp(*cur)) { + /* skip exponent part */ + cur += 1 + char_is_sign(cur[1]); + if (!char_is_digit(*cur)) { + return_err(cur, "no digit after exponent sign"); + } + cur++; + while (char_is_digit(*cur)) cur++; + } + + /* + libc's strtod() is used to parse the floating-point number. + + Note that the decimal point character used by strtod() is locale-dependent, + and the rounding direction may affected by fesetround(). + + For currently known locales, (en, zh, ja, ko, am, he, hi) use '.' as the + decimal point, while other locales use ',' as the decimal point. + + Here strtod() is called twice for different locales, but if another thread + happens calls setlocale() between two strtod(), parsing may still fail. + */ + val->uni.f64 = strtod((const char *)hdr, (char **)&f64_end); + if (unlikely(f64_end != cur)) { + /* replace '.' with ',' for locale */ + bool cut = (*cur == ','); + if (cut) *cur = ' '; + if (dot) *dot = ','; + val->uni.f64 = strtod((const char *)hdr, (char **)&f64_end); + /* restore ',' to '.' */ + if (cut) *cur = ','; + if (dot) *dot = '.'; + if (unlikely(f64_end != cur)) { + return_err(hdr, "strtod() failed to parse the number"); + } + } + if (unlikely(val->uni.f64 >= HUGE_VAL || val->uni.f64 <= -HUGE_VAL)) { + return_inf(); + } + val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; + *end = cur; + return true; + +#undef return_err +#undef return_0 +#undef return_i64 +#undef return_f64 +#undef return_f64_bin +#undef return_inf +#undef return_raw +} + +#endif /* FP_READER */ + + + +/*============================================================================== + * MARK: - String Reader (Private) + *============================================================================*/ + +/** Read unicode escape sequence. */ +static_inline bool read_uni_esc(u8 **src_ptr, u8 **dst_ptr, const char **msg) { +#define return_err(_end, _msg) *msg = _msg; *src_ptr = _end; return false + + u8 *src = *src_ptr; + u8 *dst = *dst_ptr; + u16 hi, lo; + u32 uni; + + src += 2; /* skip `\u` */ + if (unlikely(!hex_load_4(src, &hi))) { + return_err(src - 2, "invalid escaped sequence in string"); + } + src += 4; /* skip hex */ + if (likely((hi & 0xF800) != 0xD800)) { + /* a BMP character */ + if (hi >= 0x800) { + *dst++ = (u8)(0xE0 | (hi >> 12)); + *dst++ = (u8)(0x80 | ((hi >> 6) & 0x3F)); + *dst++ = (u8)(0x80 | (hi & 0x3F)); + } else if (hi >= 0x80) { + *dst++ = (u8)(0xC0 | (hi >> 6)); + *dst++ = (u8)(0x80 | (hi & 0x3F)); + } else { + *dst++ = (u8)hi; + } + } else { + /* a non-BMP character, represented as a surrogate pair */ + if (unlikely((hi & 0xFC00) != 0xD800)) { + return_err(src - 6, "invalid high surrogate in string"); + } + if (unlikely(!byte_match_2(src, "\\u"))) { + return_err(src - 6, "no low surrogate in string"); + } + if (unlikely(!hex_load_4(src + 2, &lo))) { + return_err(src - 6, "invalid escape in string"); + } + if (unlikely((lo & 0xFC00) != 0xDC00)) { + return_err(src - 6, "invalid low surrogate in string"); + } + uni = ((((u32)hi - 0xD800) << 10) | + ((u32)lo - 0xDC00)) + 0x10000; + *dst++ = (u8)(0xF0 | (uni >> 18)); + *dst++ = (u8)(0x80 | ((uni >> 12) & 0x3F)); + *dst++ = (u8)(0x80 | ((uni >> 6) & 0x3F)); + *dst++ = (u8)(0x80 | (uni & 0x3F)); + src += 6; + } + *src_ptr = src; + *dst_ptr = dst; + return true; +#undef return_err +} + +/** + Read a JSON string. + @param quo The quote character (single quote or double quote). + @param ptr The head pointer of string before quote (inout). + @param eof JSON end position. + @param flg JSON read flag. + @param val The string value to be written. + @param msg The error message pointer. + @param con Continuation for incremental parsing. + @return Whether success. + */ +static_inline bool read_str_opt(u8 quo, u8 **ptr, u8 *eof, yyjson_read_flag flg, + yyjson_val *val, const char **msg, u8 *con[2]) { + /* + GCC may sometimes load variables into registers too early, causing + unnecessary instructions and performance degradation. This inline assembly + serves as a hint to GCC: 'This variable will be modified, so avoid loading + it too early.' Other compilers like MSVC, Clang, and ICC can generate the + expected instructions without needing this hint. + + Check out this example: https://godbolt.org/z/YG6a5W5Ec + */ +#define return_err(_end, _msg) do { \ + *msg = _msg; \ + *end = _end; \ + if (con) { con[0] = _end; con[1] = dst; } \ + return false; \ +} while (false) + + u8 *hdr = *ptr + 1; + u8 **end = ptr; + u8 *src = hdr, *dst = NULL, *pos; + u16 hi, lo; + u32 uni, tmp; + + /* Resume incremental parsing. */ + if (con && unlikely(con[0])) { + src = con[0]; + dst = con[1]; + if (dst) goto copy_ascii; + } + +skip_ascii: + /* + Most strings have no escaped characters, so we can jump them quickly. + + We want to make loop unrolling, as shown in the following code. Some + compiler may not generate instructions as expected, so we rewrite it with + explicit goto statements. We hope the compiler can generate instructions + like this: https://godbolt.org/z/8vjsYq + + while (true) repeat16({ + if (likely((char_is_ascii_skip(*src)))) src++; + else break; + }) + */ + if (quo == '"') { +#define expr_jump(i) \ + if (likely(char_is_ascii_skip(src[i]))) {} \ + else goto skip_ascii_stop##i; + +#define expr_stop(i) \ + skip_ascii_stop##i: \ + src += i; \ + goto skip_ascii_end; + + repeat16_incr(expr_jump) + src += 16; + goto skip_ascii; + repeat16_incr(expr_stop) + +#undef expr_jump +#undef expr_stop + } else { +#define expr_jump(i) \ + if (likely(char_is_ascii_skip_sq(src[i]))) {} \ + else goto skip_ascii_stop_sq##i; + +#define expr_stop(i) \ + skip_ascii_stop_sq##i: \ + src += i; \ + goto skip_ascii_end; + + repeat16_incr(expr_jump) + src += 16; + goto skip_ascii; + repeat16_incr(expr_stop) + +#undef expr_jump +#undef expr_stop + } + +skip_ascii_end: + gcc_store_barrier(*src); + if (likely(*src == quo)) { + val->tag = ((u64)(src - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_STR | + (quo == '"' ? YYJSON_SUBTYPE_NOESC : 0); + val->uni.str = (const char *)hdr; + *src = '\0'; + *end = src + 1; + if (con) con[0] = con[1] = NULL; + return true; + } + +skip_utf8: + if (*src & 0x80) { /* non-ASCII character */ + /* + Non-ASCII character appears here, which means that the text is likely + to be written in non-English or emoticons. According to some common + data set statistics, byte sequences of the same length may appear + consecutively. We process the byte sequences of the same length in each + loop, which is more friendly to branch prediction. + */ + pos = src; +#if YYJSON_DISABLE_UTF8_VALIDATION + while (true) repeat8({ + if (likely((*src & 0xF0) == 0xE0)) src += 3; + else break; + }) + if (*src < 0x80) goto skip_ascii; + while (true) repeat8({ + if (likely((*src & 0xE0) == 0xC0)) src += 2; + else break; + }) + while (true) repeat8({ + if (likely((*src & 0xF8) == 0xF0)) src += 4; + else break; + }) +#else + uni = byte_load_4(src); + while (is_utf8_seq3(uni)) { + src += 3; + uni = byte_load_4(src); + } + if (is_utf8_seq1(uni)) goto skip_ascii; + while (is_utf8_seq2(uni)) { + src += 2; + uni = byte_load_4(src); + } + while (is_utf8_seq4(uni)) { + src += 4; + uni = byte_load_4(src); + } +#endif + if (unlikely(pos == src)) { + if (has_allow(INVALID_UNICODE)) ++src; + else return_err(src, "invalid UTF-8 encoding in string"); + } + goto skip_ascii; + } + + /* The escape character appears, we need to copy it. */ + dst = src; +copy_escape: + if (likely(*src == '\\')) { + switch (*++src) { + case '"': *dst++ = '"'; src++; break; + case '\\': *dst++ = '\\'; src++; break; + case '/': *dst++ = '/'; src++; break; + case 'b': *dst++ = '\b'; src++; break; + case 'f': *dst++ = '\f'; src++; break; + case 'n': *dst++ = '\n'; src++; break; + case 'r': *dst++ = '\r'; src++; break; + case 't': *dst++ = '\t'; src++; break; + case 'u': + src--; + if (!read_uni_esc(&src, &dst, msg)) return_err(src, *msg); + break; + default: { + if (has_allow(EXT_ESCAPE)) { + /* read extended escape (non-standard) */ + switch (*src) { + case '\'': *dst++ = '\''; src++; break; + case 'a': *dst++ = '\a'; src++; break; + case 'v': *dst++ = '\v'; src++; break; + case '?': *dst++ = '\?'; src++; break; + case 'e': *dst++ = 0x1B; src++; break; + case '0': + if (!char_is_digit(src[1])) { + *dst++ = '\0'; src++; break; + } + return_err(src - 1, "octal escape is not allowed"); + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + return_err(src - 1, "invalid number escape"); + case 'x': { + u8 c; + if (hex_load_2(src + 1, &c)) { + src += 3; + if (c <= 0x7F) { /* 1-byte ASCII */ + *dst++ = c; + } else { /* 2-byte UTF-8 */ + *dst++ = (u8)(0xC0 | (c >> 6)); + *dst++ = (u8)(0x80 | (c & 0x3F)); + } + break; + } + return_err(src - 1, "invalid hex escape"); + } + case '\n': src++; break; + case '\r': src++; src += (*src == '\n'); break; + case 0xE2: /* Line terminator: U+2028, U+2029 */ + if ((src[1] == 0x80 && src[2] == 0xA8) || + (src[1] == 0x80 && src[2] == 0xA9)) { + src += 3; + } + break; + default: + break; /* skip */ + } + } else if (quo == '\'' && *src == '\'') { + *dst++ = '\''; src++; break; + } else { + return_err(src - 1, "invalid escaped sequence in string"); + } + } + } + } else if (likely(*src == quo)) { + val->tag = ((u64)(dst - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + val->uni.str = (const char *)hdr; + *dst = '\0'; + *end = src + 1; + if (con) con[0] = con[1] = NULL; + return true; + } else { + if (!has_allow(INVALID_UNICODE)) { + return_err(src, "unexpected control character in string"); + } + if (src >= eof) return_err(src, "unclosed string"); + *dst++ = *src++; + } + +copy_ascii: + /* + Copy continuous ASCII, loop unrolling, same as the following code: + + while (true) repeat16({ + if (char_is_ascii_skip(*src)) *dst++ = *src++; + else break; + }) + */ + if (quo == '"') { +#define expr_jump(i) \ + if (likely((char_is_ascii_skip(src[i])))) {} \ + else { gcc_store_barrier(src[i]); goto copy_ascii_stop_##i; } + repeat16_incr(expr_jump) +#undef expr_jump + } else { +#define expr_jump(i) \ + if (likely((char_is_ascii_skip_sq(src[i])))) {} \ + else { gcc_store_barrier(src[i]); goto copy_ascii_stop_##i; } + repeat16_incr(expr_jump) +#undef expr_jump + } + + byte_move_16(dst, src); + dst += 16; src += 16; + goto copy_ascii; + + /* + The memory is copied forward since `dst < src`. + So it's safe to move one extra byte to reduce instruction count. + */ +#define expr_jump(i) \ + copy_ascii_stop_##i: \ + byte_move_forward(dst, src, i); \ + dst += i; src += i; \ + goto copy_utf8; + repeat16_incr(expr_jump) +#undef expr_jump + +copy_utf8: + if (*src & 0x80) { /* non-ASCII character */ + pos = src; + uni = byte_load_4(src); +#if YYJSON_DISABLE_UTF8_VALIDATION + while (true) repeat4({ + if ((uni & utf8_seq(b3_mask)) == utf8_seq(b3_patt)) { + byte_copy_4(dst, &uni); + dst += 3; src += 3; + uni = byte_load_4(src); + } else break; + }) + if ((uni & utf8_seq(b1_mask)) == utf8_seq(b1_patt)) goto copy_ascii; + while (true) repeat4({ + if ((uni & utf8_seq(b2_mask)) == utf8_seq(b2_patt)) { + byte_copy_2(dst, &uni); + dst += 2; src += 2; + uni = byte_load_4(src); + } else break; + }) + while (true) repeat4({ + if ((uni & utf8_seq(b4_mask)) == utf8_seq(b4_patt)) { + byte_copy_4(dst, &uni); + dst += 4; src += 4; + uni = byte_load_4(src); + } else break; + }) +#else + while (is_utf8_seq3(uni)) { + byte_copy_4(dst, &uni); + dst += 3; src += 3; + uni = byte_load_4(src); + } + if (is_utf8_seq1(uni)) goto copy_ascii; + while (is_utf8_seq2(uni)) { + byte_copy_2(dst, &uni); + dst += 2; src += 2; + uni = byte_load_4(src); + } + while (is_utf8_seq4(uni)) { + byte_copy_4(dst, &uni); + dst += 4; src += 4; + uni = byte_load_4(src); + } +#endif + if (unlikely(pos == src)) { + if (!has_allow(INVALID_UNICODE)) { + return_err(src, MSG_ERR_UTF8); + } + goto copy_ascii_stop_1; + } + goto copy_ascii; + } + goto copy_escape; + +#undef return_err +} + +static_inline bool read_str(u8 **ptr, u8 *eof, yyjson_read_flag flg, + yyjson_val *val, const char **msg) { + return read_str_opt('\"', ptr, eof, flg, val, msg, NULL); +} + +static_inline bool read_str_con(u8 **ptr, u8 *eof, yyjson_read_flag flg, + yyjson_val *val, const char **msg, u8 **con) { + return read_str_opt('\"', ptr, eof, flg, val, msg, con); +} + +static_noinline bool read_str_sq(u8 **ptr, u8 *eof, yyjson_read_flag flg, + yyjson_val *val, const char **msg) { + return read_str_opt('\'', ptr, eof, flg, val, msg, NULL); +} + +/** Read unquoted key (identifier name). */ +static_noinline bool read_str_id(u8 **ptr, u8 *eof, yyjson_read_flag flg, + u8 **pre, yyjson_val *val, const char **msg) { +#define return_err(_end, _msg) do { \ + *msg = _msg; \ + *end = _end; \ + return false; \ +} while (false) + +#define return_suc(_str_end, _cur_end) do { \ + val->tag = ((u64)(_str_end - hdr) << YYJSON_TAG_BIT) | \ + (u64)(YYJSON_TYPE_STR); \ + val->uni.str = (const char *)hdr; \ + *pre = _str_end; *end = _cur_end; \ + return true; \ +} while (false) + + u8 *hdr = *ptr; + u8 **end = ptr; + u8 *src = hdr, *dst = NULL; + u16 hi, lo; + u32 uni, tmp; + + /* add null-terminator for previous raw string */ + **pre = '\0'; + +skip_ascii: +#define expr_jump(i) \ + if (likely(char_is_id_ascii(src[i]))) {} \ + else goto skip_ascii_stop##i; + +#define expr_stop(i) \ + skip_ascii_stop##i: \ + src += i; \ + goto skip_ascii_end; + + repeat16_incr(expr_jump) + src += 16; + goto skip_ascii; + repeat16_incr(expr_stop) + +#undef expr_jump +#undef expr_stop + +skip_ascii_end: + gcc_store_barrier(*src); + if (likely(!char_is_id_next(*src))) { + return_suc(src, src); + } + +skip_utf8: + while (*src >= 0x80) { + if (has_allow(EXT_WHITESPACE)) { + if (char_is_space_ext(*src) && ext_space_len(src)) { + return_suc(src, src); + } + } + uni = byte_load_4(src); + if (is_utf8_seq2(uni)) { + src += 2; + } else if (is_utf8_seq3(uni)) { + src += 3; + } else if (is_utf8_seq4(uni)) { + src += 4; + } else { +#if !YYJSON_DISABLE_UTF8_VALIDATION + if (!has_allow(INVALID_UNICODE)) return_err(src, MSG_ERR_UTF8); +#endif + src += 1; + } + } + if (char_is_id_ascii(*src)) goto skip_ascii; + + /* The escape character appears, we need to copy it. */ + dst = src; +copy_escape: + if (byte_match_2(src, "\\u")) { + if (!read_uni_esc(&src, &dst, msg)) return_err(src, *msg); + } else { + if (!char_is_id_next(*src)) return_suc(dst, src); + return_err(src, "unexpected character in key"); + } + +copy_ascii: + /* + Copy continuous ASCII, loop unrolling, same as the following code: + + while (true) repeat16({ + if (char_is_ascii_skip(*src)) *dst++ = *src++; + else break; + }) + */ +#define expr_jump(i) \ + if (likely((char_is_id_ascii(src[i])))) {} \ + else { gcc_store_barrier(src[i]); goto copy_ascii_stop_##i; } + repeat16_incr(expr_jump) +#undef expr_jump + + byte_move_16(dst, src); + dst += 16; src += 16; + goto copy_ascii; + +#define expr_jump(i) \ + copy_ascii_stop_##i: \ + byte_move_forward(dst, src, i); \ + dst += i; src += i; \ + goto copy_utf8; + repeat16_incr(expr_jump) +#undef expr_jump + +copy_utf8: + while (*src >= 0x80) { /* non-ASCII character */ + if (has_allow(EXT_WHITESPACE)) { + if (char_is_space_ext(*src) && ext_space_len(src)) { + return_suc(dst, src); + } + } + uni = byte_load_4(src); + if (is_utf8_seq2(uni)) { + byte_copy_2(dst, &uni); + dst += 2; src += 2; + } else if (is_utf8_seq3(uni)) { + byte_copy_4(dst, &uni); + dst += 3; src += 3; + } else if (is_utf8_seq4(uni)) { + byte_copy_4(dst, &uni); + dst += 4; src += 4; + } else { +#if !YYJSON_DISABLE_UTF8_VALIDATION + if (!has_allow(INVALID_UNICODE)) return_err(src, MSG_ERR_UTF8); +#endif + *dst = *src; + dst += 1; src += 1; + } + } + if (char_is_id_ascii(*src)) goto copy_ascii; + goto copy_escape; + +#undef return_err +#undef return_suc +} + + + +/*============================================================================== + * MARK: - JSON Reader Implementation (Private) + * + * We use goto statements to build the finite state machine (FSM). + * The FSM's state was held by program counter (PC) and the 'goto' make the + * state transitions. + *============================================================================*/ + +/** Read single value JSON document. */ +static_noinline yyjson_doc *read_root_single(u8 *hdr, u8 *cur, u8 *eof, + yyjson_alc alc, + yyjson_read_flag flg, + yyjson_read_err *err) { +#define return_err(_pos, _code, _msg) do { \ + if (is_truncated_end(hdr, _pos, eof, YYJSON_READ_ERROR_##_code, flg)) { \ + err->pos = (usize)(eof - hdr); \ + err->code = YYJSON_READ_ERROR_UNEXPECTED_END; \ + err->msg = MSG_NOT_END; \ + } else { \ + err->pos = (usize)(_pos - hdr); \ + err->code = YYJSON_READ_ERROR_##_code; \ + err->msg = _msg; \ + } \ + if (val_hdr) alc.free(alc.ctx, val_hdr); \ + return NULL; \ +} while (false) + + usize hdr_len; /* value count used by doc */ + usize alc_num; /* value count capacity */ + yyjson_val *val_hdr; /* the head of allocated values */ + yyjson_val *val; /* current value */ + yyjson_doc *doc; /* the JSON document, equals to val_hdr */ + const char *msg; /* error message */ + + u8 raw_end[1]; /* raw end for null-terminator */ + u8 *raw_ptr = raw_end; + u8 **pre = &raw_ptr; /* previous raw end pointer */ + + hdr_len = sizeof(yyjson_doc) / sizeof(yyjson_val); + hdr_len += (sizeof(yyjson_doc) % sizeof(yyjson_val)) > 0; + alc_num = hdr_len + 1; /* single value */ + + val_hdr = (yyjson_val *)alc.malloc(alc.ctx, alc_num * sizeof(yyjson_val)); + if (unlikely(!val_hdr)) goto fail_alloc; + val = val_hdr + hdr_len; + + if (char_is_num(*cur)) { + if (likely(read_num(&cur, pre, flg, val, &msg))) goto doc_end; + goto fail_number; + } + if (*cur == '"') { + if (likely(read_str(&cur, eof, flg, val, &msg))) goto doc_end; + goto fail_string; + } + if (*cur == 't') { + if (likely(read_true(&cur, val))) goto doc_end; + goto fail_literal_true; + } + if (*cur == 'f') { + if (likely(read_false(&cur, val))) goto doc_end; + goto fail_literal_false; + } + if (*cur == 'n') { + if (likely(read_null(&cur, val))) goto doc_end; + if (has_allow(INF_AND_NAN)) { + if (read_nan(&cur, pre, flg, val)) goto doc_end; + } + goto fail_literal_null; + } + if (has_allow(INF_AND_NAN)) { + if (read_inf_or_nan(&cur, pre, flg, val)) goto doc_end; + } + if (has_allow(SINGLE_QUOTED_STR) && *cur == '\'') { + if (likely(read_str_sq(&cur, eof, flg, val, &msg))) goto doc_end; + goto fail_string; + } + goto fail_character; + +doc_end: + /* check invalid contents after json document */ + if (unlikely(cur < eof) && !has_flg(STOP_WHEN_DONE)) { + while (char_is_space(*cur)) cur++; + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (!skip_trivia(&cur, eof, flg) && cur == eof) { + goto fail_comment; + } + } + if (unlikely(cur < eof)) goto fail_garbage; + } + + **pre = '\0'; + doc = (yyjson_doc *)val_hdr; + doc->root = val_hdr + hdr_len; + doc->alc = alc; + doc->dat_read = (usize)(cur - hdr); + doc->val_read = 1; + doc->str_pool = has_flg(INSITU) ? NULL : (char *)hdr; + return doc; + +fail_string: return_err(cur, INVALID_STRING, msg); +fail_number: return_err(cur, INVALID_NUMBER, msg); +fail_alloc: return_err(cur, MEMORY_ALLOCATION, MSG_MALLOC); +fail_literal_true: return_err(cur, LITERAL, MSG_CHAR_T); +fail_literal_false: return_err(cur, LITERAL, MSG_CHAR_F); +fail_literal_null: return_err(cur, LITERAL, MSG_CHAR_N); +fail_character: return_err(cur, UNEXPECTED_CHARACTER, MSG_CHAR); +fail_comment: return_err(cur, INVALID_COMMENT, MSG_COMMENT); +fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); + +#undef return_err +} + +/** Read JSON document (accept all style, but optimized for minify). */ +static_inline yyjson_doc *read_root_minify(u8 *hdr, u8 *cur, u8 *eof, + yyjson_alc alc, + yyjson_read_flag flg, + yyjson_read_err *err) { +#define return_err(_pos, _code, _msg) do { \ + if (is_truncated_end(hdr, _pos, eof, YYJSON_READ_ERROR_##_code, flg)) { \ + err->pos = (usize)(eof - hdr); \ + err->code = YYJSON_READ_ERROR_UNEXPECTED_END; \ + err->msg = MSG_NOT_END; \ + } else { \ + err->pos = (usize)(_pos - hdr); \ + err->code = YYJSON_READ_ERROR_##_code; \ + err->msg = _msg; \ + } \ + if (val_hdr) alc.free(alc.ctx, val_hdr); \ + return NULL; \ +} while (false) + +#define val_incr() do { \ + val++; \ + if (unlikely(val >= val_end)) { \ + usize alc_old = alc_len; \ + usize val_ofs = (usize)(val - val_hdr); \ + usize ctn_ofs = (usize)(ctn - val_hdr); \ + alc_len += alc_len / 2; \ + if ((sizeof(usize) < 8) && (alc_len >= alc_max)) goto fail_alloc; \ + val_tmp = (yyjson_val *)alc.realloc(alc.ctx, (void *)val_hdr, \ + alc_old * sizeof(yyjson_val), \ + alc_len * sizeof(yyjson_val)); \ + if ((!val_tmp)) goto fail_alloc; \ + val = val_tmp + val_ofs; \ + ctn = val_tmp + ctn_ofs; \ + val_hdr = val_tmp; \ + val_end = val_tmp + (alc_len - 2); \ + } \ +} while (false) + + usize dat_len; /* data length in bytes, hint for allocator */ + usize hdr_len; /* value count used by yyjson_doc */ + usize alc_len; /* value count allocated */ + usize alc_max; /* maximum value count for allocator */ + usize ctn_len; /* the number of elements in current container */ + yyjson_val *val_hdr; /* the head of allocated values */ + yyjson_val *val_end; /* the end of allocated values */ + yyjson_val *val_tmp; /* temporary pointer for realloc */ + yyjson_val *val; /* current JSON value */ + yyjson_val *ctn; /* current container */ + yyjson_val *ctn_parent; /* parent of current container */ + yyjson_doc *doc; /* the JSON document, equals to val_hdr */ + const char *msg; /* error message */ + + u8 raw_end[1]; /* raw end for null-terminator */ + u8 *raw_ptr = raw_end; + u8 **pre = &raw_ptr; /* previous raw end pointer */ + + dat_len = has_flg(STOP_WHEN_DONE) ? 256 : (usize)(eof - cur); + hdr_len = sizeof(yyjson_doc) / sizeof(yyjson_val); + hdr_len += (sizeof(yyjson_doc) % sizeof(yyjson_val)) > 0; + alc_max = USIZE_MAX / sizeof(yyjson_val); + alc_len = hdr_len + (dat_len / YYJSON_READER_ESTIMATED_MINIFY_RATIO) + 4; + alc_len = yyjson_min(alc_len, alc_max); + + val_hdr = (yyjson_val *)alc.malloc(alc.ctx, alc_len * sizeof(yyjson_val)); + if (unlikely(!val_hdr)) goto fail_alloc; + val_end = val_hdr + (alc_len - 2); /* padding for key-value pair reading */ + val = val_hdr + hdr_len; + ctn = val; + ctn_len = 0; + + if (*cur++ == '{') { + ctn->tag = YYJSON_TYPE_OBJ; + ctn->uni.ofs = 0; + goto obj_key_begin; + } else { + ctn->tag = YYJSON_TYPE_ARR; + ctn->uni.ofs = 0; + goto arr_val_begin; + } + +arr_begin: + /* save current container */ + ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | + (ctn->tag & YYJSON_TAG_MASK); + + /* create a new array value, save parent container offset */ + val_incr(); + val->tag = YYJSON_TYPE_ARR; + val->uni.ofs = (usize)((u8 *)val - (u8 *)ctn); + + /* push the new array value as current container */ + ctn = val; + ctn_len = 0; + +arr_val_begin: + if (*cur == '{') { + cur++; + goto obj_begin; + } + if (*cur == '[') { + cur++; + goto arr_begin; + } + if (char_is_num(*cur)) { + val_incr(); + ctn_len++; + if (likely(read_num(&cur, pre, flg, val, &msg))) goto arr_val_end; + goto fail_number; + } + if (*cur == '"') { + val_incr(); + ctn_len++; + if (likely(read_str(&cur, eof, flg, val, &msg))) goto arr_val_end; + goto fail_string; + } + if (*cur == 't') { + val_incr(); + ctn_len++; + if (likely(read_true(&cur, val))) goto arr_val_end; + goto fail_literal_true; + } + if (*cur == 'f') { + val_incr(); + ctn_len++; + if (likely(read_false(&cur, val))) goto arr_val_end; + goto fail_literal_false; + } + if (*cur == 'n') { + val_incr(); + ctn_len++; + if (likely(read_null(&cur, val))) goto arr_val_end; + if (has_allow(INF_AND_NAN)) { + if (read_nan(&cur, pre, flg, val)) goto arr_val_end; + } + goto fail_literal_null; + } + if (*cur == ']') { + cur++; + if (likely(ctn_len == 0)) goto arr_end; + if (has_allow(TRAILING_COMMAS)) goto arr_end; + while (*cur != ',') cur--; + goto fail_trailing_comma; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto arr_val_begin; + } + if (has_allow(INF_AND_NAN) && + (*cur == 'i' || *cur == 'I' || *cur == 'N')) { + val_incr(); + ctn_len++; + if (read_inf_or_nan(&cur, pre, flg, val)) goto arr_val_end; + goto fail_character_val; + } + if (has_allow(SINGLE_QUOTED_STR) && *cur == '\'') { + val_incr(); + ctn_len++; + if (likely(read_str_sq(&cur, eof, flg, val, &msg))) goto arr_val_end; + goto fail_string; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto arr_val_begin; + if (cur == eof) goto fail_comment; + } + goto fail_character_val; + +arr_val_end: + if (*cur == ',') { + cur++; + goto arr_val_begin; + } + if (*cur == ']') { + cur++; + goto arr_end; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto arr_val_end; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto arr_val_end; + if (cur == eof) goto fail_comment; + } + goto fail_character_arr_end; + +arr_end: + /* get parent container */ + ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); + + /* save the next sibling value offset */ + ctn->uni.ofs = (usize)((u8 *)val - (u8 *)ctn) + sizeof(yyjson_val); + ctn->tag = ((ctn_len) << YYJSON_TAG_BIT) | YYJSON_TYPE_ARR; + if (unlikely(ctn == ctn_parent)) goto doc_end; + + /* pop parent as current container */ + ctn = ctn_parent; + ctn_len = (usize)(ctn->tag >> YYJSON_TAG_BIT); + if ((ctn->tag & YYJSON_TYPE_MASK) == YYJSON_TYPE_OBJ) { + goto obj_val_end; + } else { + goto arr_val_end; + } + +obj_begin: + /* push container */ + ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | + (ctn->tag & YYJSON_TAG_MASK); + val_incr(); + val->tag = YYJSON_TYPE_OBJ; + /* offset to the parent */ + val->uni.ofs = (usize)((u8 *)val - (u8 *)ctn); + ctn = val; + ctn_len = 0; + +obj_key_begin: + if (likely(*cur == '"')) { + val_incr(); + ctn_len++; + if (likely(read_str(&cur, eof, flg, val, &msg))) goto obj_key_end; + goto fail_string; + } + if (likely(*cur == '}')) { + cur++; + if (likely(ctn_len == 0)) goto obj_end; + if (has_allow(TRAILING_COMMAS)) goto obj_end; + while (*cur != ',') cur--; + goto fail_trailing_comma; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_key_begin; + } + if (has_allow(SINGLE_QUOTED_STR) && *cur == '\'') { + val_incr(); + ctn_len++; + if (likely(read_str_sq(&cur, eof, flg, val, &msg))) goto obj_key_end; + goto fail_string; + } + if (has_allow(UNQUOTED_KEY) && char_is_id_start(*cur)) { + val_incr(); + ctn_len++; + if (read_str_id(&cur, eof, flg, pre, val, &msg)) goto obj_key_end; + goto fail_string; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_key_begin; + if (cur == eof) goto fail_comment; + } + goto fail_character_obj_key; + +obj_key_end: + if (*cur == ':') { + cur++; + goto obj_val_begin; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_key_end; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_key_end; + if (cur == eof) goto fail_comment; + } + goto fail_character_obj_sep; + +obj_val_begin: + if (*cur == '"') { + val++; + ctn_len++; + if (likely(read_str(&cur, eof, flg, val, &msg))) goto obj_val_end; + goto fail_string; + } + if (char_is_num(*cur)) { + val++; + ctn_len++; + if (likely(read_num(&cur, pre, flg, val, &msg))) goto obj_val_end; + goto fail_number; + } + if (*cur == '{') { + cur++; + goto obj_begin; + } + if (*cur == '[') { + cur++; + goto arr_begin; + } + if (*cur == 't') { + val++; + ctn_len++; + if (likely(read_true(&cur, val))) goto obj_val_end; + goto fail_literal_true; + } + if (*cur == 'f') { + val++; + ctn_len++; + if (likely(read_false(&cur, val))) goto obj_val_end; + goto fail_literal_false; + } + if (*cur == 'n') { + val++; + ctn_len++; + if (likely(read_null(&cur, val))) goto obj_val_end; + if (has_allow(INF_AND_NAN)) { + if (read_nan(&cur, pre, flg, val)) goto obj_val_end; + } + goto fail_literal_null; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_val_begin; + } + if (has_allow(INF_AND_NAN) && + (*cur == 'i' || *cur == 'I' || *cur == 'N')) { + val++; + ctn_len++; + if (read_inf_or_nan(&cur, pre, flg, val)) goto obj_val_end; + goto fail_character_val; + } + if (has_allow(SINGLE_QUOTED_STR) && *cur == '\'') { + val++; + ctn_len++; + if (likely(read_str_sq(&cur, eof, flg, val, &msg))) goto obj_val_end; + goto fail_string; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_val_begin; + if (cur == eof) goto fail_comment; + } + goto fail_character_val; + +obj_val_end: + if (likely(*cur == ',')) { + cur++; + goto obj_key_begin; + } + if (likely(*cur == '}')) { + cur++; + goto obj_end; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_val_end; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_val_end; + if (cur == eof) goto fail_comment; + } + goto fail_character_obj_end; + +obj_end: + /* pop container */ + ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); + /* point to the next value */ + ctn->uni.ofs = (usize)((u8 *)val - (u8 *)ctn) + sizeof(yyjson_val); + ctn->tag = (ctn_len << (YYJSON_TAG_BIT - 1)) | YYJSON_TYPE_OBJ; + if (unlikely(ctn == ctn_parent)) goto doc_end; + ctn = ctn_parent; + ctn_len = (usize)(ctn->tag >> YYJSON_TAG_BIT); + if ((ctn->tag & YYJSON_TYPE_MASK) == YYJSON_TYPE_OBJ) { + goto obj_val_end; + } else { + goto arr_val_end; + } + +doc_end: + /* check invalid contents after json document */ + if (unlikely(cur < eof) && !has_flg(STOP_WHEN_DONE)) { + while (char_is_space(*cur)) cur++; + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (!skip_trivia(&cur, eof, flg) && cur == eof) { + goto fail_comment; + } + } + if (unlikely(cur < eof)) goto fail_garbage; + } + + **pre = '\0'; + doc = (yyjson_doc *)val_hdr; + doc->root = val_hdr + hdr_len; + doc->alc = alc; + doc->dat_read = (usize)(cur - hdr); + doc->val_read = (usize)((val - doc->root) + 1); + doc->str_pool = has_flg(INSITU) ? NULL : (char *)hdr; + return doc; + +fail_string: return_err(cur, INVALID_STRING, msg); +fail_number: return_err(cur, INVALID_NUMBER, msg); +fail_alloc: return_err(cur, MEMORY_ALLOCATION, MSG_MALLOC); +fail_trailing_comma: return_err(cur, JSON_STRUCTURE, MSG_COMMA); +fail_literal_true: return_err(cur, LITERAL, MSG_CHAR_T); +fail_literal_false: return_err(cur, LITERAL, MSG_CHAR_F); +fail_literal_null: return_err(cur, LITERAL, MSG_CHAR_N); +fail_character_val: return_err(cur, UNEXPECTED_CHARACTER, MSG_CHAR); +fail_character_arr_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_ARR_END); +fail_character_obj_key: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_KEY); +fail_character_obj_sep: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_SEP); +fail_character_obj_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_END); +fail_comment: return_err(cur, INVALID_COMMENT, MSG_COMMENT); +fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); + +#undef val_incr +#undef return_err +} + +/** Read JSON document (accept all style, but optimized for pretty). */ +static_inline yyjson_doc *read_root_pretty(u8 *hdr, u8 *cur, u8 *eof, + yyjson_alc alc, + yyjson_read_flag flg, + yyjson_read_err *err) { +#define return_err(_pos, _code, _msg) do { \ + if (is_truncated_end(hdr, _pos, eof, YYJSON_READ_ERROR_##_code, flg)) { \ + err->pos = (usize)(eof - hdr); \ + err->code = YYJSON_READ_ERROR_UNEXPECTED_END; \ + err->msg = MSG_NOT_END; \ + } else { \ + err->pos = (usize)(_pos - hdr); \ + err->code = YYJSON_READ_ERROR_##_code; \ + err->msg = _msg; \ + } \ + if (val_hdr) alc.free(alc.ctx, val_hdr); \ + return NULL; \ +} while (false) + +#define val_incr() do { \ + val++; \ + if (unlikely(val >= val_end)) { \ + usize alc_old = alc_len; \ + usize val_ofs = (usize)(val - val_hdr); \ + usize ctn_ofs = (usize)(ctn - val_hdr); \ + alc_len += alc_len / 2; \ + if ((sizeof(usize) < 8) && (alc_len >= alc_max)) goto fail_alloc; \ + val_tmp = (yyjson_val *)alc.realloc(alc.ctx, (void *)val_hdr, \ + alc_old * sizeof(yyjson_val), \ + alc_len * sizeof(yyjson_val)); \ + if ((!val_tmp)) goto fail_alloc; \ + val = val_tmp + val_ofs; \ + ctn = val_tmp + ctn_ofs; \ + val_hdr = val_tmp; \ + val_end = val_tmp + (alc_len - 2); \ + } \ +} while (false) + + usize dat_len; /* data length in bytes, hint for allocator */ + usize hdr_len; /* value count used by yyjson_doc */ + usize alc_len; /* value count allocated */ + usize alc_max; /* maximum value count for allocator */ + usize ctn_len; /* the number of elements in current container */ + yyjson_val *val_hdr; /* the head of allocated values */ + yyjson_val *val_end; /* the end of allocated values */ + yyjson_val *val_tmp; /* temporary pointer for realloc */ + yyjson_val *val; /* current JSON value */ + yyjson_val *ctn; /* current container */ + yyjson_val *ctn_parent; /* parent of current container */ + yyjson_doc *doc; /* the JSON document, equals to val_hdr */ + const char *msg; /* error message */ + + u8 raw_end[1]; /* raw end for null-terminator */ + u8 *raw_ptr = raw_end; + u8 **pre = &raw_ptr; /* previous raw end pointer */ + + dat_len = has_flg(STOP_WHEN_DONE) ? 256 : (usize)(eof - cur); + hdr_len = sizeof(yyjson_doc) / sizeof(yyjson_val); + hdr_len += (sizeof(yyjson_doc) % sizeof(yyjson_val)) > 0; + alc_max = USIZE_MAX / sizeof(yyjson_val); + alc_len = hdr_len + (dat_len / YYJSON_READER_ESTIMATED_PRETTY_RATIO) + 4; + alc_len = yyjson_min(alc_len, alc_max); + + val_hdr = (yyjson_val *)alc.malloc(alc.ctx, alc_len * sizeof(yyjson_val)); + if (unlikely(!val_hdr)) goto fail_alloc; + val_end = val_hdr + (alc_len - 2); /* padding for key-value pair reading */ + val = val_hdr + hdr_len; + ctn = val; + ctn_len = 0; + + if (*cur++ == '{') { + ctn->tag = YYJSON_TYPE_OBJ; + ctn->uni.ofs = 0; + if (*cur == '\n') cur++; + goto obj_key_begin; + } else { + ctn->tag = YYJSON_TYPE_ARR; + ctn->uni.ofs = 0; + if (*cur == '\n') cur++; + goto arr_val_begin; + } + +arr_begin: + /* save current container */ + ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | + (ctn->tag & YYJSON_TAG_MASK); + + /* create a new array value, save parent container offset */ + val_incr(); + val->tag = YYJSON_TYPE_ARR; + val->uni.ofs = (usize)((u8 *)val - (u8 *)ctn); + + /* push the new array value as current container */ + ctn = val; + ctn_len = 0; + if (*cur == '\n') cur++; + +arr_val_begin: +#if YYJSON_IS_REAL_GCC + while (true) repeat16({ + if (byte_match_2(cur, " ")) cur += 2; + else break; + }) +#else + while (true) repeat16({ + if (likely(byte_match_2(cur, " "))) cur += 2; + else break; + }) +#endif + + if (*cur == '{') { + cur++; + goto obj_begin; + } + if (*cur == '[') { + cur++; + goto arr_begin; + } + if (char_is_num(*cur)) { + val_incr(); + ctn_len++; + if (likely(read_num(&cur, pre, flg, val, &msg))) goto arr_val_end; + goto fail_number; + } + if (*cur == '"') { + val_incr(); + ctn_len++; + if (likely(read_str(&cur, eof, flg, val, &msg))) goto arr_val_end; + goto fail_string; + } + if (*cur == 't') { + val_incr(); + ctn_len++; + if (likely(read_true(&cur, val))) goto arr_val_end; + goto fail_literal_true; + } + if (*cur == 'f') { + val_incr(); + ctn_len++; + if (likely(read_false(&cur, val))) goto arr_val_end; + goto fail_literal_false; + } + if (*cur == 'n') { + val_incr(); + ctn_len++; + if (likely(read_null(&cur, val))) goto arr_val_end; + if (has_allow(INF_AND_NAN)) { + if (read_nan(&cur, pre, flg, val)) goto arr_val_end; + } + goto fail_literal_null; + } + if (*cur == ']') { + cur++; + if (likely(ctn_len == 0)) goto arr_end; + if (has_allow(TRAILING_COMMAS)) goto arr_end; + while (*cur != ',') cur--; + goto fail_trailing_comma; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto arr_val_begin; + } + if (has_allow(INF_AND_NAN) && + (*cur == 'i' || *cur == 'I' || *cur == 'N')) { + val_incr(); + ctn_len++; + if (read_inf_or_nan(&cur, pre, flg, val)) goto arr_val_end; + goto fail_character_val; + } + if (has_allow(SINGLE_QUOTED_STR) && *cur == '\'') { + val_incr(); + ctn_len++; + if (likely(read_str_sq(&cur, eof, flg, val, &msg))) goto arr_val_end; + goto fail_string; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto arr_val_begin; + if (cur == eof) goto fail_comment; + } + goto fail_character_val; + +arr_val_end: + if (byte_match_2(cur, ",\n")) { + cur += 2; + goto arr_val_begin; + } + if (*cur == ',') { + cur++; + goto arr_val_begin; + } + if (*cur == ']') { + cur++; + goto arr_end; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto arr_val_end; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto arr_val_end; + if (cur == eof) goto fail_comment; + } + goto fail_character_arr_end; + +arr_end: + /* get parent container */ + ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); + + /* save the next sibling value offset */ + ctn->uni.ofs = (usize)((u8 *)val - (u8 *)ctn) + sizeof(yyjson_val); + ctn->tag = ((ctn_len) << YYJSON_TAG_BIT) | YYJSON_TYPE_ARR; + if (unlikely(ctn == ctn_parent)) goto doc_end; + + /* pop parent as current container */ + ctn = ctn_parent; + ctn_len = (usize)(ctn->tag >> YYJSON_TAG_BIT); + if (*cur == '\n') cur++; + if ((ctn->tag & YYJSON_TYPE_MASK) == YYJSON_TYPE_OBJ) { + goto obj_val_end; + } else { + goto arr_val_end; + } + +obj_begin: + /* push container */ + ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | + (ctn->tag & YYJSON_TAG_MASK); + val_incr(); + val->tag = YYJSON_TYPE_OBJ; + /* offset to the parent */ + val->uni.ofs = (usize)((u8 *)val - (u8 *)ctn); + ctn = val; + ctn_len = 0; + if (*cur == '\n') cur++; + +obj_key_begin: +#if YYJSON_IS_REAL_GCC + while (true) repeat16({ + if (byte_match_2(cur, " ")) cur += 2; + else break; + }) +#else + while (true) repeat16({ + if (likely(byte_match_2(cur, " "))) cur += 2; + else break; + }) +#endif + if (likely(*cur == '"')) { + val_incr(); + ctn_len++; + if (likely(read_str(&cur, eof, flg, val, &msg))) goto obj_key_end; + goto fail_string; + } + if (likely(*cur == '}')) { + cur++; + if (likely(ctn_len == 0)) goto obj_end; + if (has_allow(TRAILING_COMMAS)) goto obj_end; + while (*cur != ',') cur--; + goto fail_trailing_comma; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_key_begin; + } + if (has_allow(SINGLE_QUOTED_STR) && *cur == '\'') { + val_incr(); + ctn_len++; + if (likely(read_str_sq(&cur, eof, flg, val, &msg))) goto obj_key_end; + goto fail_string; + } + if (has_allow(UNQUOTED_KEY) && char_is_id_start(*cur)) { + val_incr(); + ctn_len++; + if (read_str_id(&cur, eof, flg, pre, val, &msg)) goto obj_key_end; + goto fail_string; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_key_begin; + if (cur == eof) goto fail_comment; + } + goto fail_character_obj_key; + +obj_key_end: + if (byte_match_2(cur, ": ")) { + cur += 2; + goto obj_val_begin; + } + if (*cur == ':') { + cur++; + goto obj_val_begin; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_key_end; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_key_end; + if (cur == eof) goto fail_comment; + } + goto fail_character_obj_sep; + +obj_val_begin: + if (*cur == '"') { + val++; + ctn_len++; + if (likely(read_str(&cur, eof, flg, val, &msg))) goto obj_val_end; + goto fail_string; + } + if (char_is_num(*cur)) { + val++; + ctn_len++; + if (likely(read_num(&cur, pre, flg, val, &msg))) goto obj_val_end; + goto fail_number; + } + if (*cur == '{') { + cur++; + goto obj_begin; + } + if (*cur == '[') { + cur++; + goto arr_begin; + } + if (*cur == 't') { + val++; + ctn_len++; + if (likely(read_true(&cur, val))) goto obj_val_end; + goto fail_literal_true; + } + if (*cur == 'f') { + val++; + ctn_len++; + if (likely(read_false(&cur, val))) goto obj_val_end; + goto fail_literal_false; + } + if (*cur == 'n') { + val++; + ctn_len++; + if (likely(read_null(&cur, val))) goto obj_val_end; + if (has_allow(INF_AND_NAN)) { + if (read_nan(&cur, pre, flg, val)) goto obj_val_end; + } + goto fail_literal_null; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_val_begin; + } + if (has_allow(INF_AND_NAN) && + (*cur == 'i' || *cur == 'I' || *cur == 'N')) { + val++; + ctn_len++; + if (read_inf_or_nan(&cur, pre, flg, val)) goto obj_val_end; + goto fail_character_val; + } + if (has_allow(SINGLE_QUOTED_STR) && *cur == '\'') { + val++; + ctn_len++; + if (likely(read_str_sq(&cur, eof, flg, val, &msg))) goto obj_val_end; + goto fail_string; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_val_begin; + if (cur == eof) goto fail_comment; + } + goto fail_character_val; + +obj_val_end: + if (byte_match_2(cur, ",\n")) { + cur += 2; + goto obj_key_begin; + } + if (likely(*cur == ',')) { + cur++; + goto obj_key_begin; + } + if (likely(*cur == '}')) { + cur++; + goto obj_end; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_val_end; + } + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (skip_trivia(&cur, eof, flg)) goto obj_val_end; + if (cur == eof) goto fail_comment; + } + goto fail_character_obj_end; + +obj_end: + /* pop container */ + ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); + /* point to the next value */ + ctn->uni.ofs = (usize)((u8 *)val - (u8 *)ctn) + sizeof(yyjson_val); + ctn->tag = (ctn_len << (YYJSON_TAG_BIT - 1)) | YYJSON_TYPE_OBJ; + if (unlikely(ctn == ctn_parent)) goto doc_end; + ctn = ctn_parent; + ctn_len = (usize)(ctn->tag >> YYJSON_TAG_BIT); + if (*cur == '\n') cur++; + if ((ctn->tag & YYJSON_TYPE_MASK) == YYJSON_TYPE_OBJ) { + goto obj_val_end; + } else { + goto arr_val_end; + } + +doc_end: + /* check invalid contents after json document */ + if (unlikely(cur < eof) && !has_flg(STOP_WHEN_DONE)) { + while (char_is_space(*cur)) cur++; + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (!skip_trivia(&cur, eof, flg) && cur == eof) { + goto fail_comment; + } + } + if (unlikely(cur < eof)) goto fail_garbage; + } + + **pre = '\0'; + doc = (yyjson_doc *)val_hdr; + doc->root = val_hdr + hdr_len; + doc->alc = alc; + doc->dat_read = (usize)(cur - hdr); + doc->val_read = (usize)((val - doc->root) + 1); + doc->str_pool = has_flg(INSITU) ? NULL : (char *)hdr; + return doc; + +fail_string: return_err(cur, INVALID_STRING, msg); +fail_number: return_err(cur, INVALID_NUMBER, msg); +fail_alloc: return_err(cur, MEMORY_ALLOCATION, MSG_MALLOC); +fail_trailing_comma: return_err(cur, JSON_STRUCTURE, MSG_COMMA); +fail_literal_true: return_err(cur, LITERAL, MSG_CHAR_T); +fail_literal_false: return_err(cur, LITERAL, MSG_CHAR_F); +fail_literal_null: return_err(cur, LITERAL, MSG_CHAR_N); +fail_character_val: return_err(cur, UNEXPECTED_CHARACTER, MSG_CHAR); +fail_character_arr_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_ARR_END); +fail_character_obj_key: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_KEY); +fail_character_obj_sep: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_SEP); +fail_character_obj_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_END); +fail_comment: return_err(cur, INVALID_COMMENT, MSG_COMMENT); +fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); + +#undef val_incr +#undef return_err +} + + + +/*============================================================================== + * MARK: - JSON Reader (Public) + *============================================================================*/ + +yyjson_doc *yyjson_read_opts(char *dat, usize len, + yyjson_read_flag flg, + const yyjson_alc *alc_ptr, + yyjson_read_err *err) { +#define return_err(_pos, _code, _msg) do { \ + err->pos = (usize)(_pos); \ + err->msg = _msg; \ + err->code = YYJSON_READ_ERROR_##_code; \ + if (!has_flg(INSITU) && hdr) alc.free(alc.ctx, (void *)hdr); \ + return NULL; \ +} while (false) + + yyjson_read_err tmp_err; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + yyjson_doc *doc; + u8 *hdr = NULL, *eof, *cur; + + /* validate input parameters */ + if (!err) err = &tmp_err; + if (unlikely(!dat)) return_err(0, INVALID_PARAMETER, "input data is NULL"); + if (unlikely(!len)) return_err(0, INVALID_PARAMETER, "input length is 0"); + + /* add 4-byte zero padding for input data if necessary */ + if (has_flg(INSITU)) { + hdr = (u8 *)dat; + eof = (u8 *)dat + len; + cur = (u8 *)dat; + } else { + if (unlikely(len >= USIZE_MAX - YYJSON_PADDING_SIZE)) { + return_err(0, MEMORY_ALLOCATION, MSG_MALLOC); + } + hdr = (u8 *)alc.malloc(alc.ctx, len + YYJSON_PADDING_SIZE); + if (unlikely(!hdr)) { + return_err(0, MEMORY_ALLOCATION, MSG_MALLOC); + } + eof = hdr + len; + cur = hdr; + memcpy(hdr, dat, len); + } + memset(eof, 0, YYJSON_PADDING_SIZE); + + if (has_allow(BOM)) { + if (len >= 3 && is_utf8_bom(cur)) cur += 3; + } + + /* skip empty contents before json document */ + if (unlikely(!char_is_ctn(*cur))) { + while (char_is_space(*cur)) cur++; + if (unlikely(!char_is_ctn(*cur))) { + if (has_allow(TRIVIA) && char_is_trivia(*cur)) { + if (!skip_trivia(&cur, eof, flg) && cur == eof) { + return_err(cur - hdr, INVALID_COMMENT, MSG_COMMENT); + } + } + } + if (unlikely(cur >= eof)) { + return_err(0, EMPTY_CONTENT, "input data is empty"); + } + } + + /* read json document */ + if (likely(char_is_ctn(*cur))) { + if (char_is_space(cur[1]) && char_is_space(cur[2])) { + doc = read_root_pretty(hdr, cur, eof, alc, flg, err); + } else { + doc = read_root_minify(hdr, cur, eof, alc, flg, err); + } + } else { + doc = read_root_single(hdr, cur, eof, alc, flg, err); + } + + /* check result */ + if (likely(doc)) { + memset(err, 0, sizeof(yyjson_read_err)); + } else { + /* RFC 8259: JSON text MUST be encoded using UTF-8 */ + if (err->pos == 0 && err->code != YYJSON_READ_ERROR_MEMORY_ALLOCATION) { + if (is_utf8_bom(hdr)) err->msg = MSG_ERR_BOM; + else if (len >= 4 && is_utf32_bom(hdr)) err->msg = MSG_ERR_UTF32; + else if (len >= 2 && is_utf16_bom(hdr)) err->msg = MSG_ERR_UTF16; + } + if (!has_flg(INSITU)) alc.free(alc.ctx, hdr); + } + return doc; + +#undef return_err +} + +yyjson_doc *yyjson_read_file(const char *path, + yyjson_read_flag flg, + const yyjson_alc *alc_ptr, + yyjson_read_err *err) { +#define return_err(_code, _msg) do { \ + err->pos = 0; \ + err->msg = _msg; \ + err->code = YYJSON_READ_ERROR_##_code; \ + return NULL; \ +} while (false) + + yyjson_read_err tmp_err; + yyjson_doc *doc; + FILE *file; + + if (!err) err = &tmp_err; + if (unlikely(!path)) return_err(INVALID_PARAMETER, "input path is NULL"); + + file = fopen_readonly(path); + if (unlikely(!file)) return_err(FILE_OPEN, MSG_FREAD); + + doc = yyjson_read_fp(file, flg, alc_ptr, err); + fclose(file); + return doc; + +#undef return_err +} + +yyjson_doc *yyjson_read_fp(FILE *file, + yyjson_read_flag flg, + const yyjson_alc *alc_ptr, + yyjson_read_err *err) { +#define return_err(_code, _msg) do { \ + err->pos = 0; \ + err->msg = _msg; \ + err->code = YYJSON_READ_ERROR_##_code; \ + if (buf) alc.free(alc.ctx, buf); \ + return NULL; \ +} while (false) + + yyjson_read_err tmp_err; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + yyjson_doc *doc; + + long file_size = 0, file_pos; + void *buf = NULL; + usize buf_size = 0; + + /* validate input parameters */ + if (!err) err = &tmp_err; + if (unlikely(!file)) return_err(INVALID_PARAMETER, "input file is NULL"); + + /* get current position */ + file_pos = ftell(file); + if (file_pos != -1) { + /* get total file size, may fail */ + if (fseek(file, 0, SEEK_END) == 0) file_size = ftell(file); + /* reset to original position, may fail */ + if (fseek(file, file_pos, SEEK_SET) != 0) file_size = 0; + /* get file size from current postion to end */ + if (file_size > 0) file_size -= file_pos; + } + + /* read file */ + if (file_size > 0) { + /* read the entire file in one call */ + buf_size = (usize)file_size + YYJSON_PADDING_SIZE; + buf = alc.malloc(alc.ctx, buf_size); + if (buf == NULL) { + return_err(MEMORY_ALLOCATION, MSG_MALLOC); + } + if (fread_safe(buf, (usize)file_size, file) != (usize)file_size) { + return_err(FILE_READ, MSG_FREAD); + } + } else { + /* failed to get file size, read it as a stream */ + usize chunk_min = (usize)64; + usize chunk_max = (usize)512 * 1024 * 1024; + usize chunk_now = chunk_min; + usize read_size; + void *tmp; + + buf_size = YYJSON_PADDING_SIZE; + while (true) { + if (buf_size + chunk_now < buf_size) { /* overflow */ + return_err(MEMORY_ALLOCATION, MSG_MALLOC); + } + buf_size += chunk_now; + if (!buf) { + buf = alc.malloc(alc.ctx, buf_size); + if (!buf) return_err(MEMORY_ALLOCATION, MSG_MALLOC); + } else { + tmp = alc.realloc(alc.ctx, buf, buf_size - chunk_now, buf_size); + if (!tmp) return_err(MEMORY_ALLOCATION, MSG_MALLOC); + buf = tmp; + } + tmp = ((u8 *)buf) + buf_size - YYJSON_PADDING_SIZE - chunk_now; + read_size = fread_safe(tmp, chunk_now, file); + file_size += (long)read_size; + if (read_size != chunk_now) break; + + chunk_now *= 2; + if (chunk_now > chunk_max) chunk_now = chunk_max; + } + } + + /* read JSON */ + memset((u8 *)buf + file_size, 0, YYJSON_PADDING_SIZE); + flg |= YYJSON_READ_INSITU; + doc = yyjson_read_opts((char *)buf, (usize)file_size, flg, &alc, err); + if (doc) { + doc->str_pool = (char *)buf; + return doc; + } else { + alc.free(alc.ctx, buf); + return NULL; + } + +#undef return_err +} + +const char *yyjson_read_number(const char *dat, + yyjson_val *val, + yyjson_read_flag flg, + const yyjson_alc *alc, + yyjson_read_err *err) { +#define return_err(_pos, _code, _msg) do { \ + err->pos = _pos > hdr ? (usize)(_pos - hdr) : 0; \ + err->msg = _msg; \ + err->code = YYJSON_READ_ERROR_##_code; \ + return NULL; \ +} while (false) + + u8 *hdr = constcast(u8 *)dat, *cur = hdr; + u8 raw_end[1]; /* raw end for null-terminator */ + u8 *raw_ptr = raw_end; + u8 **pre = &raw_ptr; /* previous raw end pointer */ + const char *msg; + yyjson_read_err tmp_err; + +#if YYJSON_DISABLE_FAST_FP_CONV + u8 buf[128]; + usize dat_len; +#endif + + if (!err) err = &tmp_err; + if (unlikely(!dat)) { + return_err(cur, INVALID_PARAMETER, "input data is NULL"); + } + if (unlikely(!val)) { + return_err(cur, INVALID_PARAMETER, "output value is NULL"); + } + +#if YYJSON_DISABLE_FAST_FP_CONV + if (!alc) alc = &YYJSON_DEFAULT_ALC; + dat_len = strlen(dat); + if (dat_len < sizeof(buf)) { + memcpy(buf, dat, dat_len + 1); + hdr = buf; + cur = hdr; + } else { + hdr = (u8 *)alc->malloc(alc->ctx, dat_len + 1); + cur = hdr; + if (unlikely(!hdr)) { + return_err(cur, MEMORY_ALLOCATION, MSG_MALLOC); + } + memcpy(hdr, dat, dat_len + 1); + } + hdr[dat_len] = 0; +#endif + +#if YYJSON_DISABLE_FAST_FP_CONV + if (!read_num(&cur, pre, flg, val, &msg)) { + if (dat_len >= sizeof(buf)) alc->free(alc->ctx, hdr); + return_err(cur, INVALID_NUMBER, msg); + } + if (dat_len >= sizeof(buf)) alc->free(alc->ctx, hdr); + if (yyjson_is_raw(val)) val->uni.str = dat; + return dat + (cur - hdr); +#else + if (!read_num(&cur, pre, flg, val, &msg)) { + return_err(cur, INVALID_NUMBER, msg); + } + return (const char *)cur; +#endif + +#undef return_err +} + + + +/*============================================================================== + * MARK: - Incremental JSON Reader (Public) + *============================================================================*/ + +#if !YYJSON_DISABLE_INCR_READER + +/* labels within yyjson_incr_read() to resume incremental parsing */ +#define LABEL_doc_begin 0 +#define LABEL_arr_val_begin 1 +#define LABEL_arr_val_end 2 +#define LABEL_obj_key_begin 3 +#define LABEL_obj_key_end 4 +#define LABEL_obj_val_begin 5 +#define LABEL_obj_val_end 6 +#define LABEL_doc_end 7 + +/** State for incremental JSON reader, opaque in the API. */ +struct yyjson_incr_state { + u32 label; /* current parser goto label */ + yyjson_alc alc; /* allocator */ + yyjson_read_flag flg; /* read flags */ + u8 *hdr; /* JSON data header */ + u8 *cur; /* current position in JSON data */ + usize buf_len; /* total buffer length (without padding) */ + usize hdr_len; /* value count used by yyjson_doc */ + usize alc_len; /* value count allocated */ + usize ctn_len; /* the number of elements in current container */ + yyjson_val *val_hdr; /* the head of allocated values */ + yyjson_val *val_end; /* the end of allocated values */ + yyjson_val *val; /* current JSON value */ + yyjson_val *ctn; /* current container */ + u8 *str_con[2]; /* string parser incremental state */ +}; + +yyjson_incr_state *yyjson_incr_new(char *buf, size_t buf_len, + yyjson_read_flag flg, + const yyjson_alc *alc_ptr) { + yyjson_incr_state *state = NULL; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + + /* remove non-standard flags */ + flg &= ~YYJSON_READ_JSON5; + flg &= ~YYJSON_READ_ALLOW_BOM; + flg &= ~YYJSON_READ_ALLOW_INVALID_UNICODE; + + if (unlikely(!buf)) return NULL; + if (unlikely(buf_len >= USIZE_MAX - YYJSON_PADDING_SIZE)) return NULL; + state = (yyjson_incr_state *)alc.malloc(alc.ctx, sizeof(*state)); + if (!state) return NULL; + memset(state, 0, sizeof(yyjson_incr_state)); + state->alc = alc; + state->flg = flg; + state->buf_len = buf_len; + + /* add 4-byte zero padding for input data if necessary */ + if (has_flg(INSITU)) { + state->hdr = (u8 *)buf; + } else { + state->hdr = (u8 *)alc.malloc(alc.ctx, buf_len + YYJSON_PADDING_SIZE); + if (unlikely(!state->hdr)) { + alc.free(alc.ctx, state); + return NULL; + } + memcpy(state->hdr, buf, buf_len); + } + memset(state->hdr + buf_len, 0, YYJSON_PADDING_SIZE); + state->cur = state->hdr; + state->label = LABEL_doc_begin; + return state; +} + +void yyjson_incr_free(yyjson_incr_state *state) { + if (state) { + yyjson_alc alc = state->alc; + memset(&state->alc, 0, sizeof(alc)); + if (state->val_hdr) { + alc.free(alc.ctx, (void *)state->val_hdr); + } + if (state->hdr && !(state->flg & YYJSON_READ_INSITU)) { + alc.free(alc.ctx, state->hdr); + } + alc.free(alc.ctx, state); + } +} + +yyjson_doc *yyjson_incr_read(yyjson_incr_state *state, size_t len, + yyjson_read_err *err) { +#define return_err_inv_param(_msg) do { \ + err->pos = 0; \ + err->msg = _msg; \ + err->code = YYJSON_READ_ERROR_INVALID_PARAMETER; \ + return NULL; \ +} while (false) + +#define return_err(_pos, _code, _msg) do { \ + if (is_truncated_end(hdr, _pos, end, YYJSON_READ_ERROR_##_code, flg)) { \ + goto unexpected_end; \ + } else { \ + err->pos = (usize)(_pos - hdr); \ + err->code = YYJSON_READ_ERROR_##_code; \ + err->msg = _msg; \ + } \ + return NULL; \ +} while (false) + +#define val_incr() do { \ + val++; \ + if (unlikely(val >= val_end)) { \ + usize alc_old = alc_len; \ + alc_len += alc_len / 2; \ + if ((sizeof(usize) < 8) && (alc_len >= alc_max)) goto fail_alloc; \ + val_tmp = (yyjson_val *)alc.realloc(alc.ctx, (void *)val_hdr, \ + alc_old * sizeof(yyjson_val), \ + alc_len * sizeof(yyjson_val)); \ + if ((!val_tmp)) goto fail_alloc; \ + val = val_tmp + (usize)(val - val_hdr); \ + ctn = val_tmp + (usize)(ctn - val_hdr); \ + state->val = val_tmp + (usize)(state->val - val_hdr); \ + state->val_hdr = val_hdr = val_tmp; \ + val_end = val_tmp + (alc_len - 2); \ + state->val_end = val_end; \ + } \ +} while (false) + + /* save position where it's possible to resume incremental parsing */ +#define save_incr_state(_label) do { \ + state->label = LABEL_##_label; \ + state->cur = cur; \ + state->val = val; \ + state->ctn_len = ctn_len; \ + state->hdr_len = hdr_len; \ + if (unlikely(cur >= end)) goto unexpected_end; \ +} while (false) + +#define check_maybe_truncated_number() do { \ + if (unlikely(cur >= end)) { \ + if (unlikely(cur > state->cur + INCR_NUM_MAX_LEN)) { \ + msg = "number too long"; \ + goto fail_number; \ + } \ + goto unexpected_end; \ + } \ +} while (false) + + u8 *hdr = NULL, *end = NULL, *cur = NULL; + yyjson_read_flag flg; + yyjson_alc alc; + usize dat_len; /* data length in bytes, hint for allocator */ + usize hdr_len; /* value count used by yyjson_doc */ + usize alc_len; /* value count allocated */ + usize alc_max; /* maximum value count for allocator */ + usize ctn_len; /* the number of elements in current container */ + yyjson_val *val_hdr; /* the head of allocated values */ + yyjson_val *val_end; /* the end of allocated values */ + yyjson_val *val_tmp; /* temporary pointer for realloc */ + yyjson_val *val; /* current JSON value */ + yyjson_val *ctn; /* current container */ + yyjson_val *ctn_parent; /* parent of current container */ + yyjson_doc *doc; /* the JSON document, equals to val_hdr */ + const char *msg; /* error message */ + + yyjson_read_err tmp_err; + u8 raw_end[1]; /* raw end for null-terminator */ + u8 *raw_ptr = raw_end; + u8 **pre = &raw_ptr; /* previous raw end pointer */ + u8 **con = NULL; /* for incremental string parsing */ + u8 saved_end = '\0'; /* saved end char */ + + /* validate input parameters */ + if (!err) err = &tmp_err; + if (unlikely(!state)) { + return_err_inv_param("input state is NULL"); + } + if (unlikely(!len)) { + return_err_inv_param("input length is 0"); + } + if (unlikely(len > state->buf_len)) { + return_err_inv_param("length is greater than total input length"); + } + + /* restore state saved from the previous call */ + hdr = state->hdr; + end = state->hdr + len; + cur = state->cur; + flg = state->flg; + alc = state->alc; + ctn_len = state->ctn_len; + hdr_len = state->hdr_len; + alc_len = state->alc_len; + val = state->val; + val_hdr = state->val_hdr; + val_end = state->val_end; + ctn = state->ctn; + con = state->str_con; + alc_max = USIZE_MAX / sizeof(yyjson_val); + + /* insert null terminator to make us stop at the specified end, even if + the data contains more valid JSON */ + saved_end = *end; + *end = '\0'; + + /* resume parsing from the last save point */ + switch (state->label) { + case LABEL_doc_begin: goto doc_begin; + case LABEL_arr_val_begin: goto arr_val_begin; + case LABEL_arr_val_end: goto arr_val_end; + case LABEL_obj_key_begin: goto obj_key_begin; + case LABEL_obj_key_end: goto obj_key_end; + case LABEL_obj_val_begin: goto obj_val_begin; + case LABEL_obj_val_end: goto obj_val_end; + case LABEL_doc_end: goto doc_end; + default: return_err_inv_param("invalid incremental state"); + } + +doc_begin: + /* skip empty contents before json document */ + if (unlikely(!char_is_ctn(*cur))) { + while (char_is_space(*cur)) cur++; + if (unlikely(cur >= end)) goto unexpected_end; /* input data is empty */ + } + + /* allocate memory for document */ + if (!val_hdr) { + hdr_len = sizeof(yyjson_doc) / sizeof(yyjson_val); + hdr_len += (sizeof(yyjson_doc) % sizeof(yyjson_val)) > 0; + if (likely(char_is_ctn(*cur))) { + dat_len = has_flg(STOP_WHEN_DONE) ? 256 : state->buf_len; + alc_len = hdr_len + + (dat_len / YYJSON_READER_ESTIMATED_MINIFY_RATIO) + 4; + alc_len = yyjson_min(alc_len, alc_max); + } else { + alc_len = hdr_len + 1; /* single value */ + } + val_hdr = (yyjson_val *)alc.malloc(alc.ctx, + alc_len * sizeof(yyjson_val)); + if (unlikely(!val_hdr)) goto fail_alloc; + val_end = val_hdr + (alc_len - 2); /* padding for kv pair reading */ + val = val_hdr + hdr_len; + ctn = val; + ctn_len = 0; + state->val_hdr = val_hdr; + state->val_end = val_end; + save_incr_state(doc_begin); + } + + /* read json document */ + if (*cur == '{') { + cur++; + ctn->tag = YYJSON_TYPE_OBJ; + ctn->uni.ofs = 0; + goto obj_key_begin; + } + if (*cur == '[') { + cur++; + ctn->tag = YYJSON_TYPE_ARR; + ctn->uni.ofs = 0; + goto arr_val_begin; + } + if (char_is_num(*cur)) { + if (likely(read_num(&cur, pre, flg, val, &msg))) goto doc_end; + goto fail_number; + } + if (*cur == '"') { + if (likely(read_str_con(&cur, end, flg, val, &msg, con))) goto doc_end; + goto fail_string; + } + if (*cur == 't') { + if (likely(read_true(&cur, val))) goto doc_end; + goto fail_literal_true; + } + if (*cur == 'f') { + if (likely(read_false(&cur, val))) goto doc_end; + goto fail_literal_false; + } + if (*cur == 'n') { + if (likely(read_null(&cur, val))) goto doc_end; + goto fail_literal_null; + } + + msg = "unexpected character, expected a valid root value"; + if (cur == hdr) { + /* RFC 8259: JSON text MUST be encoded using UTF-8 */ + if (is_utf8_bom(hdr)) msg = MSG_ERR_BOM; + else if (len >= 4 && is_utf32_bom(hdr)) msg = MSG_ERR_UTF32; + else if (len >= 2 && is_utf16_bom(hdr)) msg = MSG_ERR_UTF16; + } + return_err(cur, UNEXPECTED_CHARACTER, msg); + +arr_begin: + /* save current container */ + ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | + (ctn->tag & YYJSON_TAG_MASK); + + /* create a new array value, save parent container offset */ + val_incr(); + val->tag = YYJSON_TYPE_ARR; + val->uni.ofs = (usize)((u8 *)val - (u8 *)ctn); + + /* push the new array value as current container */ + ctn = val; + ctn_len = 0; + +arr_val_begin: + save_incr_state(arr_val_begin); +arr_val_continue: + if (*cur == '{') { + cur++; + goto obj_begin; + } + if (*cur == '[') { + cur++; + goto arr_begin; + } + if (char_is_num(*cur)) { + val_incr(); + ctn_len++; + if (likely(read_num(&cur, pre, flg, val, &msg))) goto arr_val_maybe_end; + goto fail_number; + } + if (*cur == '"') { + val_incr(); + ctn_len++; + if (likely(read_str_con(&cur, end, flg, val, &msg, con))) + goto arr_val_end; + goto fail_string; + } + if (*cur == 't') { + val_incr(); + ctn_len++; + if (likely(read_true(&cur, val))) goto arr_val_end; + goto fail_literal_true; + } + if (*cur == 'f') { + val_incr(); + ctn_len++; + if (likely(read_false(&cur, val))) goto arr_val_end; + goto fail_literal_false; + } + if (*cur == 'n') { + val_incr(); + ctn_len++; + if (likely(read_null(&cur, val))) goto arr_val_end; + goto fail_literal_null; + } + if (*cur == ']') { + cur++; + if (likely(ctn_len == 0)) goto arr_end; + while (*cur != ',') cur--; + goto fail_trailing_comma; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto arr_val_continue; + } + goto fail_character_val; + +arr_val_maybe_end: + /* if incremental parsing stops in the middle of a number, it may continue + with more digits, so arr val maybe didn't end yet */ + check_maybe_truncated_number(); + +arr_val_end: + save_incr_state(arr_val_end); + if (*cur == ',') { + cur++; + goto arr_val_begin; + } + if (*cur == ']') { + cur++; + goto arr_end; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto arr_val_end; + } + goto fail_character_arr_end; + +arr_end: + /* get parent container */ + ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); + + /* save the next sibling value offset */ + ctn->uni.ofs = (usize)((u8 *)val - (u8 *)ctn) + sizeof(yyjson_val); + ctn->tag = ((ctn_len) << YYJSON_TAG_BIT) | YYJSON_TYPE_ARR; + if (unlikely(ctn == ctn_parent)) goto doc_end; + + /* pop parent as current container */ + ctn = ctn_parent; + ctn_len = (usize)(ctn->tag >> YYJSON_TAG_BIT); + if ((ctn->tag & YYJSON_TYPE_MASK) == YYJSON_TYPE_OBJ) { + goto obj_val_end; + } else { + goto arr_val_end; + } + +obj_begin: + /* push container */ + ctn->tag = (((u64)ctn_len + 1) << YYJSON_TAG_BIT) | + (ctn->tag & YYJSON_TAG_MASK); + val_incr(); + val->tag = YYJSON_TYPE_OBJ; + /* offset to the parent */ + val->uni.ofs = (usize)((u8 *)val - (u8 *)ctn); + ctn = val; + ctn_len = 0; + +obj_key_begin: + save_incr_state(obj_key_begin); +obj_key_continue: + if (likely(*cur == '"')) { + val_incr(); + ctn_len++; + if (likely(read_str_con(&cur, end, flg, val, &msg, con))) + goto obj_key_end; + goto fail_string; + } + if (likely(*cur == '}')) { + cur++; + if (likely(ctn_len == 0)) goto obj_end; + while (*cur != ',') cur--; + goto fail_trailing_comma; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_key_continue; + } + goto fail_character_obj_key; + +obj_key_end: + save_incr_state(obj_key_end); + if (*cur == ':') { + cur++; + goto obj_val_begin; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_key_end; + } + goto fail_character_obj_sep; + +obj_val_begin: + save_incr_state(obj_val_begin); +obj_val_continue: + if (*cur == '"') { + val++; + ctn_len++; + if (likely(read_str_con(&cur, end, flg, val, &msg, con))) + goto obj_val_end; + goto fail_string; + } + if (char_is_num(*cur)) { + val++; + ctn_len++; + if (likely(read_num(&cur, pre, flg, val, &msg))) goto obj_val_maybe_end; + goto fail_number; + } + if (*cur == '{') { + cur++; + goto obj_begin; + } + if (*cur == '[') { + cur++; + goto arr_begin; + } + if (*cur == 't') { + val++; + ctn_len++; + if (likely(read_true(&cur, val))) goto obj_val_end; + goto fail_literal_true; + } + if (*cur == 'f') { + val++; + ctn_len++; + if (likely(read_false(&cur, val))) goto obj_val_end; + goto fail_literal_false; + } + if (*cur == 'n') { + val++; + ctn_len++; + if (likely(read_null(&cur, val))) goto obj_val_end; + goto fail_literal_null; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_val_continue; + } + goto fail_character_val; + +obj_val_maybe_end: + /* if incremental parsing stops in the middle of a number, it may continue + with more digits, so obj val maybe didn't end yet */ + check_maybe_truncated_number(); + +obj_val_end: + save_incr_state(obj_val_end); + if (likely(*cur == ',')) { + cur++; + goto obj_key_begin; + } + if (likely(*cur == '}')) { + cur++; + goto obj_end; + } + if (char_is_space(*cur)) { + while (char_is_space(*++cur)); + goto obj_val_end; + } + goto fail_character_obj_end; + +obj_end: + /* pop container */ + ctn_parent = (yyjson_val *)(void *)((u8 *)ctn - ctn->uni.ofs); + /* point to the next value */ + ctn->uni.ofs = (usize)((u8 *)val - (u8 *)ctn) + sizeof(yyjson_val); + ctn->tag = (ctn_len << (YYJSON_TAG_BIT - 1)) | YYJSON_TYPE_OBJ; + if (unlikely(ctn == ctn_parent)) goto doc_end; + ctn = ctn_parent; + ctn_len = (usize)(ctn->tag >> YYJSON_TAG_BIT); + if ((ctn->tag & YYJSON_TYPE_MASK) == YYJSON_TYPE_OBJ) { + goto obj_val_end; + } else { + goto arr_val_end; + } + +doc_end: + /* check invalid contents after json document */ + if (unlikely(cur < end) && !has_flg(STOP_WHEN_DONE)) { + save_incr_state(doc_end); + while (char_is_space(*cur)) cur++; + if (unlikely(cur < end)) goto fail_garbage; + } + + **pre = '\0'; + doc = (yyjson_doc *)val_hdr; + doc->root = val_hdr + hdr_len; + doc->alc = alc; + doc->dat_read = (usize)(cur - hdr); + doc->val_read = (usize)((val - doc->root) + 1); + doc->str_pool = has_flg(INSITU) ? NULL : (char *)hdr; + state->hdr = NULL; + state->val_hdr = NULL; + memset(err, 0, sizeof(yyjson_read_err)); + return doc; + +unexpected_end: + err->pos = len; + /* if no nore data, stop the incr read */ + if (unlikely(len >= state->buf_len)) { + err->code = YYJSON_READ_ERROR_UNEXPECTED_END; + err->msg = MSG_NOT_END; + return NULL; + } + /* save parser state in extended error struct, in addition to what was + * stored in the last save_incr_state */ + err->code = YYJSON_READ_ERROR_MORE; + err->msg = "need more data"; + state->val_end = val_end; + state->ctn = ctn; + state->alc_len = alc_len; + /* restore the end where we've inserted a null terminator */ + *end = saved_end; + return NULL; + +fail_string: return_err(cur, INVALID_STRING, msg); +fail_number: return_err(cur, INVALID_NUMBER, msg); +fail_alloc: return_err(cur, MEMORY_ALLOCATION, MSG_MALLOC); +fail_trailing_comma: return_err(cur, JSON_STRUCTURE, MSG_COMMA); +fail_literal_true: return_err(cur, LITERAL, MSG_CHAR_T); +fail_literal_false: return_err(cur, LITERAL, MSG_CHAR_F); +fail_literal_null: return_err(cur, LITERAL, MSG_CHAR_N); +fail_character_val: return_err(cur, UNEXPECTED_CHARACTER, MSG_CHAR); +fail_character_arr_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_ARR_END); +fail_character_obj_key: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_KEY); +fail_character_obj_sep: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_SEP); +fail_character_obj_end: return_err(cur, UNEXPECTED_CHARACTER, MSG_OBJ_END); +fail_garbage: return_err(cur, UNEXPECTED_CONTENT, MSG_GARBAGE); + +#undef val_incr +#undef return_err +#undef return_err_inv_param +#undef save_incr_state +#undef check_maybe_truncated_number +} + +#endif /* YYJSON_DISABLE_INCR_READER */ + +#undef has_flg +#undef has_allow +#endif /* YYJSON_DISABLE_READER */ + + + +#if !YYJSON_DISABLE_WRITER /* writer begin */ + +/* Check write flag, avoids `always false` warning when disabled. */ +#define has_flg(_flg) unlikely(has_wflag(flg, YYJSON_WRITE_##_flg, 0)) +#define has_allow(_flg) unlikely(has_wflag(flg, YYJSON_WRITE_ALLOW_##_flg, 1)) +static_inline bool has_wflag(yyjson_write_flag flg, yyjson_write_flag chk, + bool non_standard) { +#if YYJSON_DISABLE_NON_STANDARD + if (non_standard) return false; +#endif + return (flg & chk) != 0; +} + +/*============================================================================== + * MARK: - Integer Writer (Private) + * + * The maximum value of uint32_t is 4294967295 (10 digits), + * these digits are named as 'aabbccddee' here. + * + * Although most compilers may convert the "division by constant value" into + * "multiply and shift", manual conversion can still help some compilers + * generate fewer and better instructions. + * + * Reference: + * Division by Invariant Integers using Multiplication, 1994. + * https://gmplib.org/~tege/divcnst-pldi94.pdf + * Improved division by invariant integers, 2011. + * https://gmplib.org/~tege/division-paper.pdf + *============================================================================*/ + +/** Digit table from 00 to 99. */ +yyjson_align(2) +static const char digit_table[200] = { + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', + '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', + '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', + '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', + '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', + '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', + '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', + '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', + '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', + '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', + '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', + '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', + '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', + '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', + '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', + '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', + '9', '5', '9', '6', '9', '7', '9', '8', '9', '9' +}; + +static_inline u8 *write_u32_len_8(u32 val, u8 *buf) { + u32 aa, bb, cc, dd, aabb, ccdd; /* 8 digits: aabbccdd */ + aabb = (u32)(((u64)val * 109951163) >> 40); /* (val / 10000) */ + ccdd = val - aabb * 10000; /* (val % 10000) */ + aa = (aabb * 5243) >> 19; /* (aabb / 100) */ + cc = (ccdd * 5243) >> 19; /* (ccdd / 100) */ + bb = aabb - aa * 100; /* (aabb % 100) */ + dd = ccdd - cc * 100; /* (ccdd % 100) */ + byte_copy_2(buf + 0, digit_table + aa * 2); + byte_copy_2(buf + 2, digit_table + bb * 2); + byte_copy_2(buf + 4, digit_table + cc * 2); + byte_copy_2(buf + 6, digit_table + dd * 2); + return buf + 8; +} + +static_inline u8 *write_u32_len_4(u32 val, u8 *buf) { + u32 aa, bb; /* 4 digits: aabb */ + aa = (val * 5243) >> 19; /* (val / 100) */ + bb = val - aa * 100; /* (val % 100) */ + byte_copy_2(buf + 0, digit_table + aa * 2); + byte_copy_2(buf + 2, digit_table + bb * 2); + return buf + 4; +} + +static_inline u8 *write_u32_len_1_to_8(u32 val, u8 *buf) { + u32 aa, bb, cc, dd, aabb, bbcc, ccdd, lz; + + if (val < 100) { /* 1-2 digits: aa */ + lz = val < 10; /* leading zero: 0 or 1 */ + byte_copy_2(buf + 0, digit_table + val * 2 + lz); + buf -= lz; + return buf + 2; + + } else if (val < 10000) { /* 3-4 digits: aabb */ + aa = (val * 5243) >> 19; /* (val / 100) */ + bb = val - aa * 100; /* (val % 100) */ + lz = aa < 10; /* leading zero: 0 or 1 */ + byte_copy_2(buf + 0, digit_table + aa * 2 + lz); + buf -= lz; + byte_copy_2(buf + 2, digit_table + bb * 2); + return buf + 4; + + } else if (val < 1000000) { /* 5-6 digits: aabbcc */ + aa = (u32)(((u64)val * 429497) >> 32); /* (val / 10000) */ + bbcc = val - aa * 10000; /* (val % 10000) */ + bb = (bbcc * 5243) >> 19; /* (bbcc / 100) */ + cc = bbcc - bb * 100; /* (bbcc % 100) */ + lz = aa < 10; /* leading zero: 0 or 1 */ + byte_copy_2(buf + 0, digit_table + aa * 2 + lz); + buf -= lz; + byte_copy_2(buf + 2, digit_table + bb * 2); + byte_copy_2(buf + 4, digit_table + cc * 2); + return buf + 6; + + } else { /* 7-8 digits: aabbccdd */ + aabb = (u32)(((u64)val * 109951163) >> 40); /* (val / 10000) */ + ccdd = val - aabb * 10000; /* (val % 10000) */ + aa = (aabb * 5243) >> 19; /* (aabb / 100) */ + cc = (ccdd * 5243) >> 19; /* (ccdd / 100) */ + bb = aabb - aa * 100; /* (aabb % 100) */ + dd = ccdd - cc * 100; /* (ccdd % 100) */ + lz = aa < 10; /* leading zero: 0 or 1 */ + byte_copy_2(buf + 0, digit_table + aa * 2 + lz); + buf -= lz; + byte_copy_2(buf + 2, digit_table + bb * 2); + byte_copy_2(buf + 4, digit_table + cc * 2); + byte_copy_2(buf + 6, digit_table + dd * 2); + return buf + 8; + } +} + +static_inline u8 *write_u32_len_5_to_8(u32 val, u8 *buf) { + u32 aa, bb, cc, dd, aabb, bbcc, ccdd, lz; + + if (val < 1000000) { /* 5-6 digits: aabbcc */ + aa = (u32)(((u64)val * 429497) >> 32); /* (val / 10000) */ + bbcc = val - aa * 10000; /* (val % 10000) */ + bb = (bbcc * 5243) >> 19; /* (bbcc / 100) */ + cc = bbcc - bb * 100; /* (bbcc % 100) */ + lz = aa < 10; /* leading zero: 0 or 1 */ + byte_copy_2(buf + 0, digit_table + aa * 2 + lz); + buf -= lz; + byte_copy_2(buf + 2, digit_table + bb * 2); + byte_copy_2(buf + 4, digit_table + cc * 2); + return buf + 6; + + } else { /* 7-8 digits: aabbccdd */ + aabb = (u32)(((u64)val * 109951163) >> 40); /* (val / 10000) */ + ccdd = val - aabb * 10000; /* (val % 10000) */ + aa = (aabb * 5243) >> 19; /* (aabb / 100) */ + cc = (ccdd * 5243) >> 19; /* (ccdd / 100) */ + bb = aabb - aa * 100; /* (aabb % 100) */ + dd = ccdd - cc * 100; /* (ccdd % 100) */ + lz = aa < 10; /* leading zero: 0 or 1 */ + byte_copy_2(buf + 0, digit_table + aa * 2 + lz); + buf -= lz; + byte_copy_2(buf + 2, digit_table + bb * 2); + byte_copy_2(buf + 4, digit_table + cc * 2); + byte_copy_2(buf + 6, digit_table + dd * 2); + return buf + 8; + } +} + +static_inline u8 *write_u64(u64 val, u8 *buf) { + u64 tmp, hgh; + u32 mid, low; + + if (val < 100000000) { /* 1-8 digits */ + buf = write_u32_len_1_to_8((u32)val, buf); + return buf; + + } else if (val < (u64)100000000 * 100000000) { /* 9-16 digits */ + hgh = val / 100000000; /* (val / 100000000) */ + low = (u32)(val - hgh * 100000000); /* (val % 100000000) */ + buf = write_u32_len_1_to_8((u32)hgh, buf); + buf = write_u32_len_8(low, buf); + return buf; + + } else { /* 17-20 digits */ + tmp = val / 100000000; /* (val / 100000000) */ + low = (u32)(val - tmp * 100000000); /* (val % 100000000) */ + hgh = (u32)(tmp / 10000); /* (tmp / 10000) */ + mid = (u32)(tmp - hgh * 10000); /* (tmp % 10000) */ + buf = write_u32_len_5_to_8((u32)hgh, buf); + buf = write_u32_len_4(mid, buf); + buf = write_u32_len_8(low, buf); + return buf; + } +} + + + +/*============================================================================== + * MARK: - Number Writer (Private) + *============================================================================*/ + +#if !YYJSON_DISABLE_FAST_FP_CONV /* FP_WRITER */ + +/** Trailing zero count table for number 0 to 99. + (generate with misc/make_tables.c) */ +static const u8 dec_trailing_zero_table[] = { + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static_inline u8 *write_u64_len_1_to_16(u64 val, u8 *buf) { + u64 hgh; + u32 low; + if (val < 100000000) { /* 1-8 digits */ + buf = write_u32_len_1_to_8((u32)val, buf); + return buf; + } else { /* 9-16 digits */ + hgh = val / 100000000; /* (val / 100000000) */ + low = (u32)(val - hgh * 100000000); /* (val % 100000000) */ + buf = write_u32_len_1_to_8((u32)hgh, buf); + buf = write_u32_len_8(low, buf); + return buf; + } +} + +static_inline u8 *write_u64_len_1_to_17(u64 val, u8 *buf) { + u64 hgh; + u32 mid, low, one; + if (val >= (u64)100000000 * 10000000) { /* len: 16 to 17 */ + hgh = val / 100000000; /* (val / 100000000) */ + low = (u32)(val - hgh * 100000000); /* (val % 100000000) */ + one = (u32)(hgh / 100000000); /* (hgh / 100000000) */ + mid = (u32)(hgh - (u64)one * 100000000); /* (hgh % 100000000) */ + *buf = (u8)((u8)one + (u8)'0'); + buf += one > 0; + buf = write_u32_len_8(mid, buf); + buf = write_u32_len_8(low, buf); + return buf; + } else if (val >= (u64)100000000){ /* len: 9 to 15 */ + hgh = val / 100000000; /* (val / 100000000) */ + low = (u32)(val - hgh * 100000000); /* (val % 100000000) */ + buf = write_u32_len_1_to_8((u32)hgh, buf); + buf = write_u32_len_8(low, buf); + return buf; + } else { /* len: 1 to 8 */ + buf = write_u32_len_1_to_8((u32)val, buf); + return buf; + } +} + +/** + Write an unsigned integer with a length of 7 to 9 with trailing zero trimmed. + These digits are named as "abbccddee" here. + For example, input 123456000, output "123456". + */ +static_inline u8 *write_u32_len_7_to_9_trim(u32 val, u8 *buf) { + bool lz; + u32 tz, tz1, tz2; + + u32 abbcc = val / 10000; /* (abbccddee / 10000) */ + u32 ddee = val - abbcc * 10000; /* (abbccddee % 10000) */ + u32 abb = (u32)(((u64)abbcc * 167773) >> 24); /* (abbcc / 100) */ + u32 a = (abb * 41) >> 12; /* (abb / 100) */ + u32 bb = abb - a * 100; /* (abb % 100) */ + u32 cc = abbcc - abb * 100; /* (abbcc % 100) */ + + /* write abbcc */ + buf[0] = (u8)(a + '0'); + buf += a > 0; + lz = bb < 10 && a == 0; + byte_copy_2(buf + 0, digit_table + bb * 2 + lz); + buf -= lz; + byte_copy_2(buf + 2, digit_table + cc * 2); + + if (ddee) { + u32 dd = (ddee * 5243) >> 19; /* (ddee / 100) */ + u32 ee = ddee - dd * 100; /* (ddee % 100) */ + byte_copy_2(buf + 4, digit_table + dd * 2); + byte_copy_2(buf + 6, digit_table + ee * 2); + tz1 = dec_trailing_zero_table[dd]; + tz2 = dec_trailing_zero_table[ee]; + tz = ee ? tz2 : (tz1 + 2); + buf += 8 - tz; + return buf; + } else { + tz1 = dec_trailing_zero_table[bb]; + tz2 = dec_trailing_zero_table[cc]; + tz = cc ? tz2 : (tz1 + tz2); + buf += 4 - tz; + return buf; + } +} + +/** + Write an unsigned integer with a length of 16 or 17 with trailing zero trimmed. + These digits are named as "abbccddeeffgghhii" here. + For example, input 1234567890123000, output "1234567890123". + */ +static_inline u8 *write_u64_len_16_to_17_trim(u64 val, u8 *buf) { + u32 tz, tz1, tz2; + + u32 abbccddee = (u32)(val / 100000000); + u32 ffgghhii = (u32)(val - (u64)abbccddee * 100000000); + u32 abbcc = abbccddee / 10000; + u32 ddee = abbccddee - abbcc * 10000; + u32 abb = (u32)(((u64)abbcc * 167773) >> 24); /* (abbcc / 100) */ + u32 a = (abb * 41) >> 12; /* (abb / 100) */ + u32 bb = abb - a * 100; /* (abb % 100) */ + u32 cc = abbcc - abb * 100; /* (abbcc % 100) */ + buf[0] = (u8)(a + '0'); + buf += a > 0; + byte_copy_2(buf + 0, digit_table + bb * 2); + byte_copy_2(buf + 2, digit_table + cc * 2); + + if (ffgghhii) { + u32 dd = (ddee * 5243) >> 19; /* (ddee / 100) */ + u32 ee = ddee - dd * 100; /* (ddee % 100) */ + u32 ffgg = (u32)(((u64)ffgghhii * 109951163) >> 40); /* (val / 10000) */ + u32 hhii = ffgghhii - ffgg * 10000; /* (val % 10000) */ + u32 ff = (ffgg * 5243) >> 19; /* (aabb / 100) */ + u32 gg = ffgg - ff * 100; /* (aabb % 100) */ + byte_copy_2(buf + 4, digit_table + dd * 2); + byte_copy_2(buf + 6, digit_table + ee * 2); + byte_copy_2(buf + 8, digit_table + ff * 2); + byte_copy_2(buf + 10, digit_table + gg * 2); + if (hhii) { + u32 hh = (hhii * 5243) >> 19; /* (ccdd / 100) */ + u32 ii = hhii - hh * 100; /* (ccdd % 100) */ + byte_copy_2(buf + 12, digit_table + hh * 2); + byte_copy_2(buf + 14, digit_table + ii * 2); + tz1 = dec_trailing_zero_table[hh]; + tz2 = dec_trailing_zero_table[ii]; + tz = ii ? tz2 : (tz1 + 2); + return buf + 16 - tz; + } else { + tz1 = dec_trailing_zero_table[ff]; + tz2 = dec_trailing_zero_table[gg]; + tz = gg ? tz2 : (tz1 + 2); + return buf + 12 - tz; + } + } else { + if (ddee) { + u32 dd = (ddee * 5243) >> 19; /* (ddee / 100) */ + u32 ee = ddee - dd * 100; /* (ddee % 100) */ + byte_copy_2(buf + 4, digit_table + dd * 2); + byte_copy_2(buf + 6, digit_table + ee * 2); + tz1 = dec_trailing_zero_table[dd]; + tz2 = dec_trailing_zero_table[ee]; + tz = ee ? tz2 : (tz1 + 2); + return buf + 8 - tz; + } else { + tz1 = dec_trailing_zero_table[bb]; + tz2 = dec_trailing_zero_table[cc]; + tz = cc ? tz2 : (tz1 + tz2); + return buf + 4 - tz; + } + } +} + +/** Write exponent part in range `e-45` to `e38`. */ +static_inline u8 *write_f32_exp(i32 exp, u8 *buf) { + bool lz; + byte_copy_2(buf, "e-"); + buf += 2 - (exp >= 0); + exp = exp < 0 ? -exp : exp; + lz = exp < 10; + byte_copy_2(buf + 0, digit_table + (u32)exp * 2 + lz); + return buf + 2 - lz; +} + +/** Write exponent part in range `e-324` to `e308`. */ +static_inline u8 *write_f64_exp(i32 exp, u8 *buf) { + byte_copy_2(buf, "e-"); + buf += 2 - (exp >= 0); + exp = exp < 0 ? -exp : exp; + if (exp < 100) { + bool lz = exp < 10; + byte_copy_2(buf + 0, digit_table + (u32)exp * 2 + lz); + return buf + 2 - lz; + } else { + u32 hi = ((u32)exp * 656) >> 16; /* exp / 100 */ + u32 lo = (u32)exp - hi * 100; /* exp % 100 */ + buf[0] = (u8)((u8)hi + (u8)'0'); + byte_copy_2(buf + 1, digit_table + lo * 2); + return buf + 3; + } +} + +/** Magic number for fast `divide by power of 10`. */ +typedef struct { + u64 p10, mul; + u32 shr1, shr2; +} div_pow10_magic; + +/** Generated with llvm, see https://github.com/llvm/llvm-project/ + blob/main/llvm/lib/Support/DivisionByConstantInfo.cpp */ +static const div_pow10_magic div_pow10_table[] = { + { U64(0x00000000, 0x00000001), U64(0x00000000, 0x00000000), 0, 0 }, + { U64(0x00000000, 0x0000000A), U64(0xCCCCCCCC, 0xCCCCCCCD), 0, 3 }, + { U64(0x00000000, 0x00000064), U64(0x28F5C28F, 0x5C28F5C3), 2, 2 }, + { U64(0x00000000, 0x000003E8), U64(0x20C49BA5, 0xE353F7CF), 3, 4 }, + { U64(0x00000000, 0x00002710), U64(0x346DC5D6, 0x3886594B), 0, 11 }, + { U64(0x00000000, 0x000186A0), U64(0x0A7C5AC4, 0x71B47843), 5, 7 }, + { U64(0x00000000, 0x000F4240), U64(0x431BDE82, 0xD7B634DB), 0, 18 }, + { U64(0x00000000, 0x00989680), U64(0xD6BF94D5, 0xE57A42BD), 0, 23 }, + { U64(0x00000000, 0x05F5E100), U64(0xABCC7711, 0x8461CEFD), 0, 26 }, + { U64(0x00000000, 0x3B9ACA00), U64(0x0044B82F, 0xA09B5A53), 9, 11 }, + { U64(0x00000002, 0x540BE400), U64(0xDBE6FECE, 0xBDEDD5BF), 0, 33 }, + { U64(0x00000017, 0x4876E800), U64(0xAFEBFF0B, 0xCB24AAFF), 0, 36 }, + { U64(0x000000E8, 0xD4A51000), U64(0x232F3302, 0x5BD42233), 0, 37 }, + { U64(0x00000918, 0x4E72A000), U64(0x384B84D0, 0x92ED0385), 0, 41 }, + { U64(0x00005AF3, 0x107A4000), U64(0x0B424DC3, 0x5095CD81), 0, 42 }, + { U64(0x00038D7E, 0xA4C68000), U64(0x00024075, 0xF3DCEAC3), 15, 20 }, + { U64(0x002386F2, 0x6FC10000), U64(0x39A5652F, 0xB1137857), 0, 51 }, + { U64(0x01634578, 0x5D8A0000), U64(0x00005C3B, 0xD5191B53), 17, 22 }, + { U64(0x0DE0B6B3, 0xA7640000), U64(0x000049C9, 0x7747490F), 18, 24 }, + { U64(0x8AC72304, 0x89E80000), U64(0x760F253E, 0xDB4AB0d3), 0, 62 }, +}; + +/** Divide a number by power of 10. */ +static_inline void div_pow10(u64 num, u32 exp, u64 *div, u64 *mod, u64 *p10) { + u64 hi, lo; + div_pow10_magic m = div_pow10_table[exp]; + u128_mul(num >> m.shr1, m.mul, &hi, &lo); + *div = hi >> m.shr2; + *mod = num - (*div * m.p10); + *p10 = m.p10; +} + +/** Multiplies 64-bit integer and returns highest 64-bit rounded value. */ +static_inline u32 u64_round_to_odd(u64 u, u32 cp) { + u64 hi, lo; + u32 y_hi, y_lo; + u128_mul(cp, u, &hi, &lo); + y_hi = (u32)hi; + y_lo = (u32)(lo >> 32); + return y_hi | (y_lo > 1); +} + +/** Multiplies 128-bit integer and returns highest 64-bit rounded value. */ +static_inline u64 u128_round_to_odd(u64 hi, u64 lo, u64 cp) { + u64 x_hi, x_lo, y_hi, y_lo; + u128_mul(cp, lo, &x_hi, &x_lo); + u128_mul_add(cp, hi, x_hi, &y_hi, &y_lo); + return y_hi | (y_lo > 1); +} + +/** Convert f32 from binary to decimal (shortest but may have trailing zeros). + The input should not be 0, inf or nan. */ +static_inline void f32_bin_to_dec(u32 sig_raw, u32 exp_raw, + u32 sig_bin, i32 exp_bin, + u32 *sig_dec, i32 *exp_dec) { + + bool is_even, irregular, round_up, trim; + bool u0_inside, u1_inside, w0_inside, w1_inside; + u64 p10_hi, p10_lo, hi, lo; + u32 s, sp, cb, cbl, cbr, vb, vbl, vbr, upper, lower, mid; + i32 k, h; + + /* Fast path, see f64_bin_to_dec(). */ + while (likely(sig_raw)) { + u32 mod, dec, add_1, add_10, s_hi, s_lo; + u32 c, half_ulp, t0, t1; + + /* k = floor(exp_bin * log10(2)); */ + /* h = exp_bin + floor(log2(10) * -k); (h = 0/1/2/3) */ + k = (i32)(exp_bin * 315653) >> 20; + h = exp_bin + ((-k * 217707) >> 16); + pow10_table_get_sig(-k, &p10_hi, &p10_lo); + + /* sig_bin << (1/2/3/4) */ + cb = sig_bin << (h + 1); + u128_mul(cb, p10_hi, &hi, &lo); + s_hi = (u32)(hi); + s_lo = (u32)(lo >> 32); + mod = s_hi % 10; + dec = s_hi - mod; + + /* right shift 4 to fit in u32 */ + c = (mod << (32 - 4)) | (s_lo >> 4); + half_ulp = (u32)(p10_hi >> (32 + 4 - h)); + + /* check w1, u0, w0 range */ + w1_inside = (s_lo >= ((u32)1 << 31)); + if (unlikely(s_lo == ((u32)1 << 31))) break; + u0_inside = (half_ulp >= c); + if (unlikely(half_ulp == c)) break; + t0 = (u32)10 << (32 - 4); + t1 = c + half_ulp; + w0_inside = (t1 >= t0); + if (unlikely(t0 - t1 <= (u32)1)) break; + + trim = (u0_inside | w0_inside); + add_10 = (w0_inside ? 10 : 0); + add_1 = mod + w1_inside; + *sig_dec = dec + (trim ? add_10 : add_1); + *exp_dec = k; + return; + } + + /* Schubfach algorithm, see f64_bin_to_dec(). */ + irregular = (sig_raw == 0 && exp_raw > 1); + is_even = !(sig_bin & 1); + cbl = 4 * sig_bin - 2 + irregular; + cb = 4 * sig_bin; + cbr = 4 * sig_bin + 2; + + /* k = floor(exp_bin * log10(2) + (irregular ? log10(3.0 / 4.0) : 0)); */ + /* h = exp_bin + floor(log2(10) * -k) + 1; (h = 1/2/3/4) */ + k = (i32)(exp_bin * 315653 - (irregular ? 131237 : 0)) >> 20; + h = exp_bin + ((-k * 217707) >> 16) + 1; + pow10_table_get_sig(-k, &p10_hi, &p10_lo); + p10_hi += 1; + + vbl = u64_round_to_odd(p10_hi, cbl << h); + vb = u64_round_to_odd(p10_hi, cb << h); + vbr = u64_round_to_odd(p10_hi, cbr << h); + lower = vbl + !is_even; + upper = vbr - !is_even; + + s = vb / 4; + if (s >= 10) { + sp = s / 10; + u0_inside = (lower <= 40 * sp); + w0_inside = (upper >= 40 * sp + 40); + if (u0_inside != w0_inside) { + *sig_dec = sp * 10 + (w0_inside ? 10 : 0); + *exp_dec = k; + return; + } + } + u1_inside = (lower <= 4 * s); + w1_inside = (upper >= 4 * s + 4); + mid = 4 * s + 2; + round_up = (vb > mid) || (vb == mid && (s & 1) != 0); + *sig_dec = s + ((u1_inside != w1_inside) ? w1_inside : round_up); + *exp_dec = k; +} + +/** Convert f64 from binary to decimal (shortest but may have trailing zeros). + The input should not be 0, inf or nan. */ +static_inline void f64_bin_to_dec(u64 sig_raw, u32 exp_raw, + u64 sig_bin, i32 exp_bin, + u64 *sig_dec, i32 *exp_dec) { + + bool is_even, irregular, round_up, trim; + bool u0_inside, u1_inside, w0_inside, w1_inside; + u64 s, sp, cb, cbl, cbr, vb, vbl, vbr, p10_hi, p10_lo, upper, lower, mid; + i32 k, h; + + /* + Fast path: + For regular spacing significand 'c', there are 4 candidates: + + u0 u1 c w1 w0 + ----|----|----|----|----|-*--|----|----|----|----|----|----|----|---- + 9 0 1 2 3 4 5 6 7 8 9 0 1 + |___________________|___________________| + 1ulp + + The `1ulp` is in the range [1.0, 10.0). + If (c - 0.5ulp < u0), trim the last digit and round down. + If (c + 0.5ulp > w0), trim the last digit and round up. + If (c - 0.5ulp < u1), round down. + If (c + 0.5ulp > w1), round up. + */ + while (likely(sig_raw)) { + u64 mod, dec, add_1, add_10, s_hi, s_lo; + u64 c, half_ulp, t0, t1; + + /* k = floor(exp_bin * log10(2)); */ + /* h = exp_bin + floor(log2(10) * -k); (h = 0/1/2/3) */ + k = (i32)(exp_bin * 315653) >> 20; + h = exp_bin + ((-k * 217707) >> 16); + pow10_table_get_sig(-k, &p10_hi, &p10_lo); + + /* sig_bin << (1/2/3/4) */ + cb = sig_bin << (h + 1); + u128_mul(cb, p10_lo, &s_hi, &s_lo); + u128_mul_add(cb, p10_hi, s_hi, &s_hi, &s_lo); + mod = s_hi % 10; + dec = s_hi - mod; + + /* right shift 4 to fit in u64 */ + c = (mod << (64 - 4)) | (s_lo >> 4); + half_ulp = p10_hi >> (4 - h); + + /* check w1, u0, w0 range */ + w1_inside = (s_lo >= ((u64)1 << 63)); + if (unlikely(s_lo == ((u64)1 << 63))) break; + u0_inside = (half_ulp >= c); + if (unlikely(half_ulp == c)) break; + t0 = ((u64)10 << (64 - 4)); + t1 = c + half_ulp; + w0_inside = (t1 >= t0); + if (unlikely(t0 - t1 <= (u64)1)) break; + + trim = (u0_inside | w0_inside); + add_10 = (w0_inside ? 10 : 0); + add_1 = mod + w1_inside; + *sig_dec = dec + (trim ? add_10 : add_1); + *exp_dec = k; + return; + } + + /* + Schubfach algorithm: + Raffaello Giulietti, The Schubfach way to render doubles, 2022. + https://drive.google.com/file/d/1gp5xv4CAa78SVgCeWfGqqI4FfYYYuNFb (Paper) + https://github.com/openjdk/jdk/pull/3402 (Java implementation) + https://github.com/abolz/Drachennest (C++ implementation) + */ + irregular = (sig_raw == 0 && exp_raw > 1); + is_even = !(sig_bin & 1); + cbl = 4 * sig_bin - 2 + irregular; + cb = 4 * sig_bin; + cbr = 4 * sig_bin + 2; + + /* k = floor(exp_bin * log10(2) + (irregular ? log10(3.0 / 4.0) : 0)); */ + /* h = exp_bin + floor(log2(10) * -k) + 1; (h = 1/2/3/4) */ + k = (i32)(exp_bin * 315653 - (irregular ? 131237 : 0)) >> 20; + h = exp_bin + ((-k * 217707) >> 16) + 1; + pow10_table_get_sig(-k, &p10_hi, &p10_lo); + p10_lo += 1; + + vbl = u128_round_to_odd(p10_hi, p10_lo, cbl << h); + vb = u128_round_to_odd(p10_hi, p10_lo, cb << h); + vbr = u128_round_to_odd(p10_hi, p10_lo, cbr << h); + lower = vbl + !is_even; + upper = vbr - !is_even; + + s = vb / 4; + if (s >= 10) { + sp = s / 10; + u0_inside = (lower <= 40 * sp); + w0_inside = (upper >= 40 * sp + 40); + if (u0_inside != w0_inside) { + *sig_dec = sp * 10 + (w0_inside ? 10 : 0); + *exp_dec = k; + return; + } + } + u1_inside = (lower <= 4 * s); + w1_inside = (upper >= 4 * s + 4); + mid = 4 * s + 2; + round_up = (vb > mid) || (vb == mid && (s & 1) != 0); + *sig_dec = s + ((u1_inside != w1_inside) ? w1_inside : round_up); + *exp_dec = k; +} + +/** Convert f64 from binary to decimal (fast but not the shortest). + The input should not be 0, inf, nan. */ +static_inline void f64_bin_to_dec_fast(u64 sig_raw, u32 exp_raw, + u64 sig_bin, i32 exp_bin, + u64 *sig_dec, i32 *exp_dec, + bool *round_up) { + u64 cb, p10_hi, p10_lo, s_hi, s_lo; + i32 k, h; + bool irregular, u; + + irregular = (sig_raw == 0 && exp_raw > 1); + + /* k = floor(exp_bin * log10(2) + (irregular ? log10(3.0 / 4.0) : 0)); */ + /* h = exp_bin + floor(log2(10) * -k) + 1; (h = 1/2/3/4) */ + k = (i32)(exp_bin * 315653 - (irregular ? 131237 : 0)) >> 20; + h = exp_bin + ((-k * 217707) >> 16); + pow10_table_get_sig(-k, &p10_hi, &p10_lo); + + /* sig_bin << (1/2/3/4) */ + cb = sig_bin << (h + 1); + u128_mul(cb, p10_lo, &s_hi, &s_lo); + u128_mul_add(cb, p10_hi, s_hi, &s_hi, &s_lo); + + /* round up */ + u = s_lo >= (irregular ? U64(0x55555555, 0x55555555) : ((u64)1 << 63)); + + *sig_dec = s_hi + u; + *exp_dec = k; + *round_up = u; + return; +} + +/** Write inf/nan if allowed. */ +static_inline u8 *write_inf_or_nan(u8 *buf, yyjson_write_flag flg, + u64 sig_raw, bool sign) { + if (has_flg(INF_AND_NAN_AS_NULL)) { + byte_copy_4(buf, "null"); + return buf + 4; + } + if (has_allow(INF_AND_NAN)) { + if (sig_raw == 0) { + buf[0] = '-'; + buf += sign; + byte_copy_8(buf, "Infinity"); + return buf + 8; + } else { + byte_copy_4(buf, "NaN"); + return buf + 3; + } + } + return NULL; +} + +/** + Write a float number (requires 40 bytes buffer). + We follow the ECMAScript specification for printing floating-point numbers, + similar to `Number.prototype.toString()`, but with the following changes: + 1. Keep the negative sign of `-0.0` to preserve input information. + 2. Keep decimal point to indicate the number is floating point. + 3. Remove positive sign in the exponent part. + */ +static_noinline u8 *write_f32_raw(u8 *buf, u64 raw_f64, + yyjson_write_flag flg) { + u32 sig_bin, sig_dec, sig_raw; + i32 exp_bin, exp_dec, sig_len, dot_ofs; + u32 exp_raw, raw; + u8 *end; + bool sign; + + /* cast double to float */ + raw = f32_to_bits(f64_to_f32(f64_from_bits(raw_f64))); + + /* decode raw bytes from IEEE-754 double format. */ + sign = (bool)(raw >> (F32_BITS - 1)); + sig_raw = raw & F32_SIG_MASK; + exp_raw = (raw & F32_EXP_MASK) >> F32_SIG_BITS; + + /* return inf or nan */ + if (unlikely(exp_raw == ((u32)1 << F32_EXP_BITS) - 1)) { + return write_inf_or_nan(buf, flg, sig_raw, sign); + } + + /* add sign for all finite number */ + buf[0] = '-'; + buf += sign; + + /* return zero */ + if ((raw << 1) == 0) { + byte_copy_4(buf, "0.0"); + return buf + 3; + } + + if (likely(exp_raw != 0)) { + /* normal number */ + sig_bin = sig_raw | ((u32)1 << F32_SIG_BITS); + exp_bin = (i32)exp_raw - F32_EXP_BIAS - F32_SIG_BITS; + + /* fast path for small integer number without fraction */ + if ((-F32_SIG_BITS <= exp_bin && exp_bin <= 0) && + (u64_tz_bits(sig_bin) >= (u32)-exp_bin)) { + sig_dec = sig_bin >> -exp_bin; /* range: [1, 0xFFFFFF] */ + buf = write_u32_len_1_to_8(sig_dec, buf); + byte_copy_2(buf, ".0"); + return buf + 2; + } + + /* binary to decimal */ + f32_bin_to_dec(sig_raw, exp_raw, sig_bin, exp_bin, &sig_dec, &exp_dec); + + /* the sig length is 7 or 9 */ + sig_len = 7 + (sig_dec >= (u32)10000000) + (sig_dec >= (u32)100000000); + + /* the decimal point offset relative to the first digit */ + dot_ofs = sig_len + exp_dec; + + if (-6 < dot_ofs && dot_ofs <= 21) { + i32 num_sep_pos, dot_set_pos, pre_ofs; + u8 *num_hdr, *num_end, *num_sep, *dot_end; + bool no_pre_zero; + + /* fill zeros */ + memset(buf, '0', 32); + + /* not prefixed with zero, e.g. 1.234, 1234.0 */ + no_pre_zero = (dot_ofs > 0); + + /* write the number as digits */ + pre_ofs = no_pre_zero ? 0 : (2 - dot_ofs); + num_hdr = buf + pre_ofs; + num_end = write_u32_len_7_to_9_trim(sig_dec, num_hdr); + + /* seperate these digits to leave a space for dot */ + num_sep_pos = no_pre_zero ? dot_ofs : 0; + num_sep = num_hdr + num_sep_pos; + byte_move_8(num_sep + no_pre_zero, num_sep); + num_end += no_pre_zero; + + /* write the dot */ + dot_set_pos = yyjson_max(dot_ofs, 1); + buf[dot_set_pos] = '.'; + + /* return the ending */ + dot_end = buf + dot_ofs + 2; + return yyjson_max(dot_end, num_end); + + } else { + /* write with scientific notation, e.g. 1.234e56 */ + end = write_u32_len_7_to_9_trim(sig_dec, buf + 1); + end -= (end == buf + 2); /* remove '.0', e.g. 2.0e34 -> 2e34 */ + exp_dec += sig_len - 1; + buf[0] = buf[1]; + buf[1] = '.'; + return write_f32_exp(exp_dec, end); + } + + } else { + /* subnormal number */ + sig_bin = sig_raw; + exp_bin = 1 - F32_EXP_BIAS - F32_SIG_BITS; + + /* binary to decimal */ + f32_bin_to_dec(sig_raw, exp_raw, sig_bin, exp_bin, &sig_dec, &exp_dec); + + /* write significand part */ + end = write_u32_len_1_to_8(sig_dec, buf + 1); + buf[0] = buf[1]; + buf[1] = '.'; + exp_dec += (i32)(end - buf) - 2; + + /* trim trailing zeros */ + end -= *(end - 1) == '0'; /* branchless for last zero */ + end -= *(end - 1) == '0'; /* branchless for second last zero */ + while (*(end - 1) == '0') end--; /* for unlikely more zeros */ + end -= *(end - 1) == '.'; /* remove dot, e.g. 2.e-321 -> 2e-321 */ + + /* write exponent part */ + return write_f32_exp(exp_dec, end); + } +} + +/** + Write a double number (requires 40 bytes buffer). + We follow the ECMAScript specification for printing floating-point numbers, + similar to `Number.prototype.toString()`, but with the following changes: + 1. Keep the negative sign of `-0.0` to preserve input information. + 2. Keep decimal point to indicate the number is floating point. + 3. Remove positive sign in the exponent part. + */ +static_noinline u8 *write_f64_raw(u8 *buf, u64 raw, yyjson_write_flag flg) { + u64 sig_bin, sig_dec, sig_raw; + i32 exp_bin, exp_dec, sig_len, dot_ofs; + u32 exp_raw; + u8 *end; + bool sign; + + /* decode raw bytes from IEEE-754 double format. */ + sign = (bool)(raw >> (F64_BITS - 1)); + sig_raw = raw & F64_SIG_MASK; + exp_raw = (u32)((raw & F64_EXP_MASK) >> F64_SIG_BITS); + + /* return inf or nan */ + if (unlikely(exp_raw == ((u32)1 << F64_EXP_BITS) - 1)) { + return write_inf_or_nan(buf, flg, sig_raw, sign); + } + + /* add sign for all finite number */ + buf[0] = '-'; + buf += sign; + + /* return zero */ + if ((raw << 1) == 0) { + byte_copy_4(buf, "0.0"); + return buf + 3; + } + + if (likely(exp_raw != 0)) { + /* normal number */ + sig_bin = sig_raw | ((u64)1 << F64_SIG_BITS); + exp_bin = (i32)exp_raw - F64_EXP_BIAS - F64_SIG_BITS; + + /* fast path for small integer number without fraction */ + if ((-F64_SIG_BITS <= exp_bin && exp_bin <= 0) && + (u64_tz_bits(sig_bin) >= (u32)-exp_bin)) { + sig_dec = sig_bin >> -exp_bin; /* range: [1, 0x1FFFFFFFFFFFFF] */ + buf = write_u64_len_1_to_16(sig_dec, buf); + byte_copy_2(buf, ".0"); + return buf + 2; + } + + /* binary to decimal */ + f64_bin_to_dec(sig_raw, exp_raw, sig_bin, exp_bin, &sig_dec, &exp_dec); + + /* the sig length is 16 or 17 */ + sig_len = 16 + (sig_dec >= (u64)100000000 * 100000000); + + /* the decimal point offset relative to the first digit */ + dot_ofs = sig_len + exp_dec; + + if (-6 < dot_ofs && dot_ofs <= 21) { + i32 num_sep_pos, dot_set_pos, pre_ofs; + u8 *num_hdr, *num_end, *num_sep, *dot_end; + bool no_pre_zero; + + /* fill zeros */ + memset(buf, '0', 32); + + /* not prefixed with zero, e.g. 1.234, 1234.0 */ + no_pre_zero = (dot_ofs > 0); + + /* write the number as digits */ + pre_ofs = no_pre_zero ? 0 : (2 - dot_ofs); + num_hdr = buf + pre_ofs; + num_end = write_u64_len_16_to_17_trim(sig_dec, num_hdr); + + /* seperate these digits to leave a space for dot */ + num_sep_pos = no_pre_zero ? dot_ofs : 0; + num_sep = num_hdr + num_sep_pos; + byte_move_16(num_sep + no_pre_zero, num_sep); + num_end += no_pre_zero; + + /* write the dot */ + dot_set_pos = yyjson_max(dot_ofs, 1); + buf[dot_set_pos] = '.'; + + /* return the ending */ + dot_end = buf + dot_ofs + 2; + return yyjson_max(dot_end, num_end); + + } else { + /* write with scientific notation, e.g. 1.234e56 */ + end = write_u64_len_16_to_17_trim(sig_dec, buf + 1); + end -= (end == buf + 2); /* remove '.0', e.g. 2.0e34 -> 2e34 */ + exp_dec += sig_len - 1; + buf[0] = buf[1]; + buf[1] = '.'; + return write_f64_exp(exp_dec, end); + } + + } else { + /* subnormal number */ + sig_bin = sig_raw; + exp_bin = 1 - F64_EXP_BIAS - F64_SIG_BITS; + + /* binary to decimal */ + f64_bin_to_dec(sig_raw, exp_raw, sig_bin, exp_bin, &sig_dec, &exp_dec); + + /* write significand part */ + end = write_u64_len_1_to_17(sig_dec, buf + 1); + buf[0] = buf[1]; + buf[1] = '.'; + exp_dec += (i32)(end - buf) - 2; + + /* trim trailing zeros */ + end -= *(end - 1) == '0'; /* branchless for last zero */ + end -= *(end - 1) == '0'; /* branchless for second last zero */ + while (*(end - 1) == '0') end--; /* for unlikely more zeros */ + end -= *(end - 1) == '.'; /* remove dot, e.g. 2.e-321 -> 2e-321 */ + + /* write exponent part */ + return write_f64_exp(exp_dec, end); + } +} + +/** + Write a double number using fixed-point notation (requires 40 bytes buffer). + + We follow the ECMAScript specification for printing floating-point numbers, + similar to `Number.prototype.toFixed(prec)`, but with the following changes: + 1. Keep the negative sign of `-0.0` to preserve input information. + 2. Keep decimal point to indicate the number is floating point. + 3. Remove positive sign in the exponent part. + 4. Remove trailing zeros and reduce unnecessary precision. + */ +static_noinline u8 *write_f64_raw_fixed(u8 *buf, u64 raw, yyjson_write_flag flg, + u32 prec) { + u64 sig_bin, sig_dec, sig_raw; + i32 exp_bin, exp_dec, sig_len, dot_ofs; + u32 exp_raw; + u8 *end; + bool sign; + + /* decode raw bytes from IEEE-754 double format. */ + sign = (bool)(raw >> (F64_BITS - 1)); + sig_raw = raw & F64_SIG_MASK; + exp_raw = (u32)((raw & F64_EXP_MASK) >> F64_SIG_BITS); + + /* return inf or nan */ + if (unlikely(exp_raw == ((u32)1 << F64_EXP_BITS) - 1)) { + return write_inf_or_nan(buf, flg, sig_raw, sign); + } + + /* add sign for all finite number */ + buf[0] = '-'; + buf += sign; + + /* return zero */ + if ((raw << 1) == 0) { + byte_copy_4(buf, "0.0"); + return buf + 3; + } + + if (likely(exp_raw != 0)) { + /* normal number */ + sig_bin = sig_raw | ((u64)1 << F64_SIG_BITS); + exp_bin = (i32)exp_raw - F64_EXP_BIAS - F64_SIG_BITS; + + /* fast path for small integer number without fraction */ + if ((-F64_SIG_BITS <= exp_bin && exp_bin <= 0) && + (u64_tz_bits(sig_bin) >= (u32)-exp_bin)) { + sig_dec = sig_bin >> -exp_bin; /* range: [1, 0x1FFFFFFFFFFFFF] */ + buf = write_u64_len_1_to_16(sig_dec, buf); + byte_copy_2(buf, ".0"); + return buf + 2; + } + + /* only `fabs(num) < 1e21` are processed here. */ + if ((raw << 1) < (U64(0x444B1AE4, 0xD6E2EF50) << 1)) { + i32 num_sep_pos, dot_set_pos, pre_ofs; + u8 *num_hdr, *num_end, *num_sep; + bool round_up, no_pre_zero; + + /* binary to decimal */ + f64_bin_to_dec_fast(sig_raw, exp_raw, sig_bin, exp_bin, + &sig_dec, &exp_dec, &round_up); + + /* the sig length is 16 or 17 */ + sig_len = 16 + (sig_dec >= (u64)100000000 * 100000000); + + /* limit the length of digits after the decimal point */ + if (exp_dec < -1) { + i32 sig_len_cut = -exp_dec - (i32)prec; + if (sig_len_cut > sig_len) { + byte_copy_4(buf, "0.0"); + return buf + 3; + } + if (sig_len_cut > 0) { + u64 div, mod, p10; + + /* remove round up */ + sig_dec -= round_up; + sig_len = 16 + (sig_dec >= (u64)100000000 * 100000000); + + /* cut off some digits */ + div_pow10(sig_dec, (u32)sig_len_cut, &div, &mod, &p10); + + /* add round up */ + sig_dec = div + (mod >= p10 / 2); + + /* update exp and sig length */ + exp_dec += sig_len_cut; + sig_len -= sig_len_cut; + sig_len += (sig_len >= 0) && + (sig_dec >= div_pow10_table[sig_len].p10); + } + if (sig_len <= 0) { + byte_copy_4(buf, "0.0"); + return buf + 3; + } + } + + /* fill zeros */ + memset(buf, '0', 32); + + /* the decimal point offset relative to the first digit */ + dot_ofs = sig_len + exp_dec; + + /* not prefixed with zero, e.g. 1.234, 1234.0 */ + no_pre_zero = (dot_ofs > 0); + + /* write the number as digits */ + pre_ofs = no_pre_zero ? 0 : (1 - dot_ofs); + num_hdr = buf + pre_ofs; + num_end = write_u64_len_1_to_17(sig_dec, num_hdr); + + /* seperate these digits to leave a space for dot */ + num_sep_pos = no_pre_zero ? dot_ofs : -dot_ofs; + num_sep = buf + num_sep_pos; + byte_move_16(num_sep + 1, num_sep); + num_end += (exp_dec < 0); + + /* write the dot */ + dot_set_pos = yyjson_max(dot_ofs, 1); + buf[dot_set_pos] = '.'; + + /* remove trailing zeros */ + buf += dot_set_pos + 2; + buf = yyjson_max(buf, num_end); + buf -= *(buf - 1) == '0'; /* branchless for last zero */ + buf -= *(buf - 1) == '0'; /* branchless for second last zero */ + while (*(buf - 1) == '0') buf--; /* for unlikely more zeros */ + buf += *(buf - 1) == '.'; /* keep a zero after dot */ + return buf; + + } else { + /* binary to decimal */ + f64_bin_to_dec(sig_raw, exp_raw, sig_bin, exp_bin, + &sig_dec, &exp_dec); + + /* the sig length is 16 or 17 */ + sig_len = 16 + (sig_dec >= (u64)100000000 * 100000000); + + /* write with scientific notation, e.g. 1.234e56 */ + end = write_u64_len_16_to_17_trim(sig_dec, buf + 1); + end -= (end == buf + 2); /* remove '.0', e.g. 2.0e34 -> 2e34 */ + exp_dec += sig_len - 1; + buf[0] = buf[1]; + buf[1] = '.'; + return write_f64_exp(exp_dec, end); + } + } else { + /* subnormal number */ + byte_copy_4(buf, "0.0"); + return buf + 3; + } +} + +#else /* FP_WRITER */ + +#if YYJSON_MSC_VER >= 1400 +#define snprintf_num(buf, len, fmt, dig, val) \ + sprintf_s((char *)buf, len, fmt, dig, val) +#elif defined(snprintf) || (YYJSON_STDC_VER >= 199901L) +#define snprintf_num(buf, len, fmt, dig, val) \ + snprintf((char *)buf, len, fmt, dig, val) +#else +#define snprintf_num(buf, len, fmt, dig, val) \ + sprintf((char *)buf, fmt, dig, val) +#endif + +static_noinline u8 *write_fp_reformat(u8 *buf, int len, + yyjson_write_flag flg, bool fixed) { + u8 *cur = buf; + if (unlikely(len < 1)) return NULL; + cur += (*cur == '-'); + if (unlikely(!char_is_digit(*cur))) { + /* nan, inf, or bad output */ + if (has_flg(INF_AND_NAN_AS_NULL)) { + byte_copy_4(buf, "null"); + return buf + 4; + } else if (has_allow(INF_AND_NAN)) { + if (*cur == 'i') { + byte_copy_8(cur, "Infinity"); + return cur + 8; + } else if (*cur == 'n') { + byte_copy_4(buf, "NaN"); + return buf + 3; + } + } + return NULL; + } else { + /* finite number */ + u8 *end = buf + len, *dot = NULL, *exp = NULL; + + /* + The snprintf() function is locale-dependent. For currently known + locales, (en, zh, ja, ko, am, he, hi) use '.' as the decimal point, + while other locales use ',' as the decimal point. we need to replace + ',' with '.' to avoid the locale setting. + */ + for (; cur < end; cur++) { + switch (*cur) { + case ',': *cur = '.'; /* fallthrough */ + case '.': dot = cur; break; + case 'e': exp = cur; break; + default: break; + } + } + if (fixed) { + /* remove trailing zeros */ + while (*(end - 1) == '0') end--; + end += *(end - 1) == '.'; + } else { + if (!dot && !exp) { + /* add decimal point, e.g. 123 -> 123.0 */ + byte_copy_2(end, ".0"); + end += 2; + } else if (exp) { + cur = exp + 1; + /* remove positive sign in the exponent part */ + if (*cur == '+') { + memmove(cur, cur + 1, (usize)(end - cur - 1)); + end--; + } + cur += (*cur == '-'); + /* remove leading zeros in the exponent part */ + if (*cur == '0') { + u8 *hdr = cur++; + while (*cur == '0') cur++; + memmove(hdr, cur, (usize)(end - cur)); + end -= (usize)(cur - hdr); + } + } + } + return end; + } +} + +/** Write a double number (requires 40 bytes buffer). */ +static_noinline u8 *write_f64_raw(u8 *buf, u64 raw, yyjson_write_flag flg) { +#if defined(DBL_DECIMAL_DIG) && DBL_DECIMAL_DIG < F64_DEC_DIG + int dig = DBL_DECIMAL_DIG; +#else + int dig = F64_DEC_DIG; +#endif + f64 val = f64_from_bits(raw); + int len = snprintf_num(buf, FP_BUF_LEN, "%.*g", dig, val); + return write_fp_reformat(buf, len, flg, false); +} + +/** Write a double number (requires 40 bytes buffer). */ +static_noinline u8 *write_f32_raw(u8 *buf, u64 raw, yyjson_write_flag flg) { +#if defined(FLT_DECIMAL_DIG) && FLT_DECIMAL_DIG < F32_DEC_DIG + int dig = FLT_DECIMAL_DIG; +#else + int dig = F32_DEC_DIG; +#endif + f64 val = (f64)f64_to_f32(f64_from_bits(raw)); + int len = snprintf_num(buf, FP_BUF_LEN, "%.*g", dig, val); + return write_fp_reformat(buf, len, flg, false); +} + +/** Write a double number (requires 40 bytes buffer). */ +static_noinline u8 *write_f64_raw_fixed(u8 *buf, u64 raw, + yyjson_write_flag flg, u32 prec) { + f64 val = (f64)f64_from_bits(raw); + if (-1e21 < val && val < 1e21) { + int len = snprintf_num(buf, FP_BUF_LEN, "%.*f", (int)prec, val); + return write_fp_reformat(buf, len, flg, true); + } else { + return write_f64_raw(buf, raw, flg); + } +} + +#endif /* FP_WRITER */ + +/** Write a JSON number (requires 40 bytes buffer). */ +static_inline u8 *write_num(u8 *cur, yyjson_val *val, yyjson_write_flag flg) { + if (!(val->tag & YYJSON_SUBTYPE_REAL)) { + u64 pos = val->uni.u64; + u64 neg = ~pos + 1; + usize sign = ((val->tag & YYJSON_SUBTYPE_SINT) > 0) & ((i64)pos < 0); + *cur = '-'; + return write_u64(sign ? neg : pos, cur + sign); + } else { + u64 raw = val->uni.u64; + u32 val_fmt = (u32)(val->tag >> 32); + u32 all_fmt = flg; + u32 fmt = val_fmt | all_fmt; + if (likely(!(fmt >> (32 - YYJSON_WRITE_FP_FLAG_BITS)))) { + /* double to shortest */ + return write_f64_raw(cur, raw, flg); + } else if (fmt >> (32 - YYJSON_WRITE_FP_PREC_BITS)) { + /* double to fixed */ + u32 val_prec = val_fmt >> (32 - YYJSON_WRITE_FP_PREC_BITS); + u32 all_prec = all_fmt >> (32 - YYJSON_WRITE_FP_PREC_BITS); + u32 prec = val_prec ? val_prec : all_prec; + return write_f64_raw_fixed(cur, raw, flg, prec); + } else { + if (fmt & YYJSON_WRITE_FP_TO_FLOAT) { + /* float to shortest */ + return write_f32_raw(cur, raw, flg); + } else { + /* double to shortest */ + return write_f64_raw(cur, raw, flg); + } + } + } +} + +char *yyjson_write_number(const yyjson_val *val, char *buf) { + if (unlikely(!val || !buf)) return NULL; + switch (val->tag & YYJSON_TAG_MASK) { + case YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT: { + buf = (char *)write_u64(val->uni.u64, (u8 *)buf); + *buf = '\0'; + return buf; + } + case YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT: { + u64 pos = val->uni.u64; + u64 neg = ~pos + 1; + usize sign = ((i64)pos < 0); + *buf = '-'; + buf = (char *)write_u64(sign ? neg : pos, (u8 *)buf + sign); + *buf = '\0'; + return buf; + } + case YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL: { + u64 raw = val->uni.u64; + u32 fmt = (u32)(val->tag >> 32); + u32 flg = YYJSON_WRITE_ALLOW_INF_AND_NAN; + if (likely(!(fmt >> (32 - YYJSON_WRITE_FP_FLAG_BITS)))) { + buf = (char *)write_f64_raw((u8 *)buf, raw, flg); + } else if (fmt >> (32 - YYJSON_WRITE_FP_PREC_BITS)) { + u32 prec = fmt >> (32 - YYJSON_WRITE_FP_PREC_BITS); + buf = (char *)write_f64_raw_fixed((u8 *)buf, raw, flg, prec); + } else { + if (fmt & YYJSON_WRITE_FP_TO_FLOAT) { + buf = (char *)write_f32_raw((u8 *)buf, raw, flg); + } else { + buf = (char *)write_f64_raw((u8 *)buf, raw, flg); + } + } + if (buf) *buf = '\0'; + return buf; + } + default: return NULL; + } +} + + + +/*============================================================================== + * MARK: - String Writer (Private) + *============================================================================*/ + +/** Character encode type, if (type > CHAR_ENC_ERR_1) bytes = type / 2; */ +typedef u8 char_enc_type; +#define CHAR_ENC_CPY_1 0 /* 1-byte UTF-8, copy. */ +#define CHAR_ENC_ERR_1 1 /* 1-byte UTF-8, error. */ +#define CHAR_ENC_ESC_A 2 /* 1-byte ASCII, escaped as '\x'. */ +#define CHAR_ENC_ESC_1 3 /* 1-byte UTF-8, escaped as '\uXXXX'. */ +#define CHAR_ENC_CPY_2 4 /* 2-byte UTF-8, copy. */ +#define CHAR_ENC_ESC_2 5 /* 2-byte UTF-8, escaped as '\uXXXX'. */ +#define CHAR_ENC_CPY_3 6 /* 3-byte UTF-8, copy. */ +#define CHAR_ENC_ESC_3 7 /* 3-byte UTF-8, escaped as '\uXXXX'. */ +#define CHAR_ENC_CPY_4 8 /* 4-byte UTF-8, copy. */ +#define CHAR_ENC_ESC_4 9 /* 4-byte UTF-8, escaped as '\uXXXX\uXXXX'. */ + +/** Character encode type table: don't escape unicode, don't escape '/'. + (generate with misc/make_tables.c) */ +static const char_enc_type enc_table_cpy[256] = { + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +/** Character encode type table: don't escape unicode, escape '/'. + (generate with misc/make_tables.c) */ +static const char_enc_type enc_table_cpy_slash[256] = { + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +/** Character encode type table: escape unicode, don't escape '/'. + (generate with misc/make_tables.c) */ +static const char_enc_type enc_table_esc[256] = { + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 9, 9, 9, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +/** Character encode type table: escape unicode, escape '/'. + (generate with misc/make_tables.c) */ +static const char_enc_type enc_table_esc_slash[256] = { + 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 3, 2, 2, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 9, 9, 9, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1 +}; + +/** Escaped hex character table: ["00" "01" "02" ... "FD" "FE" "FF"]. + (generate with misc/make_tables.c) */ +yyjson_align(2) +static const u8 esc_hex_char_table[512] = { + '0', '0', '0', '1', '0', '2', '0', '3', + '0', '4', '0', '5', '0', '6', '0', '7', + '0', '8', '0', '9', '0', 'A', '0', 'B', + '0', 'C', '0', 'D', '0', 'E', '0', 'F', + '1', '0', '1', '1', '1', '2', '1', '3', + '1', '4', '1', '5', '1', '6', '1', '7', + '1', '8', '1', '9', '1', 'A', '1', 'B', + '1', 'C', '1', 'D', '1', 'E', '1', 'F', + '2', '0', '2', '1', '2', '2', '2', '3', + '2', '4', '2', '5', '2', '6', '2', '7', + '2', '8', '2', '9', '2', 'A', '2', 'B', + '2', 'C', '2', 'D', '2', 'E', '2', 'F', + '3', '0', '3', '1', '3', '2', '3', '3', + '3', '4', '3', '5', '3', '6', '3', '7', + '3', '8', '3', '9', '3', 'A', '3', 'B', + '3', 'C', '3', 'D', '3', 'E', '3', 'F', + '4', '0', '4', '1', '4', '2', '4', '3', + '4', '4', '4', '5', '4', '6', '4', '7', + '4', '8', '4', '9', '4', 'A', '4', 'B', + '4', 'C', '4', 'D', '4', 'E', '4', 'F', + '5', '0', '5', '1', '5', '2', '5', '3', + '5', '4', '5', '5', '5', '6', '5', '7', + '5', '8', '5', '9', '5', 'A', '5', 'B', + '5', 'C', '5', 'D', '5', 'E', '5', 'F', + '6', '0', '6', '1', '6', '2', '6', '3', + '6', '4', '6', '5', '6', '6', '6', '7', + '6', '8', '6', '9', '6', 'A', '6', 'B', + '6', 'C', '6', 'D', '6', 'E', '6', 'F', + '7', '0', '7', '1', '7', '2', '7', '3', + '7', '4', '7', '5', '7', '6', '7', '7', + '7', '8', '7', '9', '7', 'A', '7', 'B', + '7', 'C', '7', 'D', '7', 'E', '7', 'F', + '8', '0', '8', '1', '8', '2', '8', '3', + '8', '4', '8', '5', '8', '6', '8', '7', + '8', '8', '8', '9', '8', 'A', '8', 'B', + '8', 'C', '8', 'D', '8', 'E', '8', 'F', + '9', '0', '9', '1', '9', '2', '9', '3', + '9', '4', '9', '5', '9', '6', '9', '7', + '9', '8', '9', '9', '9', 'A', '9', 'B', + '9', 'C', '9', 'D', '9', 'E', '9', 'F', + 'A', '0', 'A', '1', 'A', '2', 'A', '3', + 'A', '4', 'A', '5', 'A', '6', 'A', '7', + 'A', '8', 'A', '9', 'A', 'A', 'A', 'B', + 'A', 'C', 'A', 'D', 'A', 'E', 'A', 'F', + 'B', '0', 'B', '1', 'B', '2', 'B', '3', + 'B', '4', 'B', '5', 'B', '6', 'B', '7', + 'B', '8', 'B', '9', 'B', 'A', 'B', 'B', + 'B', 'C', 'B', 'D', 'B', 'E', 'B', 'F', + 'C', '0', 'C', '1', 'C', '2', 'C', '3', + 'C', '4', 'C', '5', 'C', '6', 'C', '7', + 'C', '8', 'C', '9', 'C', 'A', 'C', 'B', + 'C', 'C', 'C', 'D', 'C', 'E', 'C', 'F', + 'D', '0', 'D', '1', 'D', '2', 'D', '3', + 'D', '4', 'D', '5', 'D', '6', 'D', '7', + 'D', '8', 'D', '9', 'D', 'A', 'D', 'B', + 'D', 'C', 'D', 'D', 'D', 'E', 'D', 'F', + 'E', '0', 'E', '1', 'E', '2', 'E', '3', + 'E', '4', 'E', '5', 'E', '6', 'E', '7', + 'E', '8', 'E', '9', 'E', 'A', 'E', 'B', + 'E', 'C', 'E', 'D', 'E', 'E', 'E', 'F', + 'F', '0', 'F', '1', 'F', '2', 'F', '3', + 'F', '4', 'F', '5', 'F', '6', 'F', '7', + 'F', '8', 'F', '9', 'F', 'A', 'F', 'B', + 'F', 'C', 'F', 'D', 'F', 'E', 'F', 'F' +}; + +/** Escaped single character table. (generate with misc/make_tables.c) */ +yyjson_align(2) +static const u8 esc_single_char_table[512] = { + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + '\\', 'b', '\\', 't', '\\', 'n', ' ', ' ', + '\\', 'f', '\\', 'r', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', '\\', '"', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', '\\', '/', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + '\\', '\\', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', + ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' +}; + +/** Returns the encode table with options. */ +static_inline const char_enc_type *get_enc_table_with_flag( + yyjson_write_flag flg) { + if (has_flg(ESCAPE_UNICODE)) { + if (has_flg(ESCAPE_SLASHES)) { + return enc_table_esc_slash; + } else { + return enc_table_esc; + } + } else { + if (has_flg(ESCAPE_SLASHES)) { + return enc_table_cpy_slash; + } else { + return enc_table_cpy; + } + } +} + +/** Write raw string. */ +static_inline u8 *write_raw(u8 *cur, const u8 *raw, usize raw_len) { + memcpy(cur, raw, raw_len); + return cur + raw_len; +} + +/** + Write string no-escape. + @param cur Buffer cursor. + @param str A UTF-8 string, null-terminator is not required. + @param str_len Length of string in bytes. + @return The buffer cursor after string. + */ +static_inline u8 *write_str_noesc(u8 *cur, const u8 *str, usize str_len) { + *cur++ = '"'; + while (str_len >= 16) { + byte_copy_16(cur, str); + cur += 16; + str += 16; + str_len -= 16; + } + while (str_len >= 4) { + byte_copy_4(cur, str); + cur += 4; + str += 4; + str_len -= 4; + } + while (str_len) { + *cur++ = *str++; + str_len -= 1; + } + *cur++ = '"'; + return cur; +} + +/** + Write UTF-8 string (requires len * 6 + 2 bytes buffer). + @param cur Buffer cursor. + @param esc Escape unicode. + @param inv Allow invalid unicode. + @param str A UTF-8 string, null-terminator is not required. + @param str_len Length of string in bytes. + @param enc_table Encode type table for character. + @return The buffer cursor after string, or NULL on invalid unicode. + */ +static_inline u8 *write_str(u8 *cur, bool esc, bool inv, + const u8 *str, usize str_len, + const char_enc_type *enc_table) { + /* The replacement character U+FFFD, used to indicate invalid character. */ + const v32 rep = {{ 'F', 'F', 'F', 'D' }}; + const v32 pre = {{ '\\', 'u', '0', '0' }}; + + const u8 *src = str; + const u8 *end = str + str_len; + *cur++ = '"'; + +copy_ascii: + /* + Copy continuous ASCII, loop unrolling, same as the following code: + + while (end > src) ( + if (unlikely(enc_table[*src])) break; + *cur++ = *src++; + ); + */ +#define expr_jump(i) \ + if (unlikely(enc_table[src[i]])) goto stop_char_##i; + +#define expr_stop(i) \ + stop_char_##i: \ + memcpy(cur, src, i); \ + cur += i; src += i; goto copy_utf8; + + while (end - src >= 16) { + repeat16_incr(expr_jump) + byte_copy_16(cur, src); + cur += 16; src += 16; + } + + while (end - src >= 4) { + repeat4_incr(expr_jump) + byte_copy_4(cur, src); + cur += 4; src += 4; + } + + while (end > src) { + expr_jump(0) + *cur++ = *src++; + } + + *cur++ = '"'; + return cur; + + repeat16_incr(expr_stop) + +#undef expr_jump +#undef expr_stop + +copy_utf8: + if (unlikely(src + 4 > end)) { + if (end == src) goto copy_end; + if (end - src < enc_table[*src] / 2) goto err_one; + } + switch (enc_table[*src]) { + case CHAR_ENC_CPY_1: { + *cur++ = *src++; + goto copy_ascii; + } + case CHAR_ENC_CPY_2: { +#if YYJSON_DISABLE_UTF8_VALIDATION + byte_copy_2(cur, src); +#else + u32 uni = 0; + byte_copy_2(&uni, src); + if (unlikely(!is_utf8_seq2(uni))) goto err_cpy; + byte_copy_2(cur, &uni); +#endif + cur += 2; + src += 2; + goto copy_utf8; + } + case CHAR_ENC_CPY_3: { +#if YYJSON_DISABLE_UTF8_VALIDATION + if (likely(src + 4 <= end)) { + byte_copy_4(cur, src); + } else { + byte_copy_2(cur, src); + cur[2] = src[2]; + } +#else + u32 uni, tmp; + if (likely(src + 4 <= end)) { + uni = byte_load_4(src); + if (unlikely(!is_utf8_seq3(uni))) goto err_cpy; + byte_copy_4(cur, src); + } else { + uni = byte_load_3(src); + if (unlikely(!is_utf8_seq3(uni))) goto err_cpy; + byte_copy_4(cur, &uni); + } +#endif + cur += 3; + src += 3; + goto copy_utf8; + } + case CHAR_ENC_CPY_4: { +#if YYJSON_DISABLE_UTF8_VALIDATION + byte_copy_4(cur, src); +#else + u32 uni, tmp; + uni = byte_load_4(src); + if (unlikely(!is_utf8_seq4(uni))) goto err_cpy; + byte_copy_4(cur, src); +#endif + cur += 4; + src += 4; + goto copy_utf8; + } + case CHAR_ENC_ESC_A: { + byte_copy_2(cur, &esc_single_char_table[*src * 2]); + cur += 2; + src += 1; + goto copy_utf8; + } + case CHAR_ENC_ESC_1: { + byte_copy_4(cur + 0, &pre); + byte_copy_2(cur + 4, &esc_hex_char_table[*src * 2]); + cur += 6; + src += 1; + goto copy_utf8; + } + case CHAR_ENC_ESC_2: { + u16 u; +#if !YYJSON_DISABLE_UTF8_VALIDATION + u32 v4 = 0; + u16 v2 = byte_load_2(src); + byte_copy_2(&v4, &v2); + if (unlikely(!is_utf8_seq2(v4))) goto err_esc; +#endif + u = (u16)(((u16)(src[0] & 0x1F) << 6) | + ((u16)(src[1] & 0x3F) << 0)); + byte_copy_2(cur + 0, &pre); + byte_copy_2(cur + 2, &esc_hex_char_table[(u >> 8) * 2]); + byte_copy_2(cur + 4, &esc_hex_char_table[(u & 0xFF) * 2]); + cur += 6; + src += 2; + goto copy_utf8; + } + case CHAR_ENC_ESC_3: { + u16 u; + u32 v, tmp; +#if !YYJSON_DISABLE_UTF8_VALIDATION + v = byte_load_3(src); + if (unlikely(!is_utf8_seq3(v))) goto err_esc; +#endif + u = (u16)(((u16)(src[0] & 0x0F) << 12) | + ((u16)(src[1] & 0x3F) << 6) | + ((u16)(src[2] & 0x3F) << 0)); + byte_copy_2(cur + 0, &pre); + byte_copy_2(cur + 2, &esc_hex_char_table[(u >> 8) * 2]); + byte_copy_2(cur + 4, &esc_hex_char_table[(u & 0xFF) * 2]); + cur += 6; + src += 3; + goto copy_utf8; + } + case CHAR_ENC_ESC_4: { + u32 hi, lo, u, v, tmp; +#if !YYJSON_DISABLE_UTF8_VALIDATION + v = byte_load_4(src); + if (unlikely(!is_utf8_seq4(v))) goto err_esc; +#endif + u = ((u32)(src[0] & 0x07) << 18) | + ((u32)(src[1] & 0x3F) << 12) | + ((u32)(src[2] & 0x3F) << 6) | + ((u32)(src[3] & 0x3F) << 0); + u -= 0x10000; + hi = (u >> 10) + 0xD800; + lo = (u & 0x3FF) + 0xDC00; + byte_copy_2(cur + 0, &pre); + byte_copy_2(cur + 2, &esc_hex_char_table[(hi >> 8) * 2]); + byte_copy_2(cur + 4, &esc_hex_char_table[(hi & 0xFF) * 2]); + byte_copy_2(cur + 6, &pre); + byte_copy_2(cur + 8, &esc_hex_char_table[(lo >> 8) * 2]); + byte_copy_2(cur + 10, &esc_hex_char_table[(lo & 0xFF) * 2]); + cur += 12; + src += 4; + goto copy_utf8; + } + case CHAR_ENC_ERR_1: { + goto err_one; + } + default: break; /* unreachable */ + } + +copy_end: + *cur++ = '"'; + return cur; + +err_one: + if (esc) goto err_esc; + else goto err_cpy; + +err_cpy: + if (!inv) return NULL; + *cur++ = *src++; + goto copy_utf8; + +err_esc: + if (!inv) return NULL; + byte_copy_2(cur + 0, &pre); + byte_copy_4(cur + 2, &rep); + cur += 6; + src += 1; + goto copy_utf8; +} + + + +/*============================================================================== + * MARK: - JSON Writer Utilities (Private) + *============================================================================*/ + +/** Write null (requires 8 bytes buffer). */ +static_inline u8 *write_null(u8 *cur) { + v64 v = {{ 'n', 'u', 'l', 'l', ',', '\n', 0, 0 }}; + byte_copy_8(cur, &v); + return cur + 4; +} + +/** Write bool (requires 8 bytes buffer). */ +static_inline u8 *write_bool(u8 *cur, bool val) { + v64 v0 = {{ 'f', 'a', 'l', 's', 'e', ',', '\n', 0 }}; + v64 v1 = {{ 't', 'r', 'u', 'e', ',', '\n', 0, 0 }}; + if (val) { + byte_copy_8(cur, &v1); + } else { + byte_copy_8(cur, &v0); + } + return cur + 5 - val; +} + +/** Write indent (requires level x 4 bytes buffer). + Param spaces should not larger than 4. */ +static_inline u8 *write_indent(u8 *cur, usize level, usize spaces) { + while (level-- > 0) { + byte_copy_4(cur, " "); + cur += spaces; + } + return cur; +} + +/** Write data to file pointer. */ +static bool write_dat_to_fp(FILE *fp, u8 *dat, usize len, + yyjson_write_err *err) { + if (fwrite(dat, len, 1, fp) != 1) { + err->msg = "file writing failed"; + err->code = YYJSON_WRITE_ERROR_FILE_WRITE; + return false; + } + return true; +} + +/** Write data to file. */ +static bool write_dat_to_file(const char *path, u8 *dat, usize len, + yyjson_write_err *err) { +#define return_err(_code, _msg) do { \ + err->msg = _msg; \ + err->code = YYJSON_WRITE_ERROR_##_code; \ + if (file) fclose(file); \ + return false; \ +} while (false) + + FILE *file = fopen_writeonly(path); + if (file == NULL) { + return_err(FILE_OPEN, MSG_FOPEN); + } + if (fwrite(dat, len, 1, file) != 1) { + return_err(FILE_WRITE, MSG_FWRITE); + } + if (fclose(file) != 0) { + file = NULL; + return_err(FILE_WRITE, MSG_FCLOSE); + } + return true; + +#undef return_err +} + + + +/*============================================================================== + * MARK: - JSON Writer Implementation (Private) + *============================================================================*/ + +typedef struct yyjson_write_ctx { + usize tag; +} yyjson_write_ctx; + +static_inline void yyjson_write_ctx_set(yyjson_write_ctx *ctx, + usize size, bool is_obj) { + ctx->tag = (size << 1) | (usize)is_obj; +} + +static_inline void yyjson_write_ctx_get(yyjson_write_ctx *ctx, + usize *size, bool *is_obj) { + usize tag = ctx->tag; + *size = tag >> 1; + *is_obj = (bool)(tag & 1); +} + +/** Write single JSON value. */ +static_inline u8 *yyjson_write_single(yyjson_val *val, + yyjson_write_flag flg, + yyjson_alc alc, + usize *dat_len, + yyjson_write_err *err) { +#define return_err(_code, _msg) do { \ + if (hdr) alc.free(alc.ctx, (void *)hdr); \ + *dat_len = 0; \ + err->code = YYJSON_WRITE_ERROR_##_code; \ + err->msg = _msg; \ + return NULL; \ +} while (false) + +#define incr_len(_len) do { \ + hdr = (u8 *)alc.malloc(alc.ctx, _len); \ + if (!hdr) goto fail_alloc; \ + cur = hdr; \ +} while (false) + +#define check_str_len(_len) do { \ + if ((sizeof(usize) < 8) && (_len >= (USIZE_MAX - 16) / 6)) \ + goto fail_alloc; \ +} while (false) + + u8 *hdr = NULL, *cur; + usize str_len; + const u8 *str_ptr; + const char_enc_type *enc_table = get_enc_table_with_flag(flg); + bool cpy = (enc_table == enc_table_cpy); + bool esc = has_flg(ESCAPE_UNICODE) != 0; + bool inv = has_allow(INVALID_UNICODE) != 0; + bool newline = has_flg(NEWLINE_AT_END) != 0; + const usize end_len = 2; /* '\n' and '\0' */ + + switch (unsafe_yyjson_get_type(val)) { + case YYJSON_TYPE_RAW: + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len + end_len); + cur = write_raw(cur, str_ptr, str_len); + break; + + case YYJSON_TYPE_STR: + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len * 6 + 2 + end_len); + if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { + cur = write_str_noesc(cur, str_ptr, str_len); + } else { + cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + if (unlikely(!cur)) goto fail_str; + } + break; + + case YYJSON_TYPE_NUM: + incr_len(FP_BUF_LEN + end_len); + cur = write_num(cur, val, flg); + if (unlikely(!cur)) goto fail_num; + break; + + case YYJSON_TYPE_BOOL: + incr_len(8); + cur = write_bool(cur, unsafe_yyjson_get_bool(val)); + break; + + case YYJSON_TYPE_NULL: + incr_len(8); + cur = write_null(cur); + break; + + case YYJSON_TYPE_ARR: + incr_len(2 + end_len); + byte_copy_2(cur, "[]"); + cur += 2; + break; + + case YYJSON_TYPE_OBJ: + incr_len(2 + end_len); + byte_copy_2(cur, "{}"); + cur += 2; + break; + + default: + goto fail_type; + } + + if (newline) *cur++ = '\n'; + *cur = '\0'; + *dat_len = (usize)(cur - hdr); + memset(err, 0, sizeof(yyjson_write_err)); + return hdr; + +fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); +fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); +fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); +fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); + +#undef return_err +#undef check_str_len +#undef incr_len +} + +/** Write JSON document minify. + The root of this document should be a non-empty container. */ +static_inline u8 *yyjson_write_minify(const yyjson_val *root, + const yyjson_write_flag flg, + const yyjson_alc alc, + usize *dat_len, + yyjson_write_err *err) { +#define return_err(_code, _msg) do { \ + *dat_len = 0; \ + err->code = YYJSON_WRITE_ERROR_##_code; \ + err->msg = _msg; \ + if (hdr) alc.free(alc.ctx, hdr); \ + return NULL; \ +} while (false) + +#define incr_len(_len) do { \ + ext_len = (usize)(_len); \ + if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ + usize ctx_pos = (usize)((u8 *)ctx - hdr); \ + usize cur_pos = (usize)(cur - hdr); \ + ctx_len = (usize)(end - (u8 *)ctx); \ + alc_inc = yyjson_max(alc_len / 2, ext_len); \ + alc_inc = size_align_up(alc_inc, sizeof(yyjson_write_ctx)); \ + if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ + goto fail_alloc; \ + alc_len += alc_inc; \ + tmp = (u8 *)alc.realloc(alc.ctx, hdr, alc_len - alc_inc, alc_len); \ + if (unlikely(!tmp)) goto fail_alloc; \ + ctx_tmp = (yyjson_write_ctx *)(void *)(tmp + (alc_len - ctx_len)); \ + memmove((void *)ctx_tmp, (void *)(tmp + ctx_pos), ctx_len); \ + ctx = ctx_tmp; \ + cur = tmp + cur_pos; \ + end = tmp + alc_len; \ + hdr = tmp; \ + } \ +} while (false) + +#define check_str_len(_len) do { \ + if ((sizeof(usize) < 8) && (_len >= (USIZE_MAX - 16) / 6)) \ + goto fail_alloc; \ +} while (false) + + yyjson_val *val; + yyjson_type val_type; + usize ctn_len, ctn_len_tmp; + bool ctn_obj, ctn_obj_tmp, is_key; + u8 *hdr, *cur, *end, *tmp; + yyjson_write_ctx *ctx, *ctx_tmp; + usize alc_len, alc_inc, ctx_len, ext_len, str_len; + const u8 *str_ptr; + const char_enc_type *enc_table = get_enc_table_with_flag(flg); + bool cpy = (enc_table == enc_table_cpy); + bool esc = has_flg(ESCAPE_UNICODE) != 0; + bool inv = has_allow(INVALID_UNICODE) != 0; + bool newline = has_flg(NEWLINE_AT_END) != 0; + + alc_len = root->uni.ofs / sizeof(yyjson_val); + alc_len = alc_len * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + cur = hdr; + end = hdr + alc_len; + ctx = (yyjson_write_ctx *)(void *)end; + +doc_begin: + val = constcast(yyjson_val *)root; + val_type = unsafe_yyjson_get_type(val); + ctn_obj = (val_type == YYJSON_TYPE_OBJ); + ctn_len = unsafe_yyjson_get_len(val) << (u8)ctn_obj; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + val++; + +val_begin: + val_type = unsafe_yyjson_get_type(val); + if (val_type == YYJSON_TYPE_STR) { + is_key = ((u8)ctn_obj & (u8)~ctn_len); + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len * 6 + 16); + if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { + cur = write_str_noesc(cur, str_ptr, str_len); + } else { + cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + if (unlikely(!cur)) goto fail_str; + } + *cur++ = is_key ? ':' : ','; + goto val_end; + } + if (val_type == YYJSON_TYPE_NUM) { + incr_len(FP_BUF_LEN); + cur = write_num(cur, val, flg); + if (unlikely(!cur)) goto fail_num; + *cur++ = ','; + goto val_end; + } + if ((val_type & (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) == + (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) { + ctn_len_tmp = unsafe_yyjson_get_len(val); + ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); + incr_len(16); + if (unlikely(ctn_len_tmp == 0)) { + /* write empty container */ + *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); + *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); + *cur++ = ','; + goto val_end; + } else { + /* push context, setup new container */ + yyjson_write_ctx_set(--ctx, ctn_len, ctn_obj); + ctn_len = ctn_len_tmp << (u8)ctn_obj_tmp; + ctn_obj = ctn_obj_tmp; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + val++; + goto val_begin; + } + } + if (val_type == YYJSON_TYPE_BOOL) { + incr_len(16); + cur = write_bool(cur, unsafe_yyjson_get_bool(val)); + cur++; + goto val_end; + } + if (val_type == YYJSON_TYPE_NULL) { + incr_len(16); + cur = write_null(cur); + cur++; + goto val_end; + } + if (val_type == YYJSON_TYPE_RAW) { + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len + 2); + cur = write_raw(cur, str_ptr, str_len); + *cur++ = ','; + goto val_end; + } + goto fail_type; + +val_end: + val++; + ctn_len--; + if (unlikely(ctn_len == 0)) goto ctn_end; + goto val_begin; + +ctn_end: + cur--; + *cur++ = (u8)(']' | ((u8)ctn_obj << 5)); + *cur++ = ','; + if (unlikely((u8 *)ctx >= end)) goto doc_end; + yyjson_write_ctx_get(ctx++, &ctn_len, &ctn_obj); + ctn_len--; + if (likely(ctn_len > 0)) { + goto val_begin; + } else { + goto ctn_end; + } + +doc_end: + if (newline) { + incr_len(2); + *(cur - 1) = '\n'; + cur++; + } + *--cur = '\0'; + *dat_len = (usize)(cur - hdr); + memset(err, 0, sizeof(yyjson_write_err)); + return hdr; + +fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); +fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); +fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); +fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); + +#undef return_err +#undef incr_len +#undef check_str_len +} + +/** Write JSON document pretty. + The root of this document should be a non-empty container. */ +static_inline u8 *yyjson_write_pretty(const yyjson_val *root, + const yyjson_write_flag flg, + const yyjson_alc alc, + usize *dat_len, + yyjson_write_err *err) { +#define return_err(_code, _msg) do { \ + *dat_len = 0; \ + err->code = YYJSON_WRITE_ERROR_##_code; \ + err->msg = _msg; \ + if (hdr) alc.free(alc.ctx, hdr); \ + return NULL; \ +} while (false) + +#define incr_len(_len) do { \ + ext_len = (usize)(_len); \ + if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ + usize ctx_pos = (usize)((u8 *)ctx - hdr); \ + usize cur_pos = (usize)(cur - hdr); \ + ctx_len = (usize)(end - (u8 *)ctx); \ + alc_inc = yyjson_max(alc_len / 2, ext_len); \ + alc_inc = size_align_up(alc_inc, sizeof(yyjson_write_ctx)); \ + if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ + goto fail_alloc; \ + alc_len += alc_inc; \ + tmp = (u8 *)alc.realloc(alc.ctx, hdr, alc_len - alc_inc, alc_len); \ + if (unlikely(!tmp)) goto fail_alloc; \ + ctx_tmp = (yyjson_write_ctx *)(void *)(tmp + (alc_len - ctx_len)); \ + memmove((void *)ctx_tmp, (void *)(tmp + ctx_pos), ctx_len); \ + ctx = ctx_tmp; \ + cur = tmp + cur_pos; \ + end = tmp + alc_len; \ + hdr = tmp; \ + } \ +} while (false) + +#define check_str_len(_len) do { \ + if ((sizeof(usize) < 8) && (_len >= (USIZE_MAX - 16) / 6)) \ + goto fail_alloc; \ +} while (false) + + yyjson_val *val; + yyjson_type val_type; + usize ctn_len, ctn_len_tmp; + bool ctn_obj, ctn_obj_tmp, is_key, no_indent; + u8 *hdr, *cur, *end, *tmp; + yyjson_write_ctx *ctx, *ctx_tmp; + usize alc_len, alc_inc, ctx_len, ext_len, str_len, level; + const u8 *str_ptr; + const char_enc_type *enc_table = get_enc_table_with_flag(flg); + bool cpy = (enc_table == enc_table_cpy); + bool esc = has_flg(ESCAPE_UNICODE) != 0; + bool inv = has_allow(INVALID_UNICODE) != 0; + usize spaces = has_flg(PRETTY_TWO_SPACES) ? 2 : 4; + bool newline = has_flg(NEWLINE_AT_END) != 0; + + alc_len = root->uni.ofs / sizeof(yyjson_val); + alc_len = alc_len * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + cur = hdr; + end = hdr + alc_len; + ctx = (yyjson_write_ctx *)(void *)end; + +doc_begin: + val = constcast(yyjson_val *)root; + val_type = unsafe_yyjson_get_type(val); + ctn_obj = (val_type == YYJSON_TYPE_OBJ); + ctn_len = unsafe_yyjson_get_len(val) << (u8)ctn_obj; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + *cur++ = '\n'; + val++; + level = 1; + +val_begin: + val_type = unsafe_yyjson_get_type(val); + if (val_type == YYJSON_TYPE_STR) { + is_key = (bool)((u8)ctn_obj & (u8)~ctn_len); + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len * 6 + 16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { + cur = write_str_noesc(cur, str_ptr, str_len); + } else { + cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + if (unlikely(!cur)) goto fail_str; + } + *cur++ = is_key ? ':' : ','; + *cur++ = is_key ? ' ' : '\n'; + goto val_end; + } + if (val_type == YYJSON_TYPE_NUM) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + incr_len(FP_BUF_LEN + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_num(cur, val, flg); + if (unlikely(!cur)) goto fail_num; + *cur++ = ','; + *cur++ = '\n'; + goto val_end; + } + if ((val_type & (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) == + (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + ctn_len_tmp = unsafe_yyjson_get_len(val); + ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); + if (unlikely(ctn_len_tmp == 0)) { + /* write empty container */ + incr_len(16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); + *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); + *cur++ = ','; + *cur++ = '\n'; + goto val_end; + } else { + /* push context, setup new container */ + incr_len(32 + (no_indent ? 0 : level * 4)); + yyjson_write_ctx_set(--ctx, ctn_len, ctn_obj); + ctn_len = ctn_len_tmp << (u8)ctn_obj_tmp; + ctn_obj = ctn_obj_tmp; + cur = write_indent(cur, no_indent ? 0 : level, spaces); + level++; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + *cur++ = '\n'; + val++; + goto val_begin; + } + } + if (val_type == YYJSON_TYPE_BOOL) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + incr_len(16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_bool(cur, unsafe_yyjson_get_bool(val)); + cur += 2; + goto val_end; + } + if (val_type == YYJSON_TYPE_NULL) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + incr_len(16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_null(cur); + cur += 2; + goto val_end; + } + if (val_type == YYJSON_TYPE_RAW) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len + 3 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_raw(cur, str_ptr, str_len); + *cur++ = ','; + *cur++ = '\n'; + goto val_end; + } + goto fail_type; + +val_end: + val++; + ctn_len--; + if (unlikely(ctn_len == 0)) goto ctn_end; + goto val_begin; + +ctn_end: + cur -= 2; + *cur++ = '\n'; + incr_len(level * 4); + cur = write_indent(cur, --level, spaces); + *cur++ = (u8)(']' | ((u8)ctn_obj << 5)); + if (unlikely((u8 *)ctx >= end)) goto doc_end; + yyjson_write_ctx_get(ctx++, &ctn_len, &ctn_obj); + ctn_len--; + *cur++ = ','; + *cur++ = '\n'; + if (likely(ctn_len > 0)) { + goto val_begin; + } else { + goto ctn_end; + } + +doc_end: + if (newline) { + incr_len(2); + *cur++ = '\n'; + } + *cur = '\0'; + *dat_len = (usize)(cur - hdr); + memset(err, 0, sizeof(yyjson_write_err)); + return hdr; + +fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); +fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); +fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); +fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); + +#undef return_err +#undef incr_len +#undef check_str_len +} + + + +/*============================================================================== + * MARK: - JSON Writer (Public) + *============================================================================*/ + +char *yyjson_val_write_opts(const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + usize *dat_len, + yyjson_write_err *err) { + yyjson_write_err tmp_err; + usize tmp_dat_len; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + yyjson_val *root = constcast(yyjson_val *)val; + + if (!err) err = &tmp_err; + if (!dat_len) dat_len = &tmp_dat_len; + + if (unlikely(!root)) { + *dat_len = 0; + err->msg = "input JSON is NULL"; + err->code = YYJSON_READ_ERROR_INVALID_PARAMETER; + return NULL; + } + + if (!unsafe_yyjson_is_ctn(root) || unsafe_yyjson_get_len(root) == 0) { + return (char *)yyjson_write_single(root, flg, alc, dat_len, err); + } else if (flg & (YYJSON_WRITE_PRETTY | YYJSON_WRITE_PRETTY_TWO_SPACES)) { + return (char *)yyjson_write_pretty(root, flg, alc, dat_len, err); + } else { + return (char *)yyjson_write_minify(root, flg, alc, dat_len, err); + } +} + +char *yyjson_write_opts(const yyjson_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + usize *dat_len, + yyjson_write_err *err) { + yyjson_val *root = doc ? doc->root : NULL; + return yyjson_val_write_opts(root, flg, alc_ptr, dat_len, err); +} + +bool yyjson_val_write_file(const char *path, + const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_write_err tmp_err; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + u8 *dat; + usize dat_len = 0; + yyjson_val *root = constcast(yyjson_val *)val; + bool suc; + + if (!err) err = &tmp_err; + if (unlikely(!path || !*path)) { + err->msg = "input path is invalid"; + err->code = YYJSON_READ_ERROR_INVALID_PARAMETER; + return false; + } + + dat = (u8 *)yyjson_val_write_opts(root, flg, &alc, &dat_len, err); + if (unlikely(!dat)) return false; + suc = write_dat_to_file(path, dat, dat_len, err); + alc.free(alc.ctx, dat); + return suc; +} + +bool yyjson_val_write_fp(FILE *fp, + const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_write_err tmp_err; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + u8 *dat; + usize dat_len = 0; + yyjson_val *root = constcast(yyjson_val *)val; + bool suc; + + if (!err) err = &tmp_err; + if (unlikely(!fp)) { + err->msg = "input fp is invalid"; + err->code = YYJSON_READ_ERROR_INVALID_PARAMETER; + return false; + } + + dat = (u8 *)yyjson_val_write_opts(root, flg, &alc, &dat_len, err); + if (unlikely(!dat)) return false; + suc = write_dat_to_fp(fp, dat, dat_len, err); + alc.free(alc.ctx, dat); + return suc; +} + +bool yyjson_write_file(const char *path, + const yyjson_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_val *root = doc ? doc->root : NULL; + return yyjson_val_write_file(path, root, flg, alc_ptr, err); +} + +bool yyjson_write_fp(FILE *fp, + const yyjson_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_val *root = doc ? doc->root : NULL; + return yyjson_val_write_fp(fp, root, flg, alc_ptr, err); +} + + + +/*============================================================================== + * MARK: - Mutable JSON Writer Implementation (Private) + *============================================================================*/ + +typedef struct yyjson_mut_write_ctx { + usize tag; + yyjson_mut_val *ctn; +} yyjson_mut_write_ctx; + +static_inline void yyjson_mut_write_ctx_set(yyjson_mut_write_ctx *ctx, + yyjson_mut_val *ctn, + usize size, bool is_obj) { + ctx->tag = (size << 1) | (usize)is_obj; + ctx->ctn = ctn; +} + +static_inline void yyjson_mut_write_ctx_get(yyjson_mut_write_ctx *ctx, + yyjson_mut_val **ctn, + usize *size, bool *is_obj) { + usize tag = ctx->tag; + *size = tag >> 1; + *is_obj = (bool)(tag & 1); + *ctn = ctx->ctn; +} + +/** Get the estimated number of values for the mutable JSON document. */ +static_inline usize yyjson_mut_doc_estimated_val_num( + const yyjson_mut_doc *doc) { + usize sum = 0; + yyjson_val_chunk *chunk = doc->val_pool.chunks; + while (chunk) { + sum += chunk->chunk_size / sizeof(yyjson_mut_val) - 1; + if (chunk == doc->val_pool.chunks) { + sum -= (usize)(doc->val_pool.end - doc->val_pool.cur); + } + chunk = chunk->next; + } + return sum; +} + +/** Write single JSON value. */ +static_inline u8 *yyjson_mut_write_single(yyjson_mut_val *val, + yyjson_write_flag flg, + yyjson_alc alc, + usize *dat_len, + yyjson_write_err *err) { + return yyjson_write_single((yyjson_val *)val, flg, alc, dat_len, err); +} + +/** Write JSON document minify. + The root of this document should be a non-empty container. */ +static_inline u8 *yyjson_mut_write_minify(const yyjson_mut_val *root, + usize estimated_val_num, + yyjson_write_flag flg, + yyjson_alc alc, + usize *dat_len, + yyjson_write_err *err) { +#define return_err(_code, _msg) do { \ + *dat_len = 0; \ + err->code = YYJSON_WRITE_ERROR_##_code; \ + err->msg = _msg; \ + if (hdr) alc.free(alc.ctx, hdr); \ + return NULL; \ +} while (false) + +#define incr_len(_len) do { \ + ext_len = (usize)(_len); \ + if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ + usize ctx_pos = (usize)((u8 *)ctx - hdr); \ + usize cur_pos = (usize)(cur - hdr); \ + ctx_len = (usize)(end - (u8 *)ctx); \ + alc_inc = yyjson_max(alc_len / 2, ext_len); \ + alc_inc = size_align_up(alc_inc, sizeof(yyjson_mut_write_ctx)); \ + if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ + goto fail_alloc; \ + alc_len += alc_inc; \ + tmp = (u8 *)alc.realloc(alc.ctx, hdr, alc_len - alc_inc, alc_len); \ + if (unlikely(!tmp)) goto fail_alloc; \ + ctx_tmp = (yyjson_mut_write_ctx *)(void *)(tmp + (alc_len - ctx_len)); \ + memmove((void *)ctx_tmp, (void *)(tmp + ctx_pos), ctx_len); \ + ctx = ctx_tmp; \ + cur = tmp + cur_pos; \ + end = tmp + alc_len; \ + hdr = tmp; \ + } \ +} while (false) + +#define check_str_len(_len) do { \ + if ((sizeof(usize) < 8) && (_len >= (USIZE_MAX - 16) / 6)) \ + goto fail_alloc; \ +} while (false) + + yyjson_mut_val *val, *ctn; + yyjson_type val_type; + usize ctn_len, ctn_len_tmp; + bool ctn_obj, ctn_obj_tmp, is_key; + u8 *hdr, *cur, *end, *tmp; + yyjson_mut_write_ctx *ctx, *ctx_tmp; + usize alc_len, alc_inc, ctx_len, ext_len, str_len; + const u8 *str_ptr; + const char_enc_type *enc_table = get_enc_table_with_flag(flg); + bool cpy = (enc_table == enc_table_cpy); + bool esc = has_flg(ESCAPE_UNICODE) != 0; + bool inv = has_allow(INVALID_UNICODE) != 0; + bool newline = has_flg(NEWLINE_AT_END) != 0; + + alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_MINIFY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + cur = hdr; + end = hdr + alc_len; + ctx = (yyjson_mut_write_ctx *)(void *)end; + +doc_begin: + val = constcast(yyjson_mut_val *)root; + val_type = unsafe_yyjson_get_type(val); + ctn_obj = (val_type == YYJSON_TYPE_OBJ); + ctn_len = unsafe_yyjson_get_len(val) << (u8)ctn_obj; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + ctn = val; + val = (yyjson_mut_val *)val->uni.ptr; /* tail */ + val = ctn_obj ? val->next->next : val->next; + +val_begin: + val_type = unsafe_yyjson_get_type(val); + if (val_type == YYJSON_TYPE_STR) { + is_key = ((u8)ctn_obj & (u8)~ctn_len); + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len * 6 + 16); + if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { + cur = write_str_noesc(cur, str_ptr, str_len); + } else { + cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + if (unlikely(!cur)) goto fail_str; + } + *cur++ = is_key ? ':' : ','; + goto val_end; + } + if (val_type == YYJSON_TYPE_NUM) { + incr_len(FP_BUF_LEN); + cur = write_num(cur, (yyjson_val *)val, flg); + if (unlikely(!cur)) goto fail_num; + *cur++ = ','; + goto val_end; + } + if ((val_type & (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) == + (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) { + ctn_len_tmp = unsafe_yyjson_get_len(val); + ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); + incr_len(16); + if (unlikely(ctn_len_tmp == 0)) { + /* write empty container */ + *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); + *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); + *cur++ = ','; + goto val_end; + } else { + /* push context, setup new container */ + yyjson_mut_write_ctx_set(--ctx, ctn, ctn_len, ctn_obj); + ctn_len = ctn_len_tmp << (u8)ctn_obj_tmp; + ctn_obj = ctn_obj_tmp; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + ctn = val; + val = (yyjson_mut_val *)ctn->uni.ptr; /* tail */ + val = ctn_obj ? val->next->next : val->next; + goto val_begin; + } + } + if (val_type == YYJSON_TYPE_BOOL) { + incr_len(16); + cur = write_bool(cur, unsafe_yyjson_get_bool(val)); + cur++; + goto val_end; + } + if (val_type == YYJSON_TYPE_NULL) { + incr_len(16); + cur = write_null(cur); + cur++; + goto val_end; + } + if (val_type == YYJSON_TYPE_RAW) { + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len + 2); + cur = write_raw(cur, str_ptr, str_len); + *cur++ = ','; + goto val_end; + } + goto fail_type; + +val_end: + ctn_len--; + if (unlikely(ctn_len == 0)) goto ctn_end; + val = val->next; + goto val_begin; + +ctn_end: + cur--; + *cur++ = (u8)(']' | ((u8)ctn_obj << 5)); + *cur++ = ','; + if (unlikely((u8 *)ctx >= end)) goto doc_end; + val = ctn->next; + yyjson_mut_write_ctx_get(ctx++, &ctn, &ctn_len, &ctn_obj); + ctn_len--; + if (likely(ctn_len > 0)) { + goto val_begin; + } else { + goto ctn_end; + } + +doc_end: + if (newline) { + incr_len(2); + *(cur - 1) = '\n'; + cur++; + } + *--cur = '\0'; + *dat_len = (usize)(cur - hdr); + err->code = YYJSON_WRITE_SUCCESS; + err->msg = NULL; + return hdr; + +fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); +fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); +fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); +fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); + +#undef return_err +#undef incr_len +#undef check_str_len +} + +/** Write JSON document pretty. + The root of this document should be a non-empty container. */ +static_inline u8 *yyjson_mut_write_pretty(const yyjson_mut_val *root, + usize estimated_val_num, + yyjson_write_flag flg, + yyjson_alc alc, + usize *dat_len, + yyjson_write_err *err) { +#define return_err(_code, _msg) do { \ + *dat_len = 0; \ + err->code = YYJSON_WRITE_ERROR_##_code; \ + err->msg = _msg; \ + if (hdr) alc.free(alc.ctx, hdr); \ + return NULL; \ +} while (false) + +#define incr_len(_len) do { \ + ext_len = (usize)(_len); \ + if (unlikely((u8 *)(cur + ext_len) >= (u8 *)ctx)) { \ + usize ctx_pos = (usize)((u8 *)ctx - hdr); \ + usize cur_pos = (usize)(cur - hdr); \ + ctx_len = (usize)(end - (u8 *)ctx); \ + alc_inc = yyjson_max(alc_len / 2, ext_len); \ + alc_inc = size_align_up(alc_inc, sizeof(yyjson_mut_write_ctx)); \ + if ((sizeof(usize) < 8) && size_add_is_overflow(alc_len, alc_inc)) \ + goto fail_alloc; \ + alc_len += alc_inc; \ + tmp = (u8 *)alc.realloc(alc.ctx, hdr, alc_len - alc_inc, alc_len); \ + if (unlikely(!tmp)) goto fail_alloc; \ + ctx_tmp = (yyjson_mut_write_ctx *)(void *)(tmp + (alc_len - ctx_len)); \ + memmove((void *)ctx_tmp, (void *)(tmp + ctx_pos), ctx_len); \ + ctx = ctx_tmp; \ + cur = tmp + cur_pos; \ + end = tmp + alc_len; \ + hdr = tmp; \ + } \ +} while (false) + +#define check_str_len(_len) do { \ + if ((sizeof(usize) < 8) && (_len >= (USIZE_MAX - 16) / 6)) \ + goto fail_alloc; \ +} while (false) + + yyjson_mut_val *val, *ctn; + yyjson_type val_type; + usize ctn_len, ctn_len_tmp; + bool ctn_obj, ctn_obj_tmp, is_key, no_indent; + u8 *hdr, *cur, *end, *tmp; + yyjson_mut_write_ctx *ctx, *ctx_tmp; + usize alc_len, alc_inc, ctx_len, ext_len, str_len, level; + const u8 *str_ptr; + const char_enc_type *enc_table = get_enc_table_with_flag(flg); + bool cpy = (enc_table == enc_table_cpy); + bool esc = has_flg(ESCAPE_UNICODE) != 0; + bool inv = has_allow(INVALID_UNICODE) != 0; + usize spaces = has_flg(PRETTY_TWO_SPACES) ? 2 : 4; + bool newline = has_flg(NEWLINE_AT_END) != 0; + + alc_len = estimated_val_num * YYJSON_WRITER_ESTIMATED_PRETTY_RATIO + 64; + alc_len = size_align_up(alc_len, sizeof(yyjson_mut_write_ctx)); + hdr = (u8 *)alc.malloc(alc.ctx, alc_len); + if (!hdr) goto fail_alloc; + cur = hdr; + end = hdr + alc_len; + ctx = (yyjson_mut_write_ctx *)(void *)end; + +doc_begin: + val = constcast(yyjson_mut_val *)root; + val_type = unsafe_yyjson_get_type(val); + ctn_obj = (val_type == YYJSON_TYPE_OBJ); + ctn_len = unsafe_yyjson_get_len(val) << (u8)ctn_obj; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + *cur++ = '\n'; + ctn = val; + val = (yyjson_mut_val *)val->uni.ptr; /* tail */ + val = ctn_obj ? val->next->next : val->next; + level = 1; + +val_begin: + val_type = unsafe_yyjson_get_type(val); + if (val_type == YYJSON_TYPE_STR) { + is_key = (bool)((u8)ctn_obj & (u8)~ctn_len); + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len * 6 + 16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + if (likely(cpy) && unsafe_yyjson_get_subtype(val)) { + cur = write_str_noesc(cur, str_ptr, str_len); + } else { + cur = write_str(cur, esc, inv, str_ptr, str_len, enc_table); + if (unlikely(!cur)) goto fail_str; + } + *cur++ = is_key ? ':' : ','; + *cur++ = is_key ? ' ' : '\n'; + goto val_end; + } + if (val_type == YYJSON_TYPE_NUM) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + incr_len(FP_BUF_LEN + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_num(cur, (yyjson_val *)val, flg); + if (unlikely(!cur)) goto fail_num; + *cur++ = ','; + *cur++ = '\n'; + goto val_end; + } + if ((val_type & (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) == + (YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ)) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + ctn_len_tmp = unsafe_yyjson_get_len(val); + ctn_obj_tmp = (val_type == YYJSON_TYPE_OBJ); + if (unlikely(ctn_len_tmp == 0)) { + /* write empty container */ + incr_len(16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + *cur++ = (u8)('[' | ((u8)ctn_obj_tmp << 5)); + *cur++ = (u8)(']' | ((u8)ctn_obj_tmp << 5)); + *cur++ = ','; + *cur++ = '\n'; + goto val_end; + } else { + /* push context, setup new container */ + incr_len(32 + (no_indent ? 0 : level * 4)); + yyjson_mut_write_ctx_set(--ctx, ctn, ctn_len, ctn_obj); + ctn_len = ctn_len_tmp << (u8)ctn_obj_tmp; + ctn_obj = ctn_obj_tmp; + cur = write_indent(cur, no_indent ? 0 : level, spaces); + level++; + *cur++ = (u8)('[' | ((u8)ctn_obj << 5)); + *cur++ = '\n'; + ctn = val; + val = (yyjson_mut_val *)ctn->uni.ptr; /* tail */ + val = ctn_obj ? val->next->next : val->next; + goto val_begin; + } + } + if (val_type == YYJSON_TYPE_BOOL) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + incr_len(16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_bool(cur, unsafe_yyjson_get_bool(val)); + cur += 2; + goto val_end; + } + if (val_type == YYJSON_TYPE_NULL) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + incr_len(16 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_null(cur); + cur += 2; + goto val_end; + } + if (val_type == YYJSON_TYPE_RAW) { + no_indent = (bool)((u8)ctn_obj & (u8)ctn_len); + str_len = unsafe_yyjson_get_len(val); + str_ptr = (const u8 *)unsafe_yyjson_get_str(val); + check_str_len(str_len); + incr_len(str_len + 3 + (no_indent ? 0 : level * 4)); + cur = write_indent(cur, no_indent ? 0 : level, spaces); + cur = write_raw(cur, str_ptr, str_len); + *cur++ = ','; + *cur++ = '\n'; + goto val_end; + } + goto fail_type; + +val_end: + ctn_len--; + if (unlikely(ctn_len == 0)) goto ctn_end; + val = val->next; + goto val_begin; + +ctn_end: + cur -= 2; + *cur++ = '\n'; + incr_len(level * 4); + cur = write_indent(cur, --level, spaces); + *cur++ = (u8)(']' | ((u8)ctn_obj << 5)); + if (unlikely((u8 *)ctx >= end)) goto doc_end; + val = ctn->next; + yyjson_mut_write_ctx_get(ctx++, &ctn, &ctn_len, &ctn_obj); + ctn_len--; + *cur++ = ','; + *cur++ = '\n'; + if (likely(ctn_len > 0)) { + goto val_begin; + } else { + goto ctn_end; + } + +doc_end: + if (newline) { + incr_len(2); + *cur++ = '\n'; + } + *cur = '\0'; + *dat_len = (usize)(cur - hdr); + err->code = YYJSON_WRITE_SUCCESS; + err->msg = NULL; + return hdr; + +fail_alloc: return_err(MEMORY_ALLOCATION, MSG_MALLOC); +fail_type: return_err(INVALID_VALUE_TYPE, MSG_ERR_TYPE); +fail_num: return_err(NAN_OR_INF, MSG_NAN_INF); +fail_str: return_err(INVALID_STRING, MSG_ERR_UTF8); + +#undef return_err +#undef incr_len +#undef check_str_len +} + +static char *yyjson_mut_write_opts_impl(const yyjson_mut_val *val, + usize estimated_val_num, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + usize *dat_len, + yyjson_write_err *err) { + yyjson_write_err tmp_err; + usize tmp_dat_len; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + yyjson_mut_val *root = constcast(yyjson_mut_val *)val; + + if (!err) err = &tmp_err; + if (!dat_len) dat_len = &tmp_dat_len; + + if (unlikely(!root)) { + *dat_len = 0; + err->msg = "input JSON is NULL"; + err->code = YYJSON_WRITE_ERROR_INVALID_PARAMETER; + return NULL; + } + + if (!unsafe_yyjson_is_ctn(root) || unsafe_yyjson_get_len(root) == 0) { + return (char *)yyjson_mut_write_single(root, flg, alc, dat_len, err); + } else if (flg & (YYJSON_WRITE_PRETTY | YYJSON_WRITE_PRETTY_TWO_SPACES)) { + return (char *)yyjson_mut_write_pretty(root, estimated_val_num, + flg, alc, dat_len, err); + } else { + return (char *)yyjson_mut_write_minify(root, estimated_val_num, + flg, alc, dat_len, err); + } +} + + + +/*============================================================================== + * MARK: - Mutable JSON Writer (Public) + *============================================================================*/ + +char *yyjson_mut_val_write_opts(const yyjson_mut_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + usize *dat_len, + yyjson_write_err *err) { + return yyjson_mut_write_opts_impl(val, 0, flg, alc_ptr, dat_len, err); +} + +char *yyjson_mut_write_opts(const yyjson_mut_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + usize *dat_len, + yyjson_write_err *err) { + yyjson_mut_val *root; + usize estimated_val_num; + if (likely(doc)) { + root = doc->root; + estimated_val_num = yyjson_mut_doc_estimated_val_num(doc); + } else { + root = NULL; + estimated_val_num = 0; + } + return yyjson_mut_write_opts_impl(root, estimated_val_num, + flg, alc_ptr, dat_len, err); +} + +bool yyjson_mut_val_write_file(const char *path, + const yyjson_mut_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_write_err tmp_err; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + u8 *dat; + usize dat_len = 0; + yyjson_mut_val *root = constcast(yyjson_mut_val *)val; + bool suc; + + if (!err) err = &tmp_err; + if (unlikely(!path || !*path)) { + err->msg = "input path is invalid"; + err->code = YYJSON_WRITE_ERROR_INVALID_PARAMETER; + return false; + } + + dat = (u8 *)yyjson_mut_val_write_opts(root, flg, &alc, &dat_len, err); + if (unlikely(!dat)) return false; + suc = write_dat_to_file(path, dat, dat_len, err); + alc.free(alc.ctx, dat); + return suc; +} + +bool yyjson_mut_val_write_fp(FILE *fp, + const yyjson_mut_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_write_err tmp_err; + yyjson_alc alc = alc_ptr ? *alc_ptr : YYJSON_DEFAULT_ALC; + u8 *dat; + usize dat_len = 0; + yyjson_mut_val *root = constcast(yyjson_mut_val *)val; + bool suc; + + if (!err) err = &tmp_err; + if (unlikely(!fp)) { + err->msg = "input fp is invalid"; + err->code = YYJSON_WRITE_ERROR_INVALID_PARAMETER; + return false; + } + + dat = (u8 *)yyjson_mut_val_write_opts(root, flg, &alc, &dat_len, err); + if (unlikely(!dat)) return false; + suc = write_dat_to_fp(fp, dat, dat_len, err); + alc.free(alc.ctx, dat); + return suc; +} + +bool yyjson_mut_write_file(const char *path, + const yyjson_mut_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_mut_val *root = doc ? doc->root : NULL; + return yyjson_mut_val_write_file(path, root, flg, alc_ptr, err); +} + +bool yyjson_mut_write_fp(FILE *fp, + const yyjson_mut_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc_ptr, + yyjson_write_err *err) { + yyjson_mut_val *root = doc ? doc->root : NULL; + return yyjson_mut_val_write_fp(fp, root, flg, alc_ptr, err); +} + +#undef has_flg +#undef has_allow +#endif /* YYJSON_DISABLE_WRITER */ + + + +#if !YYJSON_DISABLE_UTILS + +/*============================================================================== + * MARK: - JSON Pointer API (RFC 6901) (Public) + *============================================================================*/ + +/** + Get a token from JSON pointer string. + @param ptr [in] string that points to current token prefix `/` + [out] string that points to next token prefix `/`, or string end + @param end [in] end of the entire JSON Pointer string + @param len [out] unescaped token length + @param esc [out] number of escaped characters in this token + @return head of the token, or NULL if syntax error + */ +static_inline const char *ptr_next_token(const char **ptr, const char *end, + usize *len, usize *esc) { + const char *hdr = *ptr + 1; + const char *cur = hdr; + /* skip unescaped characters */ + while (cur < end && *cur != '/' && *cur != '~') cur++; + if (likely(cur == end || *cur != '~')) { + /* no escaped characters, return */ + *ptr = cur; + *len = (usize)(cur - hdr); + *esc = 0; + return hdr; + } else { + /* handle escaped characters */ + usize esc_num = 0; + while (cur < end && *cur != '/') { + if (*cur++ == '~') { + if (cur == end || (*cur != '0' && *cur != '1')) { + *ptr = cur - 1; + return NULL; + } + esc_num++; + } + } + *ptr = cur; + *len = (usize)(cur - hdr) - esc_num; + *esc = esc_num; + return hdr; + } +} + +/** + Convert token string to index. + @param cur [in] token head + @param len [in] token length + @param idx [out] the index number, or USIZE_MAX if token is '-' + @return true if token is a valid array index + */ +static_inline bool ptr_token_to_idx(const char *cur, usize len, usize *idx) { + const char *end = cur + len; + usize num = 0, add; + if (unlikely(len == 0 || len > USIZE_SAFE_DIG)) return false; + if (*cur == '0') { + if (unlikely(len > 1)) return false; + *idx = 0; + return true; + } + if (*cur == '-') { + if (unlikely(len > 1)) return false; + *idx = USIZE_MAX; + return true; + } + for (; cur < end && (add = (usize)((u8)*cur - (u8)'0')) <= 9; cur++) { + num = num * 10 + add; + } + if (unlikely(num == 0 || cur < end)) return false; + *idx = num; + return true; +} + +/** + Compare JSON key with token. + @param key a string key (yyjson_val or yyjson_mut_val) + @param token a JSON pointer token + @param len unescaped token length + @param esc number of escaped characters in this token + @return true if `str` is equals to `token` + */ +static_inline bool ptr_token_eq(void *key, + const char *token, usize len, usize esc) { + yyjson_val *val = (yyjson_val *)key; + if (unsafe_yyjson_get_len(val) != len) return false; + if (likely(!esc)) { + return memcmp(val->uni.str, token, len) == 0; + } else { + const char *str = val->uni.str; + for (; len-- > 0; token++, str++) { + if (*token == '~') { + if (*str != (*++token == '0' ? '~' : '/')) return false; + } else { + if (*str != *token) return false; + } + } + return true; + } +} + +/** + Get a value from array by token. + @param arr an array, should not be NULL or non-array type + @param token a JSON pointer token + @param len unescaped token length + @param esc number of escaped characters in this token + @return value at index, or NULL if token is not index or index is out of range + */ +static_inline yyjson_val *ptr_arr_get(yyjson_val *arr, const char *token, + usize len, usize esc) { + yyjson_val *val = unsafe_yyjson_get_first(arr); + usize num = unsafe_yyjson_get_len(arr), idx = 0; + if (unlikely(num == 0)) return NULL; + if (unlikely(!ptr_token_to_idx(token, len, &idx))) return NULL; + if (unlikely(idx >= num)) return NULL; + if (unsafe_yyjson_arr_is_flat(arr)) { + return val + idx; + } else { + while (idx-- > 0) val = unsafe_yyjson_get_next(val); + return val; + } +} + +/** + Get a value from object by token. + @param obj [in] an object, should not be NULL or non-object type + @param token [in] a JSON pointer token + @param len [in] unescaped token length + @param esc [in] number of escaped characters in this token + @return value associated with the token, or NULL if no value + */ +static_inline yyjson_val *ptr_obj_get(yyjson_val *obj, const char *token, + usize len, usize esc) { + yyjson_val *key = unsafe_yyjson_get_first(obj); + usize num = unsafe_yyjson_get_len(obj); + if (unlikely(num == 0)) return NULL; + for (; num > 0; num--, key = unsafe_yyjson_get_next(key + 1)) { + if (ptr_token_eq(key, token, len, esc)) return key + 1; + } + return NULL; +} + +/** + Get a value from array by token. + @param arr [in] an array, should not be NULL or non-array type + @param token [in] a JSON pointer token + @param len [in] unescaped token length + @param esc [in] number of escaped characters in this token + @param pre [out] previous (sibling) value of the returned value + @param last [out] whether index is last + @return value at index, or NULL if token is not index or index is out of range + */ +static_inline yyjson_mut_val *ptr_mut_arr_get(yyjson_mut_val *arr, + const char *token, + usize len, usize esc, + yyjson_mut_val **pre, + bool *last) { + yyjson_mut_val *val = (yyjson_mut_val *)arr->uni.ptr; /* last (tail) */ + usize num = unsafe_yyjson_get_len(arr), idx; + if (last) *last = false; + if (pre) *pre = NULL; + if (unlikely(num == 0)) { + if (last && len == 1 && (*token == '0' || *token == '-')) *last = true; + return NULL; + } + if (unlikely(!ptr_token_to_idx(token, len, &idx))) return NULL; + if (last) *last = (idx == num || idx == USIZE_MAX); + if (unlikely(idx >= num)) return NULL; + while (idx-- > 0) val = val->next; + if (pre) *pre = val; + return val->next; +} + +/** + Get a value from object by token. + @param obj [in] an object, should not be NULL or non-object type + @param token [in] a JSON pointer token + @param len [in] unescaped token length + @param esc [in] number of escaped characters in this token + @param pre [out] previous (sibling) key of the returned value's key + @return value associated with the token, or NULL if no value + */ +static_inline yyjson_mut_val *ptr_mut_obj_get(yyjson_mut_val *obj, + const char *token, + usize len, usize esc, + yyjson_mut_val **pre) { + yyjson_mut_val *pre_key = (yyjson_mut_val *)obj->uni.ptr, *key; + usize num = unsafe_yyjson_get_len(obj); + if (pre) *pre = NULL; + if (unlikely(num == 0)) return NULL; + for (; num > 0; num--, pre_key = key) { + key = pre_key->next->next; + if (ptr_token_eq(key, token, len, esc)) { + if (pre) *pre = pre_key; + return key->next; + } + } + return NULL; +} + +/** + Create a string value with JSON pointer token. + @param token [in] a JSON pointer token + @param len [in] unescaped token length + @param esc [in] number of escaped characters in this token + @param doc [in] used for memory allocation when creating value + @return new string value, or NULL if memory allocation failed + */ +static_inline yyjson_mut_val *ptr_new_key(const char *token, + usize len, usize esc, + yyjson_mut_doc *doc) { + const char *src = token; + if (likely(!esc)) { + return yyjson_mut_strncpy(doc, src, len); + } else { + const char *end = src + len + esc; + char *dst = unsafe_yyjson_mut_str_alc(doc, len + esc); + char *str = dst; + if (unlikely(!dst)) return NULL; + for (; src < end; src++, dst++) { + if (*src != '~') *dst = *src; + else *dst = (*++src == '0' ? '~' : '/'); + } + *dst = '\0'; + return yyjson_mut_strn(doc, str, len); + } +} + +/* macros for yyjson_ptr */ +#define return_err(_ret, _code, _pos, _msg) do { \ + if (err) { \ + err->code = YYJSON_PTR_ERR_##_code; \ + err->msg = _msg; \ + err->pos = (usize)(_pos); \ + } \ + return _ret; \ +} while (false) + +#define return_err_resolve(_ret, _pos) \ + return_err(_ret, RESOLVE, _pos, "JSON pointer cannot be resolved") +#define return_err_syntax(_ret, _pos) \ + return_err(_ret, SYNTAX, _pos, "invalid escaped character") +#define return_err_alloc(_ret) \ + return_err(_ret, MEMORY_ALLOCATION, 0, "failed to create value") + +yyjson_val *unsafe_yyjson_ptr_getx(yyjson_val *val, + const char *ptr, size_t ptr_len, + yyjson_ptr_err *err) { + + const char *hdr = ptr, *end = ptr + ptr_len, *token; + usize len, esc; + yyjson_type type; + + while (true) { + token = ptr_next_token(&ptr, end, &len, &esc); + if (unlikely(!token)) return_err_syntax(NULL, ptr - hdr); + type = unsafe_yyjson_get_type(val); + if (type == YYJSON_TYPE_OBJ) { + val = ptr_obj_get(val, token, len, esc); + } else if (type == YYJSON_TYPE_ARR) { + val = ptr_arr_get(val, token, len, esc); + } else { + val = NULL; + } + if (!val) return_err_resolve(NULL, token - hdr); + if (ptr == end) return val; + } +} + +yyjson_mut_val *unsafe_yyjson_mut_ptr_getx( + yyjson_mut_val *val, const char *ptr, size_t ptr_len, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { + + const char *hdr = ptr, *end = ptr + ptr_len, *token; + usize len, esc; + yyjson_mut_val *ctn, *pre = NULL; + yyjson_type type; + bool idx_is_last = false; + + while (true) { + token = ptr_next_token(&ptr, end, &len, &esc); + if (unlikely(!token)) return_err_syntax(NULL, ptr - hdr); + ctn = val; + type = unsafe_yyjson_get_type(val); + if (type == YYJSON_TYPE_OBJ) { + val = ptr_mut_obj_get(val, token, len, esc, &pre); + } else if (type == YYJSON_TYPE_ARR) { + val = ptr_mut_arr_get(val, token, len, esc, &pre, &idx_is_last); + } else { + val = NULL; + } + if (ctx && (ptr == end)) { + if (type == YYJSON_TYPE_OBJ || + (type == YYJSON_TYPE_ARR && (val || idx_is_last))) { + ctx->ctn = ctn; + ctx->pre = pre; + } + } + if (!val) return_err_resolve(NULL, token - hdr); + if (ptr == end) return val; + } +} + +bool unsafe_yyjson_mut_ptr_putx( + yyjson_mut_val *val, const char *ptr, size_t ptr_len, + yyjson_mut_val *new_val, yyjson_mut_doc *doc, bool create_parent, + bool insert_new, yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { + + const char *hdr = ptr, *end = ptr + ptr_len, *token; + usize token_len, esc, ctn_len; + yyjson_mut_val *ctn, *key, *pre = NULL; + yyjson_mut_val *sep_ctn = NULL, *sep_key = NULL, *sep_val = NULL; + yyjson_type ctn_type; + bool idx_is_last = false; + + /* skip exist parent nodes */ + while (true) { + token = ptr_next_token(&ptr, end, &token_len, &esc); + if (unlikely(!token)) return_err_syntax(false, ptr - hdr); + ctn = val; + ctn_type = unsafe_yyjson_get_type(ctn); + if (ctn_type == YYJSON_TYPE_OBJ) { + val = ptr_mut_obj_get(ctn, token, token_len, esc, &pre); + } else if (ctn_type == YYJSON_TYPE_ARR) { + val = ptr_mut_arr_get(ctn, token, token_len, esc, &pre, + &idx_is_last); + } else return_err_resolve(false, token - hdr); + if (!val) break; + if (ptr == end) break; /* is last token */ + } + + /* create parent nodes if not exist */ + if (unlikely(ptr != end)) { /* not last token */ + if (!create_parent) return_err_resolve(false, token - hdr); + + /* add value at last index if container is array */ + if (ctn_type == YYJSON_TYPE_ARR) { + if (!idx_is_last || !insert_new) { + return_err_resolve(false, token - hdr); + } + val = yyjson_mut_obj(doc); + if (!val) return_err_alloc(false); + + /* delay attaching until all operations are completed */ + sep_ctn = ctn; + sep_key = NULL; + sep_val = val; + + /* move to next token */ + ctn = val; + val = NULL; + ctn_type = YYJSON_TYPE_OBJ; + token = ptr_next_token(&ptr, end, &token_len, &esc); + if (unlikely(!token)) return_err_resolve(false, token - hdr); + } + + /* container is object, create parent nodes */ + while (ptr != end) { /* not last token */ + key = ptr_new_key(token, token_len, esc, doc); + if (!key) return_err_alloc(false); + val = yyjson_mut_obj(doc); + if (!val) return_err_alloc(false); + + /* delay attaching until all operations are completed */ + if (!sep_ctn) { + sep_ctn = ctn; + sep_key = key; + sep_val = val; + } else { + yyjson_mut_obj_add(ctn, key, val); + } + + /* move to next token */ + ctn = val; + val = NULL; + token = ptr_next_token(&ptr, end, &token_len, &esc); + if (unlikely(!token)) return_err_syntax(false, ptr - hdr); + } + } + + /* JSON pointer is resolved, insert or replace target value */ + ctn_len = unsafe_yyjson_get_len(ctn); + if (ctn_type == YYJSON_TYPE_OBJ) { + if (ctx) ctx->ctn = ctn; + if (!val || insert_new) { + /* insert new key-value pair */ + key = ptr_new_key(token, token_len, esc, doc); + if (unlikely(!key)) return_err_alloc(false); + if (ctx) ctx->pre = ctn_len ? (yyjson_mut_val *)ctn->uni.ptr : key; + unsafe_yyjson_mut_obj_add(ctn, key, new_val, ctn_len); + } else { + /* replace exist value */ + key = pre->next->next; + if (ctx) ctx->pre = pre; + if (ctx) ctx->old = val; + yyjson_mut_obj_put(ctn, key, new_val); + } + } else { + /* array */ + if (ctx && (val || idx_is_last)) ctx->ctn = ctn; + if (insert_new) { + /* append new value */ + if (val) { + pre->next = new_val; + new_val->next = val; + if (ctx) ctx->pre = pre; + unsafe_yyjson_set_len(ctn, ctn_len + 1); + } else if (idx_is_last) { + if (ctx) ctx->pre = ctn_len ? + (yyjson_mut_val *)ctn->uni.ptr : new_val; + yyjson_mut_arr_append(ctn, new_val); + } else { + return_err_resolve(false, token - hdr); + } + } else { + /* replace exist value */ + if (!val) return_err_resolve(false, token - hdr); + if (ctn_len > 1) { + new_val->next = val->next; + pre->next = new_val; + if (ctn->uni.ptr == val) ctn->uni.ptr = new_val; + } else { + new_val->next = new_val; + ctn->uni.ptr = new_val; + pre = new_val; + } + if (ctx) ctx->pre = pre; + if (ctx) ctx->old = val; + } + } + + /* all operations are completed, attach the new components to the target */ + if (unlikely(sep_ctn)) { + if (sep_key) yyjson_mut_obj_add(sep_ctn, sep_key, sep_val); + else yyjson_mut_arr_append(sep_ctn, sep_val); + } + return true; +} + +yyjson_mut_val *unsafe_yyjson_mut_ptr_replacex( + yyjson_mut_val *val, const char *ptr, size_t len, yyjson_mut_val *new_val, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { + + yyjson_mut_val *cur_val; + yyjson_ptr_ctx cur_ctx; + memset(&cur_ctx, 0, sizeof(cur_ctx)); + if (!ctx) ctx = &cur_ctx; + cur_val = unsafe_yyjson_mut_ptr_getx(val, ptr, len, ctx, err); + if (!cur_val) return NULL; + + if (yyjson_mut_is_obj(ctx->ctn)) { + yyjson_mut_val *key = ctx->pre->next->next; + yyjson_mut_obj_put(ctx->ctn, key, new_val); + } else { + yyjson_ptr_ctx_replace(ctx, new_val); + } + ctx->old = cur_val; + return cur_val; +} + +yyjson_mut_val *unsafe_yyjson_mut_ptr_removex( + yyjson_mut_val *val, const char *ptr, size_t len, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { + + yyjson_mut_val *cur_val; + yyjson_ptr_ctx cur_ctx; + memset(&cur_ctx, 0, sizeof(cur_ctx)); + if (!ctx) ctx = &cur_ctx; + cur_val = unsafe_yyjson_mut_ptr_getx(val, ptr, len, ctx, err); + if (cur_val) { + if (yyjson_mut_is_obj(ctx->ctn)) { + yyjson_mut_val *key = ctx->pre->next->next; + yyjson_mut_obj_put(ctx->ctn, key, NULL); + } else { + yyjson_ptr_ctx_remove(ctx); + } + ctx->pre = NULL; + ctx->old = cur_val; + } + return cur_val; +} + +/* macros for yyjson_ptr */ +#undef return_err +#undef return_err_resolve +#undef return_err_syntax +#undef return_err_alloc + + + +/*============================================================================== + * MARK: - JSON Patch API (RFC 6902) (Public) + *============================================================================*/ + +/* JSON Patch operation */ +typedef enum patch_op { + PATCH_OP_ADD, /* path, value */ + PATCH_OP_REMOVE, /* path */ + PATCH_OP_REPLACE, /* path, value */ + PATCH_OP_MOVE, /* from, path */ + PATCH_OP_COPY, /* from, path */ + PATCH_OP_TEST, /* path, value */ + PATCH_OP_NONE /* invalid */ +} patch_op; + +static patch_op patch_op_get(yyjson_val *op) { + const char *str = op->uni.str; + switch (unsafe_yyjson_get_len(op)) { + case 3: + if (!memcmp(str, "add", 3)) return PATCH_OP_ADD; + return PATCH_OP_NONE; + case 4: + if (!memcmp(str, "move", 4)) return PATCH_OP_MOVE; + if (!memcmp(str, "copy", 4)) return PATCH_OP_COPY; + if (!memcmp(str, "test", 4)) return PATCH_OP_TEST; + return PATCH_OP_NONE; + case 6: + if (!memcmp(str, "remove", 6)) return PATCH_OP_REMOVE; + return PATCH_OP_NONE; + case 7: + if (!memcmp(str, "replace", 7)) return PATCH_OP_REPLACE; + return PATCH_OP_NONE; + default: + return PATCH_OP_NONE; + } +} + +/* macros for yyjson_patch */ +#define return_err(_code, _msg) do { \ + if (err->ptr.code == YYJSON_PTR_ERR_MEMORY_ALLOCATION) { \ + err->code = YYJSON_PATCH_ERROR_MEMORY_ALLOCATION; \ + err->msg = _msg; \ + memset(&err->ptr, 0, sizeof(yyjson_ptr_err)); \ + } else { \ + err->code = YYJSON_PATCH_ERROR_##_code; \ + err->msg = _msg; \ + err->idx = iter.idx ? iter.idx - 1 : 0; \ + } \ + return NULL; \ +} while (false) + +#define return_err_copy() \ + return_err(MEMORY_ALLOCATION, "failed to copy value") +#define return_err_key(_key) \ + return_err(MISSING_KEY, "missing key " _key) +#define return_err_val(_key) \ + return_err(INVALID_MEMBER, "invalid member " _key) + +#define ptr_get(_ptr) yyjson_mut_ptr_getx( \ + root, _ptr->uni.str, _ptr##_len, NULL, &err->ptr) +#define ptr_add(_ptr, _val) yyjson_mut_ptr_addx( \ + root, _ptr->uni.str, _ptr##_len, _val, doc, false, NULL, &err->ptr) +#define ptr_remove(_ptr) yyjson_mut_ptr_removex( \ + root, _ptr->uni.str, _ptr##_len, NULL, &err->ptr) +#define ptr_replace(_ptr, _val)yyjson_mut_ptr_replacex( \ + root, _ptr->uni.str, _ptr##_len, _val, NULL, &err->ptr) + +yyjson_mut_val *yyjson_patch(yyjson_mut_doc *doc, + yyjson_val *orig, + yyjson_val *patch, + yyjson_patch_err *err) { + + yyjson_mut_val *root; + yyjson_val *obj; + yyjson_arr_iter iter; + yyjson_patch_err err_tmp; + if (!err) err = &err_tmp; + memset(err, 0, sizeof(*err)); + memset(&iter, 0, sizeof(iter)); + + if (unlikely(!doc || !orig || !patch)) { + return_err(INVALID_PARAMETER, "input parameter is NULL"); + } + if (unlikely(!yyjson_is_arr(patch))) { + return_err(INVALID_PARAMETER, "input patch is not array"); + } + root = yyjson_val_mut_copy(doc, orig); + if (unlikely(!root)) return_err_copy(); + + /* iterate through the patch array */ + yyjson_arr_iter_init(patch, &iter); + while ((obj = yyjson_arr_iter_next(&iter))) { + patch_op op_enum; + yyjson_val *op, *path, *from = NULL, *value; + yyjson_mut_val *val = NULL, *test; + usize path_len, from_len = 0; + if (unlikely(!unsafe_yyjson_is_obj(obj))) { + return_err(INVALID_OPERATION, "JSON patch operation is not object"); + } + + /* get required member: op */ + op = yyjson_obj_get(obj, "op"); + if (unlikely(!op)) return_err_key("`op`"); + if (unlikely(!yyjson_is_str(op))) return_err_val("`op`"); + op_enum = patch_op_get(op); + + /* get required member: path */ + path = yyjson_obj_get(obj, "path"); + if (unlikely(!path)) return_err_key("`path`"); + if (unlikely(!yyjson_is_str(path))) return_err_val("`path`"); + path_len = unsafe_yyjson_get_len(path); + + /* get required member: value, from */ + switch ((int)op_enum) { + case PATCH_OP_ADD: case PATCH_OP_REPLACE: case PATCH_OP_TEST: + value = yyjson_obj_get(obj, "value"); + if (unlikely(!value)) return_err_key("`value`"); + val = yyjson_val_mut_copy(doc, value); + if (unlikely(!val)) return_err_copy(); + break; + case PATCH_OP_MOVE: case PATCH_OP_COPY: + from = yyjson_obj_get(obj, "from"); + if (unlikely(!from)) return_err_key("`from`"); + if (unlikely(!yyjson_is_str(from))) return_err_val("`from`"); + from_len = unsafe_yyjson_get_len(from); + break; + default: + break; + } + + /* perform an operation */ + switch ((int)op_enum) { + case PATCH_OP_ADD: /* add(path, val) */ + if (unlikely(path_len == 0)) { root = val; break; } + if (unlikely(!ptr_add(path, val))) { + return_err(POINTER, "failed to add `path`"); + } + break; + case PATCH_OP_REMOVE: /* remove(path) */ + if (unlikely(!ptr_remove(path))) { + return_err(POINTER, "failed to remove `path`"); + } + break; + case PATCH_OP_REPLACE: /* replace(path, val) */ + if (unlikely(path_len == 0)) { root = val; break; } + if (unlikely(!ptr_replace(path, val))) { + return_err(POINTER, "failed to replace `path`"); + } + break; + case PATCH_OP_MOVE: /* val = remove(from), add(path, val) */ + if (unlikely(from_len == 0 && path_len == 0)) break; + val = ptr_remove(from); + if (unlikely(!val)) { + return_err(POINTER, "failed to remove `from`"); + } + if (unlikely(path_len == 0)) { root = val; break; } + if (unlikely(!ptr_add(path, val))) { + return_err(POINTER, "failed to add `path`"); + } + break; + case PATCH_OP_COPY: /* val = get(from).copy, add(path, val) */ + val = ptr_get(from); + if (unlikely(!val)) { + return_err(POINTER, "failed to get `from`"); + } + if (unlikely(path_len == 0)) { root = val; break; } + val = yyjson_mut_val_mut_copy(doc, val); + if (unlikely(!val)) return_err_copy(); + if (unlikely(!ptr_add(path, val))) { + return_err(POINTER, "failed to add `path`"); + } + break; + case PATCH_OP_TEST: /* test = get(path), test.eq(val) */ + test = ptr_get(path); + if (unlikely(!test)) { + return_err(POINTER, "failed to get `path`"); + } + if (unlikely(!yyjson_mut_equals(val, test))) { + return_err(EQUAL, "failed to test equal"); + } + break; + default: + return_err(INVALID_MEMBER, "unsupported `op`"); + } + } + return root; +} + +yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc, + yyjson_mut_val *orig, + yyjson_mut_val *patch, + yyjson_patch_err *err) { + yyjson_mut_val *root, *obj; + yyjson_mut_arr_iter iter; + yyjson_patch_err err_tmp; + if (!err) err = &err_tmp; + memset(err, 0, sizeof(*err)); + memset(&iter, 0, sizeof(iter)); + + if (unlikely(!doc || !orig || !patch)) { + return_err(INVALID_PARAMETER, "input parameter is NULL"); + } + if (unlikely(!yyjson_mut_is_arr(patch))) { + return_err(INVALID_PARAMETER, "input patch is not array"); + } + root = yyjson_mut_val_mut_copy(doc, orig); + if (unlikely(!root)) return_err_copy(); + + /* iterate through the patch array */ + yyjson_mut_arr_iter_init(patch, &iter); + while ((obj = yyjson_mut_arr_iter_next(&iter))) { + patch_op op_enum; + yyjson_mut_val *op, *path, *from = NULL, *value; + yyjson_mut_val *val = NULL, *test; + usize path_len, from_len = 0; + if (!unsafe_yyjson_is_obj(obj)) { + return_err(INVALID_OPERATION, "JSON patch operation is not object"); + } + + /* get required member: op */ + op = yyjson_mut_obj_get(obj, "op"); + if (unlikely(!op)) return_err_key("`op`"); + if (unlikely(!yyjson_mut_is_str(op))) return_err_val("`op`"); + op_enum = patch_op_get((yyjson_val *)(void *)op); + + /* get required member: path */ + path = yyjson_mut_obj_get(obj, "path"); + if (unlikely(!path)) return_err_key("`path`"); + if (unlikely(!yyjson_mut_is_str(path))) return_err_val("`path`"); + path_len = unsafe_yyjson_get_len(path); + + /* get required member: value, from */ + switch ((int)op_enum) { + case PATCH_OP_ADD: case PATCH_OP_REPLACE: case PATCH_OP_TEST: + value = yyjson_mut_obj_get(obj, "value"); + if (unlikely(!value)) return_err_key("`value`"); + val = yyjson_mut_val_mut_copy(doc, value); + if (unlikely(!val)) return_err_copy(); + break; + case PATCH_OP_MOVE: case PATCH_OP_COPY: + from = yyjson_mut_obj_get(obj, "from"); + if (unlikely(!from)) return_err_key("`from`"); + if (unlikely(!yyjson_mut_is_str(from))) { + return_err_val("`from`"); + } + from_len = unsafe_yyjson_get_len(from); + break; + default: + break; + } + + /* perform an operation */ + switch ((int)op_enum) { + case PATCH_OP_ADD: /* add(path, val) */ + if (unlikely(path_len == 0)) { root = val; break; } + if (unlikely(!ptr_add(path, val))) { + return_err(POINTER, "failed to add `path`"); + } + break; + case PATCH_OP_REMOVE: /* remove(path) */ + if (unlikely(!ptr_remove(path))) { + return_err(POINTER, "failed to remove `path`"); + } + break; + case PATCH_OP_REPLACE: /* replace(path, val) */ + if (unlikely(path_len == 0)) { root = val; break; } + if (unlikely(!ptr_replace(path, val))) { + return_err(POINTER, "failed to replace `path`"); + } + break; + case PATCH_OP_MOVE: /* val = remove(from), add(path, val) */ + if (unlikely(from_len == 0 && path_len == 0)) break; + val = ptr_remove(from); + if (unlikely(!val)) { + return_err(POINTER, "failed to remove `from`"); + } + if (unlikely(path_len == 0)) { root = val; break; } + if (unlikely(!ptr_add(path, val))) { + return_err(POINTER, "failed to add `path`"); + } + break; + case PATCH_OP_COPY: /* val = get(from).copy, add(path, val) */ + val = ptr_get(from); + if (unlikely(!val)) { + return_err(POINTER, "failed to get `from`"); + } + if (unlikely(path_len == 0)) { root = val; break; } + val = yyjson_mut_val_mut_copy(doc, val); + if (unlikely(!val)) return_err_copy(); + if (unlikely(!ptr_add(path, val))) { + return_err(POINTER, "failed to add `path`"); + } + break; + case PATCH_OP_TEST: /* test = get(path), test.eq(val) */ + test = ptr_get(path); + if (unlikely(!test)) { + return_err(POINTER, "failed to get `path`"); + } + if (unlikely(!yyjson_mut_equals(val, test))) { + return_err(EQUAL, "failed to test equal"); + } + break; + default: + return_err(INVALID_MEMBER, "unsupported `op`"); + } + } + return root; +} + +/* macros for yyjson_patch */ +#undef return_err +#undef return_err_copy +#undef return_err_key +#undef return_err_val +#undef ptr_get +#undef ptr_add +#undef ptr_remove +#undef ptr_replace + + + +/*============================================================================== + * MARK: - JSON Merge-Patch API (RFC 7386) (Public) + *============================================================================*/ + +yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, + yyjson_val *orig, + yyjson_val *patch) { + usize idx, max; + yyjson_val *key, *orig_val, *patch_val, local_orig; + yyjson_mut_val *builder, *mut_key, *mut_val, *merged_val; + + if (unlikely(!yyjson_is_obj(patch))) { + return yyjson_val_mut_copy(doc, patch); + } + + builder = yyjson_mut_obj(doc); + if (unlikely(!builder)) return NULL; + + memset(&local_orig, 0, sizeof(local_orig)); + if (!yyjson_is_obj(orig)) { + orig = &local_orig; + orig->tag = builder->tag; + orig->uni = builder->uni; + } + + /* If orig is contributing, copy any items not modified by the patch */ + if (orig != &local_orig) { + yyjson_obj_foreach(orig, idx, max, key, orig_val) { + patch_val = yyjson_obj_getn(patch, + unsafe_yyjson_get_str(key), + unsafe_yyjson_get_len(key)); + if (!patch_val) { + mut_key = yyjson_val_mut_copy(doc, key); + mut_val = yyjson_val_mut_copy(doc, orig_val); + if (!yyjson_mut_obj_add(builder, mut_key, mut_val)) return NULL; + } + } + } + + /* Merge items modified by the patch. */ + yyjson_obj_foreach(patch, idx, max, key, patch_val) { + /* null indicates the field is removed. */ + if (unsafe_yyjson_is_null(patch_val)) { + continue; + } + mut_key = yyjson_val_mut_copy(doc, key); + orig_val = yyjson_obj_getn(orig, + unsafe_yyjson_get_str(key), + unsafe_yyjson_get_len(key)); + merged_val = yyjson_merge_patch(doc, orig_val, patch_val); + if (!yyjson_mut_obj_add(builder, mut_key, merged_val)) return NULL; + } + + return builder; +} + +yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc, + yyjson_mut_val *orig, + yyjson_mut_val *patch) { + usize idx, max; + yyjson_mut_val *key, *orig_val, *patch_val, local_orig; + yyjson_mut_val *builder, *mut_key, *mut_val, *merged_val; + + if (unlikely(!yyjson_mut_is_obj(patch))) { + return yyjson_mut_val_mut_copy(doc, patch); + } + + builder = yyjson_mut_obj(doc); + if (unlikely(!builder)) return NULL; + + memset(&local_orig, 0, sizeof(local_orig)); + if (!yyjson_mut_is_obj(orig)) { + orig = &local_orig; + orig->tag = builder->tag; + orig->uni = builder->uni; + } + + /* If orig is contributing, copy any items not modified by the patch */ + if (orig != &local_orig) { + yyjson_mut_obj_foreach(orig, idx, max, key, orig_val) { + patch_val = yyjson_mut_obj_getn(patch, + unsafe_yyjson_get_str(key), + unsafe_yyjson_get_len(key)); + if (!patch_val) { + mut_key = yyjson_mut_val_mut_copy(doc, key); + mut_val = yyjson_mut_val_mut_copy(doc, orig_val); + if (!yyjson_mut_obj_add(builder, mut_key, mut_val)) return NULL; + } + } + } + + /* Merge items modified by the patch. */ + yyjson_mut_obj_foreach(patch, idx, max, key, patch_val) { + /* null indicates the field is removed. */ + if (unsafe_yyjson_is_null(patch_val)) { + continue; + } + mut_key = yyjson_mut_val_mut_copy(doc, key); + orig_val = yyjson_mut_obj_getn(orig, + unsafe_yyjson_get_str(key), + unsafe_yyjson_get_len(key)); + merged_val = yyjson_mut_merge_patch(doc, orig_val, patch_val); + if (!yyjson_mut_obj_add(builder, mut_key, merged_val)) return NULL; + } + + return builder; +} + +#endif /* YYJSON_DISABLE_UTILS */ diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/yyjson.h b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/yyjson.h new file mode 100644 index 0000000..5eb6d46 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/sources/vendor/yyjson/yyjson.h @@ -0,0 +1,8230 @@ +/*============================================================================== + Copyright (c) 2020 YaoYuan <[email protected]> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + *============================================================================*/ + +/** + @file yyjson.h + @date 2019-03-09 + @author YaoYuan + */ + +#ifndef YYJSON_H +#define YYJSON_H + + + +/*============================================================================== + * MARK: - Header Files + *============================================================================*/ + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <limits.h> +#include <string.h> +#include <float.h> + + + +/*============================================================================== + * MARK: - Compile-time Options + *============================================================================*/ + +/* + Define as 1 to disable JSON reader at compile-time. + This disables functions with "read" in their name. + Reduces binary size by about 60%. + */ +#ifndef YYJSON_DISABLE_READER +#endif + +/* + Define as 1 to disable JSON writer at compile-time. + This disables functions with "write" in their name. + Reduces binary size by about 30%. + */ +#ifndef YYJSON_DISABLE_WRITER +#endif + +/* + Define as 1 to disable JSON incremental reader at compile-time. + This disables functions with "incr" in their name. + */ +#ifndef YYJSON_DISABLE_INCR_READER +#endif + +/* + Define as 1 to disable JSON Pointer, JSON Patch and JSON Merge Patch supports. + This disables functions with "ptr" or "patch" in their name. + */ +#ifndef YYJSON_DISABLE_UTILS +#endif + +/* + Define as 1 to disable the fast floating-point number conversion in yyjson. + Libc's `strtod/snprintf` will be used instead. + + This reduces binary size by about 30%, but significantly slows down the + floating-point read/write speed. + */ +#ifndef YYJSON_DISABLE_FAST_FP_CONV +#endif + +/* + Define as 1 to disable non-standard JSON features support at compile-time, + such as YYJSON_READ_ALLOW_XXX and YYJSON_WRITE_ALLOW_XXX. + + This reduces binary size by about 10%, and slightly improves performance. + */ +#ifndef YYJSON_DISABLE_NON_STANDARD +#endif + +/* + Define as 1 to disable UTF-8 validation at compile-time. + + Use this if all input strings are guaranteed to be valid UTF-8 + (e.g. language-level String types are already validated). + + Disabling UTF-8 validation improves performance for non-ASCII strings by about + 3% to 7%. + + Note: If this flag is enabled while passing illegal UTF-8 strings, + the following errors may occur: + - Escaped characters may be ignored when parsing JSON strings. + - Ending quotes may be ignored when parsing JSON strings, causing the + string to merge with the next value. + - When serializing with `yyjson_mut_val`, the string's end may be accessed + out of bounds, potentially causing a segmentation fault. + */ +#ifndef YYJSON_DISABLE_UTF8_VALIDATION +#endif + +/* + Define as 1 to improve performance on architectures that do not support + unaligned memory access. + + Normally, this does not need to be set manually. See the C file for details. + */ +#ifndef YYJSON_DISABLE_UNALIGNED_MEMORY_ACCESS +#endif + +/* Define as 1 to export symbols when building this library as a Windows DLL. */ +#ifndef YYJSON_EXPORTS +#endif + +/* Define as 1 to import symbols when using this library as a Windows DLL. */ +#ifndef YYJSON_IMPORTS +#endif + +/* Define as 1 to include <stdint.h> for compilers without C99 support. */ +#ifndef YYJSON_HAS_STDINT_H +#endif + +/* Define as 1 to include <stdbool.h> for compilers without C99 support. */ +#ifndef YYJSON_HAS_STDBOOL_H +#endif + + + +/*============================================================================== + * MARK: - Compiler Macros + *============================================================================*/ + +/** compiler version (MSVC) */ +#ifdef _MSC_VER +# define YYJSON_MSC_VER _MSC_VER +#else +# define YYJSON_MSC_VER 0 +#endif + +/** compiler version (GCC) */ +#ifdef __GNUC__ +# define YYJSON_GCC_VER __GNUC__ +# if defined(__GNUC_PATCHLEVEL__) +# define yyjson_gcc_available(major, minor, patch) \ + ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) \ + >= (major * 10000 + minor * 100 + patch)) +# else +# define yyjson_gcc_available(major, minor, patch) \ + ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100) \ + >= (major * 10000 + minor * 100 + patch)) +# endif +#else +# define YYJSON_GCC_VER 0 +# define yyjson_gcc_available(major, minor, patch) 0 +#endif + +/** real gcc check */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + !defined(__clang__) && !defined(__llvm__) && \ + !defined(__INTEL_COMPILER) && !defined(__ICC) && \ + !defined(__NVCC__) && !defined(__PGI) && !defined(__TINYC__) +# define YYJSON_IS_REAL_GCC 1 +#else +# define YYJSON_IS_REAL_GCC 0 +#endif + +/** C version (STDC) */ +#if defined(__STDC__) && (__STDC__ >= 1) && defined(__STDC_VERSION__) +# define YYJSON_STDC_VER __STDC_VERSION__ +#else +# define YYJSON_STDC_VER 0 +#endif + +/** C++ version */ +#if defined(__cplusplus) +# define YYJSON_CPP_VER __cplusplus +#else +# define YYJSON_CPP_VER 0 +#endif + +/** compiler builtin check (since gcc 10.0, clang 2.6, icc 2021) */ +#ifndef yyjson_has_builtin +# ifdef __has_builtin +# define yyjson_has_builtin(x) __has_builtin(x) +# else +# define yyjson_has_builtin(x) 0 +# endif +#endif + +/** compiler attribute check (since gcc 5.0, clang 2.9, icc 17) */ +#ifndef yyjson_has_attribute +# ifdef __has_attribute +# define yyjson_has_attribute(x) __has_attribute(x) +# else +# define yyjson_has_attribute(x) 0 +# endif +#endif + +/** compiler feature check (since clang 2.6, icc 17) */ +#ifndef yyjson_has_feature +# ifdef __has_feature +# define yyjson_has_feature(x) __has_feature(x) +# else +# define yyjson_has_feature(x) 0 +# endif +#endif + +/** include check (since gcc 5.0, clang 2.7, icc 16, msvc 2017 15.3) */ +#ifndef yyjson_has_include +# ifdef __has_include +# define yyjson_has_include(x) __has_include(x) +# else +# define yyjson_has_include(x) 0 +# endif +#endif + +/** inline for compiler */ +#ifndef yyjson_inline +# if YYJSON_MSC_VER >= 1200 +# define yyjson_inline __forceinline +# elif defined(_MSC_VER) +# define yyjson_inline __inline +# elif yyjson_has_attribute(always_inline) || YYJSON_GCC_VER >= 4 +# define yyjson_inline __inline__ __attribute__((always_inline)) +# elif defined(__clang__) || defined(__GNUC__) +# define yyjson_inline __inline__ +# elif defined(__cplusplus) || YYJSON_STDC_VER >= 199901L +# define yyjson_inline inline +# else +# define yyjson_inline +# endif +#endif + +/** noinline for compiler */ +#ifndef yyjson_noinline +# if YYJSON_MSC_VER >= 1400 +# define yyjson_noinline __declspec(noinline) +# elif yyjson_has_attribute(noinline) || YYJSON_GCC_VER >= 4 +# define yyjson_noinline __attribute__((noinline)) +# else +# define yyjson_noinline +# endif +#endif + +/** align for compiler */ +#ifndef yyjson_align +# if YYJSON_MSC_VER >= 1300 +# define yyjson_align(x) __declspec(align(x)) +# elif yyjson_has_attribute(aligned) || defined(__GNUC__) +# define yyjson_align(x) __attribute__((aligned(x))) +# elif YYJSON_CPP_VER >= 201103L +# define yyjson_align(x) alignas(x) +# else +# define yyjson_align(x) +# endif +#endif + +/** likely for compiler */ +#ifndef yyjson_likely +# if yyjson_has_builtin(__builtin_expect) || \ + (YYJSON_GCC_VER >= 4 && YYJSON_GCC_VER != 5) +# define yyjson_likely(expr) __builtin_expect(!!(expr), 1) +# else +# define yyjson_likely(expr) (expr) +# endif +#endif + +/** unlikely for compiler */ +#ifndef yyjson_unlikely +# if yyjson_has_builtin(__builtin_expect) || \ + (YYJSON_GCC_VER >= 4 && YYJSON_GCC_VER != 5) +# define yyjson_unlikely(expr) __builtin_expect(!!(expr), 0) +# else +# define yyjson_unlikely(expr) (expr) +# endif +#endif + +/** compile-time constant check for compiler */ +#ifndef yyjson_constant_p +# if yyjson_has_builtin(__builtin_constant_p) || (YYJSON_GCC_VER >= 3) +# define YYJSON_HAS_CONSTANT_P 1 +# define yyjson_constant_p(value) __builtin_constant_p(value) +# else +# define YYJSON_HAS_CONSTANT_P 0 +# define yyjson_constant_p(value) 0 +# endif +#endif + +/** deprecate warning */ +#ifndef yyjson_deprecated +# if YYJSON_MSC_VER >= 1400 +# define yyjson_deprecated(msg) __declspec(deprecated(msg)) +# elif yyjson_has_feature(attribute_deprecated_with_message) || \ + (YYJSON_GCC_VER > 4 || (YYJSON_GCC_VER == 4 && __GNUC_MINOR__ >= 5)) +# define yyjson_deprecated(msg) __attribute__((deprecated(msg))) +# elif YYJSON_GCC_VER >= 3 +# define yyjson_deprecated(msg) __attribute__((deprecated)) +# else +# define yyjson_deprecated(msg) +# endif +#endif + +/** function export */ +#ifndef yyjson_api +# if defined(_WIN32) +# if defined(YYJSON_EXPORTS) && YYJSON_EXPORTS +# define yyjson_api __declspec(dllexport) +# elif defined(YYJSON_IMPORTS) && YYJSON_IMPORTS +# define yyjson_api __declspec(dllimport) +# else +# define yyjson_api +# endif +# elif yyjson_has_attribute(visibility) || YYJSON_GCC_VER >= 4 +# define yyjson_api __attribute__((visibility("default"))) +# else +# define yyjson_api +# endif +#endif + +/** inline function export */ +#ifndef yyjson_api_inline +# define yyjson_api_inline static yyjson_inline +#endif + +/** stdint (C89 compatible) */ +#if (defined(YYJSON_HAS_STDINT_H) && YYJSON_HAS_STDINT_H) || \ + YYJSON_MSC_VER >= 1600 || YYJSON_STDC_VER >= 199901L || \ + defined(_STDINT_H) || defined(_STDINT_H_) || \ + defined(__CLANG_STDINT_H) || defined(_STDINT_H_INCLUDED) || \ + yyjson_has_include(<stdint.h>) +# include <stdint.h> +#elif defined(_MSC_VER) +# if _MSC_VER < 1300 + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; +# else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; +# endif +#else +# if UCHAR_MAX == 0xFFU + typedef signed char int8_t; + typedef unsigned char uint8_t; +# else +# error cannot find 8-bit integer type +# endif +# if USHRT_MAX == 0xFFFFU + typedef unsigned short uint16_t; + typedef signed short int16_t; +# elif UINT_MAX == 0xFFFFU + typedef unsigned int uint16_t; + typedef signed int int16_t; +# else +# error cannot find 16-bit integer type +# endif +# if UINT_MAX == 0xFFFFFFFFUL + typedef unsigned int uint32_t; + typedef signed int int32_t; +# elif ULONG_MAX == 0xFFFFFFFFUL + typedef unsigned long uint32_t; + typedef signed long int32_t; +# elif USHRT_MAX == 0xFFFFFFFFUL + typedef unsigned short uint32_t; + typedef signed short int32_t; +# else +# error cannot find 32-bit integer type +# endif +# if defined(__INT64_TYPE__) && defined(__UINT64_TYPE__) + typedef __INT64_TYPE__ int64_t; + typedef __UINT64_TYPE__ uint64_t; +# elif defined(__GNUC__) || defined(__clang__) +# if !defined(_SYS_TYPES_H) && !defined(__int8_t_defined) + __extension__ typedef long long int64_t; +# endif + __extension__ typedef unsigned long long uint64_t; +# elif defined(_LONG_LONG) || defined(__MWERKS__) || defined(_CRAYC) || \ + defined(__SUNPRO_C) || defined(__SUNPRO_CC) + typedef long long int64_t; + typedef unsigned long long uint64_t; +# elif (defined(__BORLANDC__) && __BORLANDC__ > 0x460) || \ + defined(__WATCOM_INT64__) || defined (__alpha) || defined (__DECC) + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# else +# error cannot find 64-bit integer type +# endif +#endif + +/** stdbool (C89 compatible) */ +#if (defined(YYJSON_HAS_STDBOOL_H) && YYJSON_HAS_STDBOOL_H) || \ + (yyjson_has_include(<stdbool.h>) && !defined(__STRICT_ANSI__)) || \ + YYJSON_MSC_VER >= 1800 || YYJSON_STDC_VER >= 199901L +# include <stdbool.h> +#elif !defined(__bool_true_false_are_defined) +# define __bool_true_false_are_defined 1 +# if defined(__cplusplus) +# if defined(__GNUC__) && !defined(__STRICT_ANSI__) +# define _Bool bool +# if __cplusplus < 201103L +# define bool bool +# define false false +# define true true +# endif +# endif +# else +# define bool unsigned char +# define true 1 +# define false 0 +# endif +#endif + +/** char bit check */ +#if defined(CHAR_BIT) +# if CHAR_BIT != 8 +# error non 8-bit char is not supported +# endif +#endif + +/** + Microsoft Visual C++ 6.0 doesn't support converting number from u64 to f64: + error C2520: conversion from unsigned __int64 to double not implemented. + */ +#ifndef YYJSON_U64_TO_F64_NO_IMPL +# if (0 < YYJSON_MSC_VER) && (YYJSON_MSC_VER <= 1200) +# define YYJSON_U64_TO_F64_NO_IMPL 1 +# else +# define YYJSON_U64_TO_F64_NO_IMPL 0 +# endif +#endif + + + +/*============================================================================== + * MARK: - Compile Hint Begin + *============================================================================*/ + +/* extern "C" begin */ +#ifdef __cplusplus +extern "C" { +#endif + +/* warning suppress begin */ +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-parameter" +#elif defined(__GNUC__) +# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# pragma GCC diagnostic push +# endif +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunused-parameter" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4800) /* 'int': forcing value to 'true' or 'false' */ +#endif + + + +/*============================================================================== + * MARK: - Version + *============================================================================*/ + +/** The major version of yyjson. */ +#define YYJSON_VERSION_MAJOR 0 + +/** The minor version of yyjson. */ +#define YYJSON_VERSION_MINOR 12 + +/** The patch version of yyjson. */ +#define YYJSON_VERSION_PATCH 0 + +/** The version of yyjson in hex: `(major << 16) | (minor << 8) | (patch)`. */ +#define YYJSON_VERSION_HEX 0x000C00 + +/** The version string of yyjson. */ +#define YYJSON_VERSION_STRING "0.12.0" + +/** The version of yyjson in hex, same as `YYJSON_VERSION_HEX`. */ +yyjson_api uint32_t yyjson_version(void); + + + +/*============================================================================== + * MARK: - JSON Types + *============================================================================*/ + +/** Type of a JSON value (3 bit). */ +typedef uint8_t yyjson_type; +/** No type, invalid. */ +#define YYJSON_TYPE_NONE ((uint8_t)0) /* _____000 */ +/** Raw string type, no subtype. */ +#define YYJSON_TYPE_RAW ((uint8_t)1) /* _____001 */ +/** Null type: `null` literal, no subtype. */ +#define YYJSON_TYPE_NULL ((uint8_t)2) /* _____010 */ +/** Boolean type, subtype: TRUE, FALSE. */ +#define YYJSON_TYPE_BOOL ((uint8_t)3) /* _____011 */ +/** Number type, subtype: UINT, SINT, REAL. */ +#define YYJSON_TYPE_NUM ((uint8_t)4) /* _____100 */ +/** String type, subtype: NONE, NOESC. */ +#define YYJSON_TYPE_STR ((uint8_t)5) /* _____101 */ +/** Array type, no subtype. */ +#define YYJSON_TYPE_ARR ((uint8_t)6) /* _____110 */ +/** Object type, no subtype. */ +#define YYJSON_TYPE_OBJ ((uint8_t)7) /* _____111 */ + +/** Subtype of a JSON value (2 bit). */ +typedef uint8_t yyjson_subtype; +/** No subtype. */ +#define YYJSON_SUBTYPE_NONE ((uint8_t)(0 << 3)) /* ___00___ */ +/** False subtype: `false` literal. */ +#define YYJSON_SUBTYPE_FALSE ((uint8_t)(0 << 3)) /* ___00___ */ +/** True subtype: `true` literal. */ +#define YYJSON_SUBTYPE_TRUE ((uint8_t)(1 << 3)) /* ___01___ */ +/** Unsigned integer subtype: `uint64_t`. */ +#define YYJSON_SUBTYPE_UINT ((uint8_t)(0 << 3)) /* ___00___ */ +/** Signed integer subtype: `int64_t`. */ +#define YYJSON_SUBTYPE_SINT ((uint8_t)(1 << 3)) /* ___01___ */ +/** Real number subtype: `double`. */ +#define YYJSON_SUBTYPE_REAL ((uint8_t)(2 << 3)) /* ___10___ */ +/** String that do not need to be escaped for writing (internal use). */ +#define YYJSON_SUBTYPE_NOESC ((uint8_t)(1 << 3)) /* ___01___ */ + +/** The mask used to extract the type of a JSON value. */ +#define YYJSON_TYPE_MASK ((uint8_t)0x07) /* _____111 */ +/** The number of bits used by the type. */ +#define YYJSON_TYPE_BIT ((uint8_t)3) +/** The mask used to extract the subtype of a JSON value. */ +#define YYJSON_SUBTYPE_MASK ((uint8_t)0x18) /* ___11___ */ +/** The number of bits used by the subtype. */ +#define YYJSON_SUBTYPE_BIT ((uint8_t)2) +/** The mask used to extract the reserved bits of a JSON value. */ +#define YYJSON_RESERVED_MASK ((uint8_t)0xE0) /* 111_____ */ +/** The number of reserved bits. */ +#define YYJSON_RESERVED_BIT ((uint8_t)3) +/** The mask used to extract the tag of a JSON value. */ +#define YYJSON_TAG_MASK ((uint8_t)0xFF) /* 11111111 */ +/** The number of bits used by the tag. */ +#define YYJSON_TAG_BIT ((uint8_t)8) + +/** Padding size for JSON reader. */ +#define YYJSON_PADDING_SIZE 4 + + + +/*============================================================================== + * MARK: - Allocator + *============================================================================*/ + +/** + A memory allocator. + + Typically you don't need to use it, unless you want to customize your own + memory allocator. + */ +typedef struct yyjson_alc { + /** Same as libc's malloc(size), should not be NULL. */ + void *(*malloc)(void *ctx, size_t size); + /** Same as libc's realloc(ptr, size), should not be NULL. */ + void *(*realloc)(void *ctx, void *ptr, size_t old_size, size_t size); + /** Same as libc's free(ptr), should not be NULL. */ + void (*free)(void *ctx, void *ptr); + /** A context for malloc/realloc/free, can be NULL. */ + void *ctx; +} yyjson_alc; + +/** + A pool allocator uses fixed length pre-allocated memory. + + This allocator may be used to avoid malloc/realloc calls. The pre-allocated + memory should be held by the caller. The maximum amount of memory required to + read a JSON can be calculated using the `yyjson_read_max_memory_usage()` + function, but the amount of memory required to write a JSON cannot be directly + calculated. + + This is not a general-purpose allocator. It is designed to handle a single JSON + data at a time. If it is used for overly complex memory tasks, such as parsing + multiple JSON documents using the same allocator but releasing only a few of + them, it may cause memory fragmentation, resulting in performance degradation + and memory waste. + + @param alc The allocator to be initialized. + If this parameter is NULL, the function will fail and return false. + If `buf` or `size` is invalid, this will be set to an empty allocator. + @param buf The buffer memory for this allocator. + If this parameter is NULL, the function will fail and return false. + @param size The size of `buf`, in bytes. + If this parameter is less than 8 words (32/64 bytes on 32/64-bit OS), the + function will fail and return false. + @return true if the `alc` has been successfully initialized. + + @b Example + @code + // parse JSON with stack memory + char buf[1024]; + yyjson_alc alc; + yyjson_alc_pool_init(&alc, buf, 1024); + + const char *json = "{\"name\":\"Helvetica\",\"size\":16}" + yyjson_doc *doc = yyjson_read_opts(json, strlen(json), 0, &alc, NULL); + // the memory of `doc` is on the stack + @endcode + + @warning This Allocator is not thread-safe. + */ +yyjson_api bool yyjson_alc_pool_init(yyjson_alc *alc, void *buf, size_t size); + +/** + A dynamic allocator. + + This allocator has a similar usage to the pool allocator above. However, when + there is not enough memory, this allocator will dynamically request more memory + using libc's `malloc` function, and frees it all at once when it is destroyed. + + @return A new dynamic allocator, or NULL if memory allocation failed. + @note The returned value should be freed with `yyjson_alc_dyn_free()`. + + @warning This Allocator is not thread-safe. + */ +yyjson_api yyjson_alc *yyjson_alc_dyn_new(void); + +/** + Free a dynamic allocator which is created by `yyjson_alc_dyn_new()`. + @param alc The dynamic allocator to be destroyed. + */ +yyjson_api void yyjson_alc_dyn_free(yyjson_alc *alc); + + + +/*============================================================================== + * MARK: - Text Locating + *============================================================================*/ + +/** + Locate the line and column number for a byte position in a string. + This can be used to get better description for error position. + + @param str The input string. + @param len The byte length of the input string. + @param pos The byte position within the input string. + @param line A pointer to receive the line number, starting from 1. + @param col A pointer to receive the column number, starting from 1. + @param chr A pointer to receive the character index, starting from 0. + @return true on success, false if `str` is NULL or `pos` is out of bounds. + @note Line/column/character are calculated based on Unicode characters for + compatibility with text editors. For multi-byte UTF-8 characters, + the returned value may not directly correspond to the byte position. + */ +yyjson_api bool yyjson_locate_pos(const char *str, size_t len, size_t pos, + size_t *line, size_t *col, size_t *chr); + + + +/*============================================================================== + * MARK: - JSON Structure + *============================================================================*/ + +/** + An immutable document for reading JSON. + This document holds memory for all its JSON values and strings. When it is no + longer used, the user should call `yyjson_doc_free()` to free its memory. + */ +typedef struct yyjson_doc yyjson_doc; + +/** + An immutable value for reading JSON. + A JSON Value has the same lifetime as its document. The memory is held by its + document and and cannot be freed alone. + */ +typedef struct yyjson_val yyjson_val; + +/** + A mutable document for building JSON. + This document holds memory for all its JSON values and strings. When it is no + longer used, the user should call `yyjson_mut_doc_free()` to free its memory. + */ +typedef struct yyjson_mut_doc yyjson_mut_doc; + +/** + A mutable value for building JSON. + A JSON Value has the same lifetime as its document. The memory is held by its + document and and cannot be freed alone. + */ +typedef struct yyjson_mut_val yyjson_mut_val; + + + +/*============================================================================== + * MARK: - JSON Reader API + *============================================================================*/ + +/** Run-time options for JSON reader. */ +typedef uint32_t yyjson_read_flag; + +/** Default option (RFC 8259 compliant): + - Read positive integer as uint64_t. + - Read negative integer as int64_t. + - Read floating-point number as double with round-to-nearest mode. + - Read integer which cannot fit in uint64_t or int64_t as double. + - Report error if double number is infinity. + - Report error if string contains invalid UTF-8 character or BOM. + - Report error on trailing commas, comments, inf and nan literals. */ +static const yyjson_read_flag YYJSON_READ_NOFLAG = 0; + +/** Read the input data in-situ. + This option allows the reader to modify and use input data to store string + values, which can increase reading speed slightly. + The caller should hold the input data before free the document. + The input data must be padded by at least `YYJSON_PADDING_SIZE` bytes. + For example: `[1,2]` should be `[1,2]\0\0\0\0`, input length should be 5. */ +static const yyjson_read_flag YYJSON_READ_INSITU = 1 << 0; + +/** Stop when done instead of issuing an error if there's additional content + after a JSON document. This option may be used to parse small pieces of JSON + in larger data, such as `NDJSON`. */ +static const yyjson_read_flag YYJSON_READ_STOP_WHEN_DONE = 1 << 1; + +/** Allow single trailing comma at the end of an object or array, + such as `[1,2,3,]`, `{"a":1,"b":2,}` (non-standard). */ +static const yyjson_read_flag YYJSON_READ_ALLOW_TRAILING_COMMAS = 1 << 2; + +/** Allow C-style single-line and mult-line comments (non-standard). */ +static const yyjson_read_flag YYJSON_READ_ALLOW_COMMENTS = 1 << 3; + +/** Allow inf/nan number and literal, case-insensitive, + such as 1e999, NaN, inf, -Infinity (non-standard). */ +static const yyjson_read_flag YYJSON_READ_ALLOW_INF_AND_NAN = 1 << 4; + +/** Read all numbers as raw strings (value with `YYJSON_TYPE_RAW` type), + inf/nan literal is also read as raw with `ALLOW_INF_AND_NAN` flag. */ +static const yyjson_read_flag YYJSON_READ_NUMBER_AS_RAW = 1 << 5; + +/** Allow reading invalid unicode when parsing string values (non-standard). + Invalid characters will be allowed to appear in the string values, but + invalid escape sequences will still be reported as errors. + This flag does not affect the performance of correctly encoded strings. + + @warning Strings in JSON values may contain incorrect encoding when this + option is used, you need to handle these strings carefully to avoid security + risks. */ +static const yyjson_read_flag YYJSON_READ_ALLOW_INVALID_UNICODE = 1 << 6; + +/** Read big numbers as raw strings. These big numbers include integers that + cannot be represented by `int64_t` and `uint64_t`, and floating-point + numbers that cannot be represented by finite `double`. + The flag will be overridden by `YYJSON_READ_NUMBER_AS_RAW` flag. */ +static const yyjson_read_flag YYJSON_READ_BIGNUM_AS_RAW = 1 << 7; + +/** Allow UTF-8 BOM and skip it before parsing if any (non-standard). */ +static const yyjson_read_flag YYJSON_READ_ALLOW_BOM = 1 << 8; + +/** Allow extended number formats (non-standard): + - Hexadecimal numbers, such as `0x7B`. + - Numbers with leading or trailing decimal point, such as `.123`, `123.`. + - Numbers with a leading plus sign, such as `+123`. */ +static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_NUMBER = 1 << 9; + +/** Allow extended escape sequences in strings (non-standard): + - Additional escapes: `\a`, `\e`, `\v`, ``\'``, `\?`, `\0`. + - Hex escapes: `\xNN`, such as `\x7B`. + - Line continuation: backslash followed by line terminator sequences. + - Unknown escape: if backslash is followed by an unsupported character, + the backslash will be removed and the character will be kept as-is. + However, `\1`-`\9` will still trigger an error. */ +static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_ESCAPE = 1 << 10; + +/** Allow extended whitespace characters (non-standard): + - Vertical tab `\v` and form feed `\f`. + - Line separator `\u2028` and paragraph separator `\u2029`. + - Non-breaking space `\xA0`. + - Byte order mark: `\uFEFF`. + - Other Unicode characters in the Zs (Separator, space) category. */ +static const yyjson_read_flag YYJSON_READ_ALLOW_EXT_WHITESPACE = 1 << 11; + +/** Allow strings enclosed in single quotes (non-standard), such as ``'ab'``. */ +static const yyjson_read_flag YYJSON_READ_ALLOW_SINGLE_QUOTED_STR = 1 << 12; + +/** Allow object keys without quotes (non-standard), such as `{a:1,b:2}`. + This extends the ECMAScript IdentifierName rule by allowing any + non-whitespace character with code point above `U+007F`. */ +static const yyjson_read_flag YYJSON_READ_ALLOW_UNQUOTED_KEY = 1 << 13; + +/** Allow JSON5 format, see: [https://json5.org]. + This flag supports all JSON5 features with some additional extensions: + - Accepts more escape sequences than JSON5 (e.g. `\a`, `\e`). + - Unquoted keys are not limited to ECMAScript IdentifierName. + - Allow case-insensitive `NaN`, `Inf` and `Infinity` literals. */ +static const yyjson_read_flag YYJSON_READ_JSON5 = + (1 << 2) | /* YYJSON_READ_ALLOW_TRAILING_COMMAS */ + (1 << 3) | /* YYJSON_READ_ALLOW_COMMENTS */ + (1 << 4) | /* YYJSON_READ_ALLOW_INF_AND_NAN */ + (1 << 9) | /* YYJSON_READ_ALLOW_EXT_NUMBER */ + (1 << 10) | /* YYJSON_READ_ALLOW_EXT_ESCAPE */ + (1 << 11) | /* YYJSON_READ_ALLOW_EXT_WHITESPACE */ + (1 << 12) | /* YYJSON_READ_ALLOW_SINGLE_QUOTED_STR */ + (1 << 13); /* YYJSON_READ_ALLOW_UNQUOTED_KEY */ + + + +/** Result code for JSON reader. */ +typedef uint32_t yyjson_read_code; + +/** Success, no error. */ +static const yyjson_read_code YYJSON_READ_SUCCESS = 0; + +/** Invalid parameter, such as NULL input string or 0 input length. */ +static const yyjson_read_code YYJSON_READ_ERROR_INVALID_PARAMETER = 1; + +/** Memory allocation failed. */ +static const yyjson_read_code YYJSON_READ_ERROR_MEMORY_ALLOCATION = 2; + +/** Input JSON string is empty. */ +static const yyjson_read_code YYJSON_READ_ERROR_EMPTY_CONTENT = 3; + +/** Unexpected content after document, such as `[123]abc`. */ +static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CONTENT = 4; + +/** Unexpected end of input, the parsed part is valid, such as `[123`. */ +static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_END = 5; + +/** Unexpected character inside the document, such as `[abc]`. */ +static const yyjson_read_code YYJSON_READ_ERROR_UNEXPECTED_CHARACTER = 6; + +/** Invalid JSON structure, such as `[1,]`. */ +static const yyjson_read_code YYJSON_READ_ERROR_JSON_STRUCTURE = 7; + +/** Invalid comment, deprecated, use `UNEXPECTED_END` for unclosed comment. */ +static const yyjson_read_code YYJSON_READ_ERROR_INVALID_COMMENT = 8; + +/** Invalid number, such as `123.e12`, `000`. */ +static const yyjson_read_code YYJSON_READ_ERROR_INVALID_NUMBER = 9; + +/** Invalid string, such as invalid escaped character inside a string. */ +static const yyjson_read_code YYJSON_READ_ERROR_INVALID_STRING = 10; + +/** Invalid JSON literal, such as `truu`. */ +static const yyjson_read_code YYJSON_READ_ERROR_LITERAL = 11; + +/** Failed to open a file. */ +static const yyjson_read_code YYJSON_READ_ERROR_FILE_OPEN = 12; + +/** Failed to read a file. */ +static const yyjson_read_code YYJSON_READ_ERROR_FILE_READ = 13; + +/** Incomplete input during incremental parsing; parsing state is preserved. */ +static const yyjson_read_code YYJSON_READ_ERROR_MORE = 14; + +/** Error information for JSON reader. */ +typedef struct yyjson_read_err { + /** Error code, see `yyjson_read_code` for all possible values. */ + yyjson_read_code code; + /** Error message, constant, no need to free (NULL if success). */ + const char *msg; + /** Error byte position for input data (0 if success). */ + size_t pos; +} yyjson_read_err; + + + +#if !defined(YYJSON_DISABLE_READER) || !YYJSON_DISABLE_READER + +/** + Read JSON with options. + + This function is thread-safe when: + 1. The `dat` is not modified by other threads. + 2. The `alc` is thread-safe or NULL. + + @param dat The JSON data (UTF-8 without BOM), null-terminator is not required. + If this parameter is NULL, the function will fail and return NULL. + The `dat` will not be modified without the flag `YYJSON_READ_INSITU`, so you + can pass a `const char *` string and case it to `char *` if you don't use + the `YYJSON_READ_INSITU` flag. + @param len The length of JSON data in bytes. + If this parameter is 0, the function will fail and return NULL. + @param flg The JSON read options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON reader. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return A new JSON document, or NULL if an error occurs. + When it's no longer needed, it should be freed with `yyjson_doc_free()`. + */ +yyjson_api yyjson_doc *yyjson_read_opts(char *dat, + size_t len, + yyjson_read_flag flg, + const yyjson_alc *alc, + yyjson_read_err *err); + +/** + Read a JSON file. + + This function is thread-safe when: + 1. The file is not modified by other threads. + 2. The `alc` is thread-safe or NULL. + + @param path The JSON file's path. + This should be a null-terminated string using the system's native encoding. + If this path is NULL or invalid, the function will fail and return NULL. + @param flg The JSON read options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON reader. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return A new JSON document, or NULL if an error occurs. + When it's no longer needed, it should be freed with `yyjson_doc_free()`. + + @warning On 32-bit operating system, files larger than 2GB may fail to read. + */ +yyjson_api yyjson_doc *yyjson_read_file(const char *path, + yyjson_read_flag flg, + const yyjson_alc *alc, + yyjson_read_err *err); + +/** + Read JSON from a file pointer. + + @param fp The file pointer. + The data will be read from the current position of the FILE to the end. + If this fp is NULL or invalid, the function will fail and return NULL. + @param flg The JSON read options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON reader. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return A new JSON document, or NULL if an error occurs. + When it's no longer needed, it should be freed with `yyjson_doc_free()`. + + @warning On 32-bit operating system, files larger than 2GB may fail to read. + */ +yyjson_api yyjson_doc *yyjson_read_fp(FILE *fp, + yyjson_read_flag flg, + const yyjson_alc *alc, + yyjson_read_err *err); + +/** + Read a JSON string. + + This function is thread-safe. + + @param dat The JSON data (UTF-8 without BOM), null-terminator is not required. + If this parameter is NULL, the function will fail and return NULL. + @param len The length of JSON data in bytes. + If this parameter is 0, the function will fail and return NULL. + @param flg The JSON read options. + Multiple options can be combined with `|` operator. 0 means no options. + @return A new JSON document, or NULL if an error occurs. + When it's no longer needed, it should be freed with `yyjson_doc_free()`. + */ +yyjson_api_inline yyjson_doc *yyjson_read(const char *dat, + size_t len, + yyjson_read_flag flg) { + flg &= ~YYJSON_READ_INSITU; /* const string cannot be modified */ + return yyjson_read_opts((char *)(void *)(size_t)(const void *)dat, + len, flg, NULL, NULL); +} + + + +#if !defined(YYJSON_DISABLE_INCR_READER) || !YYJSON_DISABLE_INCR_READER + +/** Opaque state for incremental JSON reader. */ +typedef struct yyjson_incr_state yyjson_incr_state; + +/** + Initialize state for incremental read. + + To read a large JSON document incrementally: + 1. Call `yyjson_incr_new()` to create the state for incremental reading. + 2. Call `yyjson_incr_read()` repeatedly. + 3. Call `yyjson_incr_free()` to free the state. + + Note: The incremental JSON reader only supports standard JSON. + Flags for non-standard features (e.g. comments, trailing commas) are ignored. + + @param buf The JSON data, null-terminator is not required. + If this parameter is NULL, the function will fail and return NULL. + @param buf_len The length of the JSON data in `buf`. + If use `YYJSON_READ_INSITU`, `buf_len` should not include the padding size. + @param flg The JSON read options. + Multiple options can be combined with `|` operator. + @param alc The memory allocator used by JSON reader. + Pass NULL to use the libc's default allocator. + @return A state for incremental reading. + It should be freed with `yyjson_incr_free()`. + NULL is returned if memory allocation fails. +*/ +yyjson_api yyjson_incr_state *yyjson_incr_new(char *buf, size_t buf_len, + yyjson_read_flag flg, + const yyjson_alc *alc); + +/** + Performs incremental read of up to `len` bytes. + + If NULL is returned and `err->code` is set to `YYJSON_READ_ERROR_MORE`, it + indicates that more data is required to continue parsing. Then, call this + function again with incremented `len`. Continue until a document is returned or + an error other than `YYJSON_READ_ERROR_MORE` is returned. + + Note: Parsing in very small increments is not efficient. An increment of + several kilobytes or megabytes is recommended. + + @param state The state for incremental reading, created using + `yyjson_incr_new()`. + @param len The number of bytes of JSON data available to parse. + If this parameter is 0, the function will fail and return NULL. + @param err A pointer to receive error information. + @return A new JSON document, or NULL if an error occurs. + When the document is no longer needed, it should be freed with + `yyjson_doc_free()`. +*/ +yyjson_api yyjson_doc *yyjson_incr_read(yyjson_incr_state *state, size_t len, + yyjson_read_err *err); + +/** Release the incremental read state and free the memory. */ +yyjson_api void yyjson_incr_free(yyjson_incr_state *state); + +#endif /* YYJSON_DISABLE_INCR_READER */ + +/** + Returns the size of maximum memory usage to read a JSON data. + + You may use this value to avoid malloc() or calloc() call inside the reader + to get better performance, or read multiple JSON with one piece of memory. + + @param len The length of JSON data in bytes. + @param flg The JSON read options. + @return The maximum memory size to read this JSON, or 0 if overflow. + + @b Example + @code + // read multiple JSON with same pre-allocated memory + + char *dat1, *dat2, *dat3; // JSON data + size_t len1, len2, len3; // JSON length + size_t max_len = MAX(len1, MAX(len2, len3)); + yyjson_doc *doc; + + // use one allocator for multiple JSON + size_t size = yyjson_read_max_memory_usage(max_len, 0); + void *buf = malloc(size); + yyjson_alc alc; + yyjson_alc_pool_init(&alc, buf, size); + + // no more alloc() or realloc() call during reading + doc = yyjson_read_opts(dat1, len1, 0, &alc, NULL); + yyjson_doc_free(doc); + doc = yyjson_read_opts(dat2, len2, 0, &alc, NULL); + yyjson_doc_free(doc); + doc = yyjson_read_opts(dat3, len3, 0, &alc, NULL); + yyjson_doc_free(doc); + + free(buf); + @endcode + @see yyjson_alc_pool_init() + */ +yyjson_api_inline size_t yyjson_read_max_memory_usage(size_t len, + yyjson_read_flag flg) { + /* + 1. The max value count is (json_size / 2 + 1), + for example: "[1,2,3,4]" size is 9, value count is 5. + 2. Some broken JSON may cost more memory during reading, but fail at end, + for example: "[[[[[[[[". + 3. yyjson use 16 bytes per value, see struct yyjson_val. + 4. yyjson use dynamic memory with a growth factor of 1.5. + + The max memory size is (json_size / 2 * 16 * 1.5 + padding). + */ + size_t mul = (size_t)12 + !(flg & YYJSON_READ_INSITU); + size_t pad = 256; + size_t max = (size_t)(~(size_t)0); + if (flg & YYJSON_READ_STOP_WHEN_DONE) len = len < 256 ? 256 : len; + if (len >= (max - pad - mul) / mul) return 0; + return len * mul + pad; +} + +/** + Read a JSON number. + + This function is thread-safe when data is not modified by other threads. + + @param dat The JSON data (UTF-8 without BOM), null-terminator is required. + If this parameter is NULL, the function will fail and return NULL. + @param val The output value where result is stored. + If this parameter is NULL, the function will fail and return NULL. + The value will hold either UINT or SINT or REAL number; + @param flg The JSON read options. + Multiple options can be combined with `|` operator. 0 means no options. + Supports `YYJSON_READ_NUMBER_AS_RAW` and `YYJSON_READ_ALLOW_INF_AND_NAN`. + @param alc The memory allocator used for long number. + It is only used when the built-in floating point reader is disabled. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return If successful, a pointer to the character after the last character + used in the conversion, NULL if an error occurs. + */ +yyjson_api const char *yyjson_read_number(const char *dat, + yyjson_val *val, + yyjson_read_flag flg, + const yyjson_alc *alc, + yyjson_read_err *err); + +/** Same as `yyjson_read_number()`. */ +yyjson_api_inline const char *yyjson_mut_read_number(const char *dat, + yyjson_mut_val *val, + yyjson_read_flag flg, + const yyjson_alc *alc, + yyjson_read_err *err) { + return yyjson_read_number(dat, (yyjson_val *)val, flg, alc, err); +} + +#endif /* YYJSON_DISABLE_READER) */ + + + +/*============================================================================== + * MARK: - JSON Writer API + *============================================================================*/ + +/** Run-time options for JSON writer. */ +typedef uint32_t yyjson_write_flag; + +/** Default option: + - Write JSON minify. + - Report error on inf or nan number. + - Report error on invalid UTF-8 string. + - Do not escape unicode or slash. */ +static const yyjson_write_flag YYJSON_WRITE_NOFLAG = 0; + +/** Write JSON pretty with 4 space indent. */ +static const yyjson_write_flag YYJSON_WRITE_PRETTY = 1 << 0; + +/** Escape unicode as `uXXXX`, make the output ASCII only. */ +static const yyjson_write_flag YYJSON_WRITE_ESCAPE_UNICODE = 1 << 1; + +/** Escape '/' as '\/'. */ +static const yyjson_write_flag YYJSON_WRITE_ESCAPE_SLASHES = 1 << 2; + +/** Write inf and nan number as 'Infinity' and 'NaN' literal (non-standard). */ +static const yyjson_write_flag YYJSON_WRITE_ALLOW_INF_AND_NAN = 1 << 3; + +/** Write inf and nan number as null literal. + This flag will override `YYJSON_WRITE_ALLOW_INF_AND_NAN` flag. */ +static const yyjson_write_flag YYJSON_WRITE_INF_AND_NAN_AS_NULL = 1 << 4; + +/** Allow invalid unicode when encoding string values (non-standard). + Invalid characters in string value will be copied byte by byte. + If `YYJSON_WRITE_ESCAPE_UNICODE` flag is also set, invalid character will be + escaped as `U+FFFD` (replacement character). + This flag does not affect the performance of correctly encoded strings. */ +static const yyjson_write_flag YYJSON_WRITE_ALLOW_INVALID_UNICODE = 1 << 5; + +/** Write JSON pretty with 2 space indent. + This flag will override `YYJSON_WRITE_PRETTY` flag. */ +static const yyjson_write_flag YYJSON_WRITE_PRETTY_TWO_SPACES = 1 << 6; + +/** Adds a newline character `\n` at the end of the JSON. + This can be helpful for text editors or NDJSON. */ +static const yyjson_write_flag YYJSON_WRITE_NEWLINE_AT_END = 1 << 7; + + + +/** The highest 8 bits of `yyjson_write_flag` and real number value's `tag` + are reserved for controlling the output format of floating-point numbers. */ +#define YYJSON_WRITE_FP_FLAG_BITS 8 + +/** The highest 4 bits of flag are reserved for precision value. */ +#define YYJSON_WRITE_FP_PREC_BITS 4 + +/** Write floating-point number using fixed-point notation. + - This is similar to ECMAScript `Number.prototype.toFixed(prec)`, + but with trailing zeros removed. The `prec` ranges from 1 to 15. + - This will produce shorter output but may lose some precision. */ +#define YYJSON_WRITE_FP_TO_FIXED(prec) ((yyjson_write_flag)( \ + (uint32_t)((uint32_t)(prec)) << (32 - 4) )) + +/** Write floating-point numbers using single-precision (float). + - This casts `double` to `float` before serialization. + - This will produce shorter output, but may lose some precision. + - This flag is ignored if `YYJSON_WRITE_FP_TO_FIXED(prec)` is also used. */ +#define YYJSON_WRITE_FP_TO_FLOAT ((yyjson_write_flag)(1 << (32 - 5))) + + + +/** Result code for JSON writer */ +typedef uint32_t yyjson_write_code; + +/** Success, no error. */ +static const yyjson_write_code YYJSON_WRITE_SUCCESS = 0; + +/** Invalid parameter, such as NULL document. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_PARAMETER = 1; + +/** Memory allocation failure occurs. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_MEMORY_ALLOCATION = 2; + +/** Invalid value type in JSON document. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_VALUE_TYPE = 3; + +/** NaN or Infinity number occurs. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_NAN_OR_INF = 4; + +/** Failed to open a file. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_FILE_OPEN = 5; + +/** Failed to write a file. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_FILE_WRITE = 6; + +/** Invalid unicode in string. */ +static const yyjson_write_code YYJSON_WRITE_ERROR_INVALID_STRING = 7; + +/** Error information for JSON writer. */ +typedef struct yyjson_write_err { + /** Error code, see `yyjson_write_code` for all possible values. */ + yyjson_write_code code; + /** Error message, constant, no need to free (NULL if success). */ + const char *msg; +} yyjson_write_err; + + + +#if !defined(YYJSON_DISABLE_WRITER) || !YYJSON_DISABLE_WRITER + +/*============================================================================== + * MARK: - JSON Document Writer API + *============================================================================*/ + +/** + Write a document to JSON string with options. + + This function is thread-safe when: + The `alc` is thread-safe or NULL. + + @param doc The JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free() or alc->free(). + */ +yyjson_api char *yyjson_write_opts(const yyjson_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc, + size_t *len, + yyjson_write_err *err); + +/** + Write a document to JSON file with options. + + This function is thread-safe when: + 1. The file is not accessed by other threads. + 2. The `alc` is thread-safe or NULL. + + @param path The JSON file's path. + This should be a null-terminated string using the system's native encoding. + If this path is NULL or invalid, the function will fail and return false. + If this file is not empty, the content will be discarded. + @param doc The JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_write_file(const char *path, + const yyjson_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a document to file pointer with options. + + @param fp The file pointer. + The data will be written to the current position of the file. + If this fp is NULL or invalid, the function will fail and return false. + @param doc The JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_write_fp(FILE *fp, + const yyjson_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a document to JSON string. + + This function is thread-safe. + + @param doc The JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free(). + */ +yyjson_api_inline char *yyjson_write(const yyjson_doc *doc, + yyjson_write_flag flg, + size_t *len) { + return yyjson_write_opts(doc, flg, NULL, len, NULL); +} + + + +/** + Write a document to JSON string with options. + + This function is thread-safe when: + 1. The `doc` is not modified by other threads. + 2. The `alc` is thread-safe or NULL. + + @param doc The mutable JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free() or alc->free(). + */ +yyjson_api char *yyjson_mut_write_opts(const yyjson_mut_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc, + size_t *len, + yyjson_write_err *err); + +/** + Write a document to JSON file with options. + + This function is thread-safe when: + 1. The file is not accessed by other threads. + 2. The `doc` is not modified by other threads. + 3. The `alc` is thread-safe or NULL. + + @param path The JSON file's path. + This should be a null-terminated string using the system's native encoding. + If this path is NULL or invalid, the function will fail and return false. + If this file is not empty, the content will be discarded. + @param doc The mutable JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_mut_write_file(const char *path, + const yyjson_mut_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a document to file pointer with options. + + @param fp The file pointer. + The data will be written to the current position of the file. + If this fp is NULL or invalid, the function will fail and return false. + @param doc The mutable JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_mut_write_fp(FILE *fp, + const yyjson_mut_doc *doc, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a document to JSON string. + + This function is thread-safe when: + The `doc` is not modified by other threads. + + @param doc The JSON document. + If this doc is NULL or has no root, the function will fail and return false. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free(). + */ +yyjson_api_inline char *yyjson_mut_write(const yyjson_mut_doc *doc, + yyjson_write_flag flg, + size_t *len) { + return yyjson_mut_write_opts(doc, flg, NULL, len, NULL); +} + + + +/*============================================================================== + * MARK: - JSON Value Writer API + *============================================================================*/ + +/** + Write a value to JSON string with options. + + This function is thread-safe when: + The `alc` is thread-safe or NULL. + + @param val The JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free() or alc->free(). + */ +yyjson_api char *yyjson_val_write_opts(const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc, + size_t *len, + yyjson_write_err *err); + +/** + Write a value to JSON file with options. + + This function is thread-safe when: + 1. The file is not accessed by other threads. + 2. The `alc` is thread-safe or NULL. + + @param path The JSON file's path. + This should be a null-terminated string using the system's native encoding. + If this path is NULL or invalid, the function will fail and return false. + If this file is not empty, the content will be discarded. + @param val The JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_val_write_file(const char *path, + const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a value to file pointer with options. + + @param fp The file pointer. + The data will be written to the current position of the file. + If this path is NULL or invalid, the function will fail and return false. + @param val The JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_val_write_fp(FILE *fp, + const yyjson_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a value to JSON string. + + This function is thread-safe. + + @param val The JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free(). + */ +yyjson_api_inline char *yyjson_val_write(const yyjson_val *val, + yyjson_write_flag flg, + size_t *len) { + return yyjson_val_write_opts(val, flg, NULL, len, NULL); +} + +/** + Write a value to JSON string with options. + + This function is thread-safe when: + 1. The `val` is not modified by other threads. + 2. The `alc` is thread-safe or NULL. + + @param val The mutable JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free() or alc->free(). + */ +yyjson_api char *yyjson_mut_val_write_opts(const yyjson_mut_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc, + size_t *len, + yyjson_write_err *err); + +/** + Write a value to JSON file with options. + + This function is thread-safe when: + 1. The file is not accessed by other threads. + 2. The `val` is not modified by other threads. + 3. The `alc` is thread-safe or NULL. + + @param path The JSON file's path. + This should be a null-terminated string using the system's native encoding. + If this path is NULL or invalid, the function will fail and return false. + If this file is not empty, the content will be discarded. + @param val The mutable JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_mut_val_write_file(const char *path, + const yyjson_mut_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a value to JSON file with options. + + @param fp The file pointer. + The data will be written to the current position of the file. + If this path is NULL or invalid, the function will fail and return false. + @param val The mutable JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param alc The memory allocator used by JSON writer. + Pass NULL to use the libc's default allocator. + @param err A pointer to receive error information. + Pass NULL if you don't need error information. + @return true if successful, false if an error occurs. + + @warning On 32-bit operating system, files larger than 2GB may fail to write. + */ +yyjson_api bool yyjson_mut_val_write_fp(FILE *fp, + const yyjson_mut_val *val, + yyjson_write_flag flg, + const yyjson_alc *alc, + yyjson_write_err *err); + +/** + Write a value to JSON string. + + This function is thread-safe when: + The `val` is not modified by other threads. + + @param val The JSON root value. + If this parameter is NULL, the function will fail and return NULL. + @param flg The JSON write options. + Multiple options can be combined with `|` operator. 0 means no options. + @param len A pointer to receive output length in bytes (not including the + null-terminator). Pass NULL if you don't need length information. + @return A new JSON string, or NULL if an error occurs. + This string is encoded as UTF-8 with a null-terminator. + When it's no longer needed, it should be freed with free(). + */ +yyjson_api_inline char *yyjson_mut_val_write(const yyjson_mut_val *val, + yyjson_write_flag flg, + size_t *len) { + return yyjson_mut_val_write_opts(val, flg, NULL, len, NULL); +} + +/** + Write a JSON number. + + @param val A JSON number value to be converted to a string. + If this parameter is invalid, the function will fail and return NULL. + @param buf A buffer to store the resulting null-terminated string. + If this parameter is NULL, the function will fail and return NULL. + For integer values, the buffer must be at least 21 bytes. + For floating-point values, the buffer must be at least 40 bytes. + @return On success, returns a pointer to the character after the last + written character. On failure, returns NULL. + @note + - This function is thread-safe and does not allocate memory + (when `YYJSON_DISABLE_FAST_FP_CONV` is not defined). + - This function will fail and return NULL only in the following cases: + 1) `val` or `buf` is NULL; + 2) `val` is not a number type; + 3) `val` is `inf` or `nan`, and non-standard JSON is explicitly disabled + via the `YYJSON_DISABLE_NON_STANDARD` flag. + */ +yyjson_api char *yyjson_write_number(const yyjson_val *val, char *buf); + +/** Same as `yyjson_write_number()`. */ +yyjson_api_inline char *yyjson_mut_write_number(const yyjson_mut_val *val, + char *buf) { + return yyjson_write_number((const yyjson_val *)val, buf); +} + +#endif /* YYJSON_DISABLE_WRITER */ + + + +/*============================================================================== + * MARK: - JSON Document API + *============================================================================*/ + +/** Returns the root value of this JSON document. + Returns NULL if `doc` is NULL. */ +yyjson_api_inline yyjson_val *yyjson_doc_get_root(yyjson_doc *doc); + +/** Returns read size of input JSON data. + Returns 0 if `doc` is NULL. + For example: the read size of `[1,2,3]` is 7 bytes. */ +yyjson_api_inline size_t yyjson_doc_get_read_size(yyjson_doc *doc); + +/** Returns total value count in this JSON document. + Returns 0 if `doc` is NULL. + For example: the value count of `[1,2,3]` is 4. */ +yyjson_api_inline size_t yyjson_doc_get_val_count(yyjson_doc *doc); + +/** Release the JSON document and free the memory. + After calling this function, the `doc` and all values from the `doc` are no + longer available. This function will do nothing if the `doc` is NULL. */ +yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc); + + + +/*============================================================================== + * MARK: - JSON Value Type API + *============================================================================*/ + +/** Returns whether the JSON value is raw. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_raw(yyjson_val *val); + +/** Returns whether the JSON value is `null`. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_null(yyjson_val *val); + +/** Returns whether the JSON value is `true`. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_true(yyjson_val *val); + +/** Returns whether the JSON value is `false`. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_false(yyjson_val *val); + +/** Returns whether the JSON value is bool (true/false). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_bool(yyjson_val *val); + +/** Returns whether the JSON value is unsigned integer (uint64_t). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_uint(yyjson_val *val); + +/** Returns whether the JSON value is signed integer (int64_t). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_sint(yyjson_val *val); + +/** Returns whether the JSON value is integer (uint64_t/int64_t). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_int(yyjson_val *val); + +/** Returns whether the JSON value is real number (double). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_real(yyjson_val *val); + +/** Returns whether the JSON value is number (uint64_t/int64_t/double). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_num(yyjson_val *val); + +/** Returns whether the JSON value is string. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_str(yyjson_val *val); + +/** Returns whether the JSON value is array. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_arr(yyjson_val *val); + +/** Returns whether the JSON value is object. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_obj(yyjson_val *val); + +/** Returns whether the JSON value is container (array/object). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val); + + + +/*============================================================================== + * MARK: - JSON Value Content API + *============================================================================*/ + +/** Returns the JSON value's type. + Returns YYJSON_TYPE_NONE if `val` is NULL. */ +yyjson_api_inline yyjson_type yyjson_get_type(yyjson_val *val); + +/** Returns the JSON value's subtype. + Returns YYJSON_SUBTYPE_NONE if `val` is NULL. */ +yyjson_api_inline yyjson_subtype yyjson_get_subtype(yyjson_val *val); + +/** Returns the JSON value's tag. + Returns 0 if `val` is NULL. */ +yyjson_api_inline uint8_t yyjson_get_tag(yyjson_val *val); + +/** Returns the JSON value's type description. + The return value should be one of these strings: "raw", "null", "string", + "array", "object", "true", "false", "uint", "sint", "real", "unknown". */ +yyjson_api_inline const char *yyjson_get_type_desc(yyjson_val *val); + +/** Returns the content if the value is raw. + Returns NULL if `val` is NULL or type is not raw. */ +yyjson_api_inline const char *yyjson_get_raw(yyjson_val *val); + +/** Returns the content if the value is bool. + Returns false if `val` is NULL or type is not bool. */ +yyjson_api_inline bool yyjson_get_bool(yyjson_val *val); + +/** Returns the content and cast to uint64_t. + Returns 0 if `val` is NULL or type is not integer(sint/uint). */ +yyjson_api_inline uint64_t yyjson_get_uint(yyjson_val *val); + +/** Returns the content and cast to int64_t. + Returns 0 if `val` is NULL or type is not integer(sint/uint). */ +yyjson_api_inline int64_t yyjson_get_sint(yyjson_val *val); + +/** Returns the content and cast to int. + Returns 0 if `val` is NULL or type is not integer(sint/uint). */ +yyjson_api_inline int yyjson_get_int(yyjson_val *val); + +/** Returns the content if the value is real number, or 0.0 on error. + Returns 0.0 if `val` is NULL or type is not real(double). */ +yyjson_api_inline double yyjson_get_real(yyjson_val *val); + +/** Returns the content and typecast to `double` if the value is number. + Returns 0.0 if `val` is NULL or type is not number(uint/sint/real). */ +yyjson_api_inline double yyjson_get_num(yyjson_val *val); + +/** Returns the content if the value is string. + Returns NULL if `val` is NULL or type is not string. */ +yyjson_api_inline const char *yyjson_get_str(yyjson_val *val); + +/** Returns the content length (string length, array size, object size. + Returns 0 if `val` is NULL or type is not string/array/object. */ +yyjson_api_inline size_t yyjson_get_len(yyjson_val *val); + +/** Returns whether the JSON value is equals to a string. + Returns false if input is NULL or type is not string. */ +yyjson_api_inline bool yyjson_equals_str(yyjson_val *val, const char *str); + +/** Returns whether the JSON value is equals to a string. + The `str` should be a UTF-8 string, null-terminator is not required. + Returns false if input is NULL or type is not string. */ +yyjson_api_inline bool yyjson_equals_strn(yyjson_val *val, const char *str, + size_t len); + +/** Returns whether two JSON values are equal (deep compare). + Returns false if input is NULL. + @note the result may be inaccurate if object has duplicate keys. + @warning This function is recursive and may cause a stack overflow + if the object level is too deep. */ +yyjson_api_inline bool yyjson_equals(yyjson_val *lhs, yyjson_val *rhs); + +/** Set the value to raw. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_raw(yyjson_val *val, + const char *raw, size_t len); + +/** Set the value to null. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_null(yyjson_val *val); + +/** Set the value to bool. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_bool(yyjson_val *val, bool num); + +/** Set the value to uint. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_uint(yyjson_val *val, uint64_t num); + +/** Set the value to sint. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_sint(yyjson_val *val, int64_t num); + +/** Set the value to int. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_int(yyjson_val *val, int num); + +/** Set the value to float. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_float(yyjson_val *val, float num); + +/** Set the value to double. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_double(yyjson_val *val, double num); + +/** Set the value to real. + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_real(yyjson_val *val, double num); + +/** Set the floating-point number's output format to fixed-point notation. + Returns false if input is NULL or `val` is not real type. + @see YYJSON_WRITE_FP_TO_FIXED flag. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_fp_to_fixed(yyjson_val *val, int prec); + +/** Set the floating-point number's output format to single-precision. + Returns false if input is NULL or `val` is not real type. + @see YYJSON_WRITE_FP_TO_FLOAT flag. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_fp_to_float(yyjson_val *val, bool flt); + +/** Set the value to string (null-terminated). + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_str(yyjson_val *val, const char *str); + +/** Set the value to string (with length). + Returns false if input is NULL or `val` is object or array. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_strn(yyjson_val *val, + const char *str, size_t len); + +/** Marks this string as not needing to be escaped during JSON writing. + This can be used to avoid the overhead of escaping if the string contains + only characters that do not require escaping. + Returns false if input is NULL or `val` is not string. + @see YYJSON_SUBTYPE_NOESC subtype. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_set_str_noesc(yyjson_val *val, bool noesc); + + + +/*============================================================================== + * MARK: - JSON Array API + *============================================================================*/ + +/** Returns the number of elements in this array. + Returns 0 if `arr` is NULL or type is not array. */ +yyjson_api_inline size_t yyjson_arr_size(yyjson_val *arr); + +/** Returns the element at the specified position in this array. + Returns NULL if array is NULL/empty or the index is out of bounds. + @warning This function takes a linear search time if array is not flat. + For example: `[1,{},3]` is flat, `[1,[2],3]` is not flat. */ +yyjson_api_inline yyjson_val *yyjson_arr_get(yyjson_val *arr, size_t idx); + +/** Returns the first element of this array. + Returns NULL if `arr` is NULL/empty or type is not array. */ +yyjson_api_inline yyjson_val *yyjson_arr_get_first(yyjson_val *arr); + +/** Returns the last element of this array. + Returns NULL if `arr` is NULL/empty or type is not array. + @warning This function takes a linear search time if array is not flat. + For example: `[1,{},3]` is flat, `[1,[2],3]` is not flat.*/ +yyjson_api_inline yyjson_val *yyjson_arr_get_last(yyjson_val *arr); + + + +/*============================================================================== + * MARK: - JSON Array Iterator API + *============================================================================*/ + +/** + A JSON array iterator. + + @b Example + @code + yyjson_val *val; + yyjson_arr_iter iter = yyjson_arr_iter_with(arr); + while ((val = yyjson_arr_iter_next(&iter))) { + your_func(val); + } + @endcode + */ +typedef struct yyjson_arr_iter { + size_t idx; /**< next value's index */ + size_t max; /**< maximum index (arr.size) */ + yyjson_val *cur; /**< next value */ +} yyjson_arr_iter; + +/** + Initialize an iterator for this array. + + @param arr The array to be iterated over. + If this parameter is NULL or not an array, `iter` will be set to empty. + @param iter The iterator to be initialized. + If this parameter is NULL, the function will fail and return false. + @return true if the `iter` has been successfully initialized. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline bool yyjson_arr_iter_init(yyjson_val *arr, + yyjson_arr_iter *iter); + +/** + Create an iterator with an array , same as `yyjson_arr_iter_init()`. + + @param arr The array to be iterated over. + If this parameter is NULL or not an array, an empty iterator will returned. + @return A new iterator for the array. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline yyjson_arr_iter yyjson_arr_iter_with(yyjson_val *arr); + +/** + Returns whether the iteration has more elements. + If `iter` is NULL, this function will return false. + */ +yyjson_api_inline bool yyjson_arr_iter_has_next(yyjson_arr_iter *iter); + +/** + Returns the next element in the iteration, or NULL on end. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_val *yyjson_arr_iter_next(yyjson_arr_iter *iter); + +/** + Macro for iterating over an array. + It works like iterator, but with a more intuitive API. + + @b Example + @code + size_t idx, max; + yyjson_val *val; + yyjson_arr_foreach(arr, idx, max, val) { + your_func(idx, val); + } + @endcode + */ +#define yyjson_arr_foreach(arr, idx, max, val) \ + for ((idx) = 0, \ + (max) = yyjson_arr_size(arr), \ + (val) = yyjson_arr_get_first(arr); \ + (idx) < (max); \ + (idx)++, \ + (val) = unsafe_yyjson_get_next(val)) + + + +/*============================================================================== + * MARK: - JSON Object API + *============================================================================*/ + +/** Returns the number of key-value pairs in this object. + Returns 0 if `obj` is NULL or type is not object. */ +yyjson_api_inline size_t yyjson_obj_size(yyjson_val *obj); + +/** Returns the value to which the specified key is mapped. + Returns NULL if this object contains no mapping for the key. + Returns NULL if `obj/key` is NULL, or type is not object. + + The `key` should be a null-terminated UTF-8 string. + + @warning This function takes a linear search time. */ +yyjson_api_inline yyjson_val *yyjson_obj_get(yyjson_val *obj, const char *key); + +/** Returns the value to which the specified key is mapped. + Returns NULL if this object contains no mapping for the key. + Returns NULL if `obj/key` is NULL, or type is not object. + + The `key` should be a UTF-8 string, null-terminator is not required. + The `key_len` should be the length of the key, in bytes. + + @warning This function takes a linear search time. */ +yyjson_api_inline yyjson_val *yyjson_obj_getn(yyjson_val *obj, const char *key, + size_t key_len); + + + +/*============================================================================== + * MARK: - JSON Object Iterator API + *============================================================================*/ + +/** + A JSON object iterator. + + @b Example + @code + yyjson_val *key, *val; + yyjson_obj_iter iter = yyjson_obj_iter_with(obj); + while ((key = yyjson_obj_iter_next(&iter))) { + val = yyjson_obj_iter_get_val(key); + your_func(key, val); + } + @endcode + + If the ordering of the keys is known at compile-time, you can use this method + to speed up value lookups: + @code + // {"k1":1, "k2": 3, "k3": 3} + yyjson_val *key, *val; + yyjson_obj_iter iter = yyjson_obj_iter_with(obj); + yyjson_val *v1 = yyjson_obj_iter_get(&iter, "k1"); + yyjson_val *v3 = yyjson_obj_iter_get(&iter, "k3"); + @endcode + @see yyjson_obj_iter_get() and yyjson_obj_iter_getn() + */ +typedef struct yyjson_obj_iter { + size_t idx; /**< next key's index */ + size_t max; /**< maximum key index (obj.size) */ + yyjson_val *cur; /**< next key */ + yyjson_val *obj; /**< the object being iterated */ +} yyjson_obj_iter; + +/** + Initialize an iterator for this object. + + @param obj The object to be iterated over. + If this parameter is NULL or not an object, `iter` will be set to empty. + @param iter The iterator to be initialized. + If this parameter is NULL, the function will fail and return false. + @return true if the `iter` has been successfully initialized. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline bool yyjson_obj_iter_init(yyjson_val *obj, + yyjson_obj_iter *iter); + +/** + Create an iterator with an object, same as `yyjson_obj_iter_init()`. + + @param obj The object to be iterated over. + If this parameter is NULL or not an object, an empty iterator will returned. + @return A new iterator for the object. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline yyjson_obj_iter yyjson_obj_iter_with(yyjson_val *obj); + +/** + Returns whether the iteration has more elements. + If `iter` is NULL, this function will return false. + */ +yyjson_api_inline bool yyjson_obj_iter_has_next(yyjson_obj_iter *iter); + +/** + Returns the next key in the iteration, or NULL on end. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_val *yyjson_obj_iter_next(yyjson_obj_iter *iter); + +/** + Returns the value for key inside the iteration. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_val *yyjson_obj_iter_get_val(yyjson_val *key); + +/** + Iterates to a specified key and returns the value. + + This function does the same thing as `yyjson_obj_get()`, but is much faster + if the ordering of the keys is known at compile-time and you are using the same + order to look up the values. If the key exists in this object, then the + iterator will stop at the next key, otherwise the iterator will not change and + NULL is returned. + + @param iter The object iterator, should not be NULL. + @param key The key, should be a UTF-8 string with null-terminator. + @return The value to which the specified key is mapped. + NULL if this object contains no mapping for the key or input is invalid. + + @warning This function takes a linear search time if the key is not nearby. + */ +yyjson_api_inline yyjson_val *yyjson_obj_iter_get(yyjson_obj_iter *iter, + const char *key); + +/** + Iterates to a specified key and returns the value. + + This function does the same thing as `yyjson_obj_getn()`, but is much faster + if the ordering of the keys is known at compile-time and you are using the same + order to look up the values. If the key exists in this object, then the + iterator will stop at the next key, otherwise the iterator will not change and + NULL is returned. + + @param iter The object iterator, should not be NULL. + @param key The key, should be a UTF-8 string, null-terminator is not required. + @param key_len The the length of `key`, in bytes. + @return The value to which the specified key is mapped. + NULL if this object contains no mapping for the key or input is invalid. + + @warning This function takes a linear search time if the key is not nearby. + */ +yyjson_api_inline yyjson_val *yyjson_obj_iter_getn(yyjson_obj_iter *iter, + const char *key, + size_t key_len); + +/** + Macro for iterating over an object. + It works like iterator, but with a more intuitive API. + + @b Example + @code + size_t idx, max; + yyjson_val *key, *val; + yyjson_obj_foreach(obj, idx, max, key, val) { + your_func(key, val); + } + @endcode + */ +#define yyjson_obj_foreach(obj, idx, max, key, val) \ + for ((idx) = 0, \ + (max) = yyjson_obj_size(obj), \ + (key) = (obj) ? unsafe_yyjson_get_first(obj) : NULL, \ + (val) = (key) + 1; \ + (idx) < (max); \ + (idx)++, \ + (key) = unsafe_yyjson_get_next(val), \ + (val) = (key) + 1) + + + +/*============================================================================== + * MARK: - Mutable JSON Document API + *============================================================================*/ + +/** Returns the root value of this JSON document. + Returns NULL if `doc` is NULL. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_get_root(yyjson_mut_doc *doc); + +/** Sets the root value of this JSON document. + Pass NULL to clear root value of the document. */ +yyjson_api_inline void yyjson_mut_doc_set_root(yyjson_mut_doc *doc, + yyjson_mut_val *root); + +/** + Set the string pool size for a mutable document. + This function does not allocate memory immediately, but uses the size when + the next memory allocation is needed. + + If the caller knows the approximate bytes of strings that the document needs to + store (e.g. copy string with `yyjson_mut_strcpy` function), setting a larger + size can avoid multiple memory allocations and improve performance. + + @param doc The mutable document. + @param len The desired string pool size in bytes (total string length). + @return true if successful, false if size is 0 or overflow. + */ +yyjson_api bool yyjson_mut_doc_set_str_pool_size(yyjson_mut_doc *doc, + size_t len); + +/** + Set the value pool size for a mutable document. + This function does not allocate memory immediately, but uses the size when + the next memory allocation is needed. + + If the caller knows the approximate number of values that the document needs to + store (e.g. create new value with `yyjson_mut_xxx` functions), setting a larger + size can avoid multiple memory allocations and improve performance. + + @param doc The mutable document. + @param count The desired value pool size (number of `yyjson_mut_val`). + @return true if successful, false if size is 0 or overflow. + */ +yyjson_api bool yyjson_mut_doc_set_val_pool_size(yyjson_mut_doc *doc, + size_t count); + +/** Release the JSON document and free the memory. + After calling this function, the `doc` and all values from the `doc` are no + longer available. This function will do nothing if the `doc` is NULL. */ +yyjson_api void yyjson_mut_doc_free(yyjson_mut_doc *doc); + +/** Creates and returns a new mutable JSON document, returns NULL on error. + If allocator is NULL, the default allocator will be used. */ +yyjson_api yyjson_mut_doc *yyjson_mut_doc_new(const yyjson_alc *alc); + +/** Copies and returns a new mutable document from input, returns NULL on error. + This makes a `deep-copy` on the immutable document. + If allocator is NULL, the default allocator will be used. + @note `imut_doc` -> `mut_doc`. */ +yyjson_api yyjson_mut_doc *yyjson_doc_mut_copy(yyjson_doc *doc, + const yyjson_alc *alc); + +/** Copies and returns a new mutable document from input, returns NULL on error. + This makes a `deep-copy` on the mutable document. + If allocator is NULL, the default allocator will be used. + @note `mut_doc` -> `mut_doc`. */ +yyjson_api yyjson_mut_doc *yyjson_mut_doc_mut_copy(yyjson_mut_doc *doc, + const yyjson_alc *alc); + +/** Copies and returns a new mutable value from input, returns NULL on error. + This makes a `deep-copy` on the immutable value. + The memory was managed by mutable document. + @note `imut_val` -> `mut_val`. */ +yyjson_api yyjson_mut_val *yyjson_val_mut_copy(yyjson_mut_doc *doc, + yyjson_val *val); + +/** Copies and returns a new mutable value from input, returns NULL on error. + This makes a `deep-copy` on the mutable value. + The memory was managed by mutable document. + @note `mut_val` -> `mut_val`. + @warning This function is recursive and may cause a stack overflow + if the object level is too deep. */ +yyjson_api yyjson_mut_val *yyjson_mut_val_mut_copy(yyjson_mut_doc *doc, + yyjson_mut_val *val); + +/** Copies and returns a new immutable document from input, + returns NULL on error. This makes a `deep-copy` on the mutable document. + The returned document should be freed with `yyjson_doc_free()`. + @note `mut_doc` -> `imut_doc`. + @warning This function is recursive and may cause a stack overflow + if the object level is too deep. */ +yyjson_api yyjson_doc *yyjson_mut_doc_imut_copy(yyjson_mut_doc *doc, + const yyjson_alc *alc); + +/** Copies and returns a new immutable document from input, + returns NULL on error. This makes a `deep-copy` on the mutable value. + The returned document should be freed with `yyjson_doc_free()`. + @note `mut_val` -> `imut_doc`. + @warning This function is recursive and may cause a stack overflow + if the object level is too deep. */ +yyjson_api yyjson_doc *yyjson_mut_val_imut_copy(yyjson_mut_val *val, + const yyjson_alc *alc); + + + +/*============================================================================== + * MARK: - Mutable JSON Value Type API + *============================================================================*/ + +/** Returns whether the JSON value is raw. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_raw(yyjson_mut_val *val); + +/** Returns whether the JSON value is `null`. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_null(yyjson_mut_val *val); + +/** Returns whether the JSON value is `true`. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_true(yyjson_mut_val *val); + +/** Returns whether the JSON value is `false`. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_false(yyjson_mut_val *val); + +/** Returns whether the JSON value is bool (true/false). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_bool(yyjson_mut_val *val); + +/** Returns whether the JSON value is unsigned integer (uint64_t). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_uint(yyjson_mut_val *val); + +/** Returns whether the JSON value is signed integer (int64_t). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_sint(yyjson_mut_val *val); + +/** Returns whether the JSON value is integer (uint64_t/int64_t). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_int(yyjson_mut_val *val); + +/** Returns whether the JSON value is real number (double). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_real(yyjson_mut_val *val); + +/** Returns whether the JSON value is number (uint/sint/real). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_num(yyjson_mut_val *val); + +/** Returns whether the JSON value is string. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_str(yyjson_mut_val *val); + +/** Returns whether the JSON value is array. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_arr(yyjson_mut_val *val); + +/** Returns whether the JSON value is object. + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_obj(yyjson_mut_val *val); + +/** Returns whether the JSON value is container (array/object). + Returns false if `val` is NULL. */ +yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val); + + + +/*============================================================================== + * MARK: - Mutable JSON Value Content API + *============================================================================*/ + +/** Returns the JSON value's type. + Returns `YYJSON_TYPE_NONE` if `val` is NULL. */ +yyjson_api_inline yyjson_type yyjson_mut_get_type(yyjson_mut_val *val); + +/** Returns the JSON value's subtype. + Returns `YYJSON_SUBTYPE_NONE` if `val` is NULL. */ +yyjson_api_inline yyjson_subtype yyjson_mut_get_subtype(yyjson_mut_val *val); + +/** Returns the JSON value's tag. + Returns 0 if `val` is NULL. */ +yyjson_api_inline uint8_t yyjson_mut_get_tag(yyjson_mut_val *val); + +/** Returns the JSON value's type description. + The return value should be one of these strings: "raw", "null", "string", + "array", "object", "true", "false", "uint", "sint", "real", "unknown". */ +yyjson_api_inline const char *yyjson_mut_get_type_desc(yyjson_mut_val *val); + +/** Returns the content if the value is raw. + Returns NULL if `val` is NULL or type is not raw. */ +yyjson_api_inline const char *yyjson_mut_get_raw(yyjson_mut_val *val); + +/** Returns the content if the value is bool. + Returns NULL if `val` is NULL or type is not bool. */ +yyjson_api_inline bool yyjson_mut_get_bool(yyjson_mut_val *val); + +/** Returns the content and cast to uint64_t. + Returns 0 if `val` is NULL or type is not integer(sint/uint). */ +yyjson_api_inline uint64_t yyjson_mut_get_uint(yyjson_mut_val *val); + +/** Returns the content and cast to int64_t. + Returns 0 if `val` is NULL or type is not integer(sint/uint). */ +yyjson_api_inline int64_t yyjson_mut_get_sint(yyjson_mut_val *val); + +/** Returns the content and cast to int. + Returns 0 if `val` is NULL or type is not integer(sint/uint). */ +yyjson_api_inline int yyjson_mut_get_int(yyjson_mut_val *val); + +/** Returns the content if the value is real number. + Returns 0.0 if `val` is NULL or type is not real(double). */ +yyjson_api_inline double yyjson_mut_get_real(yyjson_mut_val *val); + +/** Returns the content and typecast to `double` if the value is number. + Returns 0.0 if `val` is NULL or type is not number(uint/sint/real). */ +yyjson_api_inline double yyjson_mut_get_num(yyjson_mut_val *val); + +/** Returns the content if the value is string. + Returns NULL if `val` is NULL or type is not string. */ +yyjson_api_inline const char *yyjson_mut_get_str(yyjson_mut_val *val); + +/** Returns the content length (string length, array size, object size. + Returns 0 if `val` is NULL or type is not string/array/object. */ +yyjson_api_inline size_t yyjson_mut_get_len(yyjson_mut_val *val); + +/** Returns whether the JSON value is equals to a string. + The `str` should be a null-terminated UTF-8 string. + Returns false if input is NULL or type is not string. */ +yyjson_api_inline bool yyjson_mut_equals_str(yyjson_mut_val *val, + const char *str); + +/** Returns whether the JSON value is equals to a string. + The `str` should be a UTF-8 string, null-terminator is not required. + Returns false if input is NULL or type is not string. */ +yyjson_api_inline bool yyjson_mut_equals_strn(yyjson_mut_val *val, + const char *str, size_t len); + +/** Returns whether two JSON values are equal (deep compare). + Returns false if input is NULL. + @note the result may be inaccurate if object has duplicate keys. + @warning This function is recursive and may cause a stack overflow + if the object level is too deep. */ +yyjson_api_inline bool yyjson_mut_equals(yyjson_mut_val *lhs, + yyjson_mut_val *rhs); + +/** Set the value to raw. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_raw(yyjson_mut_val *val, + const char *raw, size_t len); + +/** Set the value to null. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_null(yyjson_mut_val *val); + +/** Set the value to bool. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_bool(yyjson_mut_val *val, bool num); + +/** Set the value to uint. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_uint(yyjson_mut_val *val, uint64_t num); + +/** Set the value to sint. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_sint(yyjson_mut_val *val, int64_t num); + +/** Set the value to int. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_int(yyjson_mut_val *val, int num); + +/** Set the value to float. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_float(yyjson_mut_val *val, float num); + +/** Set the value to double. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_double(yyjson_mut_val *val, double num); + +/** Set the value to real. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_real(yyjson_mut_val *val, double num); + +/** Set the floating-point number's output format to fixed-point notation. + Returns false if input is NULL or `val` is not real type. + @see YYJSON_WRITE_FP_TO_FIXED flag. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_mut_set_fp_to_fixed(yyjson_mut_val *val, + int prec); + +/** Set the floating-point number's output format to single-precision. + Returns false if input is NULL or `val` is not real type. + @see YYJSON_WRITE_FP_TO_FLOAT flag. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_mut_set_fp_to_float(yyjson_mut_val *val, + bool flt); + +/** Set the value to string (null-terminated). + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_str(yyjson_mut_val *val, const char *str); + +/** Set the value to string (with length). + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_strn(yyjson_mut_val *val, + const char *str, size_t len); + +/** Marks this string as not needing to be escaped during JSON writing. + This can be used to avoid the overhead of escaping if the string contains + only characters that do not require escaping. + Returns false if input is NULL or `val` is not string. + @see YYJSON_SUBTYPE_NOESC subtype. + @warning This will modify the `immutable` value, use with caution. */ +yyjson_api_inline bool yyjson_mut_set_str_noesc(yyjson_mut_val *val, + bool noesc); + +/** Set the value to array. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_arr(yyjson_mut_val *val); + +/** Set the value to array. + Returns false if input is NULL. + @warning This function should not be used on an existing object or array. */ +yyjson_api_inline bool yyjson_mut_set_obj(yyjson_mut_val *val); + + + +/*============================================================================== + * MARK: - Mutable JSON Value Creation API + *============================================================================*/ + +/** Creates and returns a raw value, returns NULL on error. + The `str` should be a null-terminated UTF-8 string. + + @warning The input string is not copied, you should keep this string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_raw(yyjson_mut_doc *doc, + const char *str); + +/** Creates and returns a raw value, returns NULL on error. + The `str` should be a UTF-8 string, null-terminator is not required. + + @warning The input string is not copied, you should keep this string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_rawn(yyjson_mut_doc *doc, + const char *str, + size_t len); + +/** Creates and returns a raw value, returns NULL on error. + The `str` should be a null-terminated UTF-8 string. + The input string is copied and held by the document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_rawcpy(yyjson_mut_doc *doc, + const char *str); + +/** Creates and returns a raw value, returns NULL on error. + The `str` should be a UTF-8 string, null-terminator is not required. + The input string is copied and held by the document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_rawncpy(yyjson_mut_doc *doc, + const char *str, + size_t len); + +/** Creates and returns a null value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_null(yyjson_mut_doc *doc); + +/** Creates and returns a true value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_true(yyjson_mut_doc *doc); + +/** Creates and returns a false value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_false(yyjson_mut_doc *doc); + +/** Creates and returns a bool value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_bool(yyjson_mut_doc *doc, + bool val); + +/** Creates and returns an unsigned integer value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_uint(yyjson_mut_doc *doc, + uint64_t num); + +/** Creates and returns a signed integer value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_sint(yyjson_mut_doc *doc, + int64_t num); + +/** Creates and returns a signed integer value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_int(yyjson_mut_doc *doc, + int64_t num); + +/** Creates and returns a float number value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_float(yyjson_mut_doc *doc, + float num); + +/** Creates and returns a double number value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_double(yyjson_mut_doc *doc, + double num); + +/** Creates and returns a real number value, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_real(yyjson_mut_doc *doc, + double num); + +/** Creates and returns a string value, returns NULL on error. + The `str` should be a null-terminated UTF-8 string. + @warning The input string is not copied, you should keep this string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_str(yyjson_mut_doc *doc, + const char *str); + +/** Creates and returns a string value, returns NULL on error. + The `str` should be a UTF-8 string, null-terminator is not required. + @warning The input string is not copied, you should keep this string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_strn(yyjson_mut_doc *doc, + const char *str, + size_t len); + +/** Creates and returns a string value, returns NULL on error. + The `str` should be a null-terminated UTF-8 string. + The input string is copied and held by the document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_strcpy(yyjson_mut_doc *doc, + const char *str); + +/** Creates and returns a string value, returns NULL on error. + The `str` should be a UTF-8 string, null-terminator is not required. + The input string is copied and held by the document. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_strncpy(yyjson_mut_doc *doc, + const char *str, + size_t len); + + + +/*============================================================================== + * MARK: - Mutable JSON Array API + *============================================================================*/ + +/** Returns the number of elements in this array. + Returns 0 if `arr` is NULL or type is not array. */ +yyjson_api_inline size_t yyjson_mut_arr_size(yyjson_mut_val *arr); + +/** Returns the element at the specified position in this array. + Returns NULL if array is NULL/empty or the index is out of bounds. + @warning This function takes a linear search time. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get(yyjson_mut_val *arr, + size_t idx); + +/** Returns the first element of this array. + Returns NULL if `arr` is NULL/empty or type is not array. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_first(yyjson_mut_val *arr); + +/** Returns the last element of this array. + Returns NULL if `arr` is NULL/empty or type is not array. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_last(yyjson_mut_val *arr); + + + +/*============================================================================== + * MARK: - Mutable JSON Array Iterator API + *============================================================================*/ + +/** + A mutable JSON array iterator. + + @warning You should not modify the array while iterating over it, but you can + use `yyjson_mut_arr_iter_remove()` to remove current value. + + @b Example + @code + yyjson_mut_val *val; + yyjson_mut_arr_iter iter = yyjson_mut_arr_iter_with(arr); + while ((val = yyjson_mut_arr_iter_next(&iter))) { + your_func(val); + if (your_val_is_unused(val)) { + yyjson_mut_arr_iter_remove(&iter); + } + } + @endcode + */ +typedef struct yyjson_mut_arr_iter { + size_t idx; /**< next value's index */ + size_t max; /**< maximum index (arr.size) */ + yyjson_mut_val *cur; /**< current value */ + yyjson_mut_val *pre; /**< previous value */ + yyjson_mut_val *arr; /**< the array being iterated */ +} yyjson_mut_arr_iter; + +/** + Initialize an iterator for this array. + + @param arr The array to be iterated over. + If this parameter is NULL or not an array, `iter` will be set to empty. + @param iter The iterator to be initialized. + If this parameter is NULL, the function will fail and return false. + @return true if the `iter` has been successfully initialized. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline bool yyjson_mut_arr_iter_init(yyjson_mut_val *arr, + yyjson_mut_arr_iter *iter); + +/** + Create an iterator with an array , same as `yyjson_mut_arr_iter_init()`. + + @param arr The array to be iterated over. + If this parameter is NULL or not an array, an empty iterator will returned. + @return A new iterator for the array. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline yyjson_mut_arr_iter yyjson_mut_arr_iter_with( + yyjson_mut_val *arr); + +/** + Returns whether the iteration has more elements. + If `iter` is NULL, this function will return false. + */ +yyjson_api_inline bool yyjson_mut_arr_iter_has_next( + yyjson_mut_arr_iter *iter); + +/** + Returns the next element in the iteration, or NULL on end. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_next( + yyjson_mut_arr_iter *iter); + +/** + Removes and returns current element in the iteration. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove( + yyjson_mut_arr_iter *iter); + +/** + Macro for iterating over an array. + It works like iterator, but with a more intuitive API. + + @warning You should not modify the array while iterating over it. + + @b Example + @code + size_t idx, max; + yyjson_mut_val *val; + yyjson_mut_arr_foreach(arr, idx, max, val) { + your_func(idx, val); + } + @endcode + */ +#define yyjson_mut_arr_foreach(arr, idx, max, val) \ + for ((idx) = 0, \ + (max) = yyjson_mut_arr_size(arr), \ + (val) = yyjson_mut_arr_get_first(arr); \ + (idx) < (max); \ + (idx)++, \ + (val) = (val)->next) + + + +/*============================================================================== + * MARK: - Mutable JSON Array Creation API + *============================================================================*/ + +/** + Creates and returns an empty mutable array. + @param doc A mutable document, used for memory allocation only. + @return The new array. NULL if input is NULL or memory allocation failed. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr(yyjson_mut_doc *doc); + +/** + Creates and returns a new mutable array with the given boolean values. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of boolean values. + @param count The value count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const bool vals[3] = { true, false, true }; + yyjson_mut_val *arr = yyjson_mut_arr_with_bool(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_bool( + yyjson_mut_doc *doc, const bool *vals, size_t count); + +/** + Creates and returns a new mutable array with the given sint numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of sint numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const int64_t vals[3] = { -1, 0, 1 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_sint64(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint( + yyjson_mut_doc *doc, const int64_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given uint numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of uint numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const uint64_t vals[3] = { 0, 1, 0 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_uint(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint( + yyjson_mut_doc *doc, const uint64_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given real numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of real numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const double vals[3] = { 0.1, 0.2, 0.3 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_real(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_real( + yyjson_mut_doc *doc, const double *vals, size_t count); + +/** + Creates and returns a new mutable array with the given int8 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of int8 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const int8_t vals[3] = { -1, 0, 1 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_sint8(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint8( + yyjson_mut_doc *doc, const int8_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given int16 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of int16 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const int16_t vals[3] = { -1, 0, 1 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_sint16(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint16( + yyjson_mut_doc *doc, const int16_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given int32 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of int32 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const int32_t vals[3] = { -1, 0, 1 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_sint32(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint32( + yyjson_mut_doc *doc, const int32_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given int64 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of int64 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const int64_t vals[3] = { -1, 0, 1 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_sint64(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint64( + yyjson_mut_doc *doc, const int64_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given uint8 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of uint8 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const uint8_t vals[3] = { 0, 1, 0 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_uint8(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint8( + yyjson_mut_doc *doc, const uint8_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given uint16 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of uint16 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const uint16_t vals[3] = { 0, 1, 0 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_uint16(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint16( + yyjson_mut_doc *doc, const uint16_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given uint32 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of uint32 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const uint32_t vals[3] = { 0, 1, 0 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_uint32(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint32( + yyjson_mut_doc *doc, const uint32_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given uint64 numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of uint64 numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const uint64_t vals[3] = { 0, 1, 0 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_uint64(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint64( + yyjson_mut_doc *doc, const uint64_t *vals, size_t count); + +/** + Creates and returns a new mutable array with the given float numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of float numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const float vals[3] = { -1.0f, 0.0f, 1.0f }; + yyjson_mut_val *arr = yyjson_mut_arr_with_float(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_float( + yyjson_mut_doc *doc, const float *vals, size_t count); + +/** + Creates and returns a new mutable array with the given double numbers. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of double numbers. + @param count The number count. If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const double vals[3] = { -1.0, 0.0, 1.0 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_double(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_double( + yyjson_mut_doc *doc, const double *vals, size_t count); + +/** + Creates and returns a new mutable array with the given strings, these strings + will not be copied. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of UTF-8 null-terminator strings. + If this array contains NULL, the function will fail and return NULL. + @param count The number of values in `vals`. + If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @warning The input strings are not copied, you should keep these strings + unmodified for the lifetime of this JSON document. If these strings will be + modified, you should use `yyjson_mut_arr_with_strcpy()` instead. + + @b Example + @code + const char *vals[3] = { "a", "b", "c" }; + yyjson_mut_val *arr = yyjson_mut_arr_with_str(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_str( + yyjson_mut_doc *doc, const char **vals, size_t count); + +/** + Creates and returns a new mutable array with the given strings and string + lengths, these strings will not be copied. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of UTF-8 strings, null-terminator is not required. + If this array contains NULL, the function will fail and return NULL. + @param lens A C array of string lengths, in bytes. + @param count The number of strings in `vals`. + If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @warning The input strings are not copied, you should keep these strings + unmodified for the lifetime of this JSON document. If these strings will be + modified, you should use `yyjson_mut_arr_with_strncpy()` instead. + + @b Example + @code + const char *vals[3] = { "a", "bb", "c" }; + const size_t lens[3] = { 1, 2, 1 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_strn(doc, vals, lens, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strn( + yyjson_mut_doc *doc, const char **vals, const size_t *lens, size_t count); + +/** + Creates and returns a new mutable array with the given strings, these strings + will be copied. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of UTF-8 null-terminator strings. + If this array contains NULL, the function will fail and return NULL. + @param count The number of values in `vals`. + If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const char *vals[3] = { "a", "b", "c" }; + yyjson_mut_val *arr = yyjson_mut_arr_with_strcpy(doc, vals, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strcpy( + yyjson_mut_doc *doc, const char **vals, size_t count); + +/** + Creates and returns a new mutable array with the given strings and string + lengths, these strings will be copied. + + @param doc A mutable document, used for memory allocation only. + If this parameter is NULL, the function will fail and return NULL. + @param vals A C array of UTF-8 strings, null-terminator is not required. + If this array contains NULL, the function will fail and return NULL. + @param lens A C array of string lengths, in bytes. + @param count The number of strings in `vals`. + If this value is 0, an empty array will return. + @return The new array. NULL if input is invalid or memory allocation failed. + + @b Example + @code + const char *vals[3] = { "a", "bb", "c" }; + const size_t lens[3] = { 1, 2, 1 }; + yyjson_mut_val *arr = yyjson_mut_arr_with_strn(doc, vals, lens, 3); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strncpy( + yyjson_mut_doc *doc, const char **vals, const size_t *lens, size_t count); + + + +/*============================================================================== + * MARK: - Mutable JSON Array Modification API + *============================================================================*/ + +/** + Inserts a value into an array at a given index. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param val The value to be inserted. Returns false if it is NULL. + @param idx The index to which to insert the new value. + Returns false if the index is out of range. + @return Whether successful. + @warning This function takes a linear search time. + */ +yyjson_api_inline bool yyjson_mut_arr_insert(yyjson_mut_val *arr, + yyjson_mut_val *val, size_t idx); + +/** + Inserts a value at the end of the array. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param val The value to be inserted. Returns false if it is NULL. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_append(yyjson_mut_val *arr, + yyjson_mut_val *val); + +/** + Inserts a value at the head of the array. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param val The value to be inserted. Returns false if it is NULL. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_prepend(yyjson_mut_val *arr, + yyjson_mut_val *val); + +/** + Replaces a value at index and returns old value. + @param arr The array to which the value is to be replaced. + Returns false if it is NULL or not an array. + @param idx The index to which to replace the value. + Returns false if the index is out of range. + @param val The new value to replace. Returns false if it is NULL. + @return Old value, or NULL on error. + @warning This function takes a linear search time. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_replace(yyjson_mut_val *arr, + size_t idx, + yyjson_mut_val *val); + +/** + Removes and returns a value at index. + @param arr The array from which the value is to be removed. + Returns false if it is NULL or not an array. + @param idx The index from which to remove the value. + Returns false if the index is out of range. + @return Old value, or NULL on error. + @warning This function takes a linear search time. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_remove(yyjson_mut_val *arr, + size_t idx); + +/** + Removes and returns the first value in this array. + @param arr The array from which the value is to be removed. + Returns false if it is NULL or not an array. + @return The first value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_remove_first( + yyjson_mut_val *arr); + +/** + Removes and returns the last value in this array. + @param arr The array from which the value is to be removed. + Returns false if it is NULL or not an array. + @return The last value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_remove_last( + yyjson_mut_val *arr); + +/** + Removes all values within a specified range in the array. + @param arr The array from which the value is to be removed. + Returns false if it is NULL or not an array. + @param idx The start index of the range (0 is the first). + @param len The number of items in the range (can be 0). + @return Whether successful. + @warning This function takes a linear search time. + */ +yyjson_api_inline bool yyjson_mut_arr_remove_range(yyjson_mut_val *arr, + size_t idx, size_t len); + +/** + Removes all values in this array. + @param arr The array from which all of the values are to be removed. + Returns false if it is NULL or not an array. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_clear(yyjson_mut_val *arr); + +/** + Rotates values in this array for the given number of times. + For example: `[1,2,3,4,5]` rotate 2 is `[3,4,5,1,2]`. + @param arr The array to be rotated. + @param idx Index (or times) to rotate. + @warning This function takes a linear search time. + */ +yyjson_api_inline bool yyjson_mut_arr_rotate(yyjson_mut_val *arr, + size_t idx); + + + +/*============================================================================== + * MARK: - Mutable JSON Array Modification Convenience API + *============================================================================*/ + +/** + Adds a value at the end of the array. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param val The value to be inserted. Returns false if it is NULL. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_val(yyjson_mut_val *arr, + yyjson_mut_val *val); + +/** + Adds a `null` value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_null(yyjson_mut_doc *doc, + yyjson_mut_val *arr); + +/** + Adds a `true` value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_true(yyjson_mut_doc *doc, + yyjson_mut_val *arr); + +/** + Adds a `false` value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_false(yyjson_mut_doc *doc, + yyjson_mut_val *arr); + +/** + Adds a bool value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param val The bool value to be added. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_bool(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + bool val); + +/** + Adds an unsigned integer value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param num The number to be added. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_uint(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + uint64_t num); + +/** + Adds a signed integer value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param num The number to be added. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_sint(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + int64_t num); + +/** + Adds an integer value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param num The number to be added. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_int(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + int64_t num); + +/** + Adds a float value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param num The number to be added. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_float(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + float num); + +/** + Adds a double value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param num The number to be added. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_double(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + double num); + +/** + Adds a double value at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param num The number to be added. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_real(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + double num); + +/** + Adds a string value at the end of the array (no copy). + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param str A null-terminated UTF-8 string. + @return Whether successful. + @warning The input string is not copied, you should keep this string unmodified + for the lifetime of this JSON document. + */ +yyjson_api_inline bool yyjson_mut_arr_add_str(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str); + +/** + Adds a string value at the end of the array (no copy). + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param str A UTF-8 string, null-terminator is not required. + @param len The length of the string, in bytes. + @return Whether successful. + @warning The input string is not copied, you should keep this string unmodified + for the lifetime of this JSON document. + */ +yyjson_api_inline bool yyjson_mut_arr_add_strn(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str, + size_t len); + +/** + Adds a string value at the end of the array (copied). + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param str A null-terminated UTF-8 string. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_strcpy(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str); + +/** + Adds a string value at the end of the array (copied). + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @param str A UTF-8 string, null-terminator is not required. + @param len The length of the string, in bytes. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_arr_add_strncpy(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str, + size_t len); + +/** + Creates and adds a new array at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @return The new array, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *arr); + +/** + Creates and adds a new object at the end of the array. + @param doc The `doc` is only used for memory allocation. + @param arr The array to which the value is to be inserted. + Returns false if it is NULL or not an array. + @return The new object, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *arr); + + + +/*============================================================================== + * MARK: - Mutable JSON Object API + *============================================================================*/ + +/** Returns the number of key-value pairs in this object. + Returns 0 if `obj` is NULL or type is not object. */ +yyjson_api_inline size_t yyjson_mut_obj_size(yyjson_mut_val *obj); + +/** Returns the value to which the specified key is mapped. + Returns NULL if this object contains no mapping for the key. + Returns NULL if `obj/key` is NULL, or type is not object. + + The `key` should be a null-terminated UTF-8 string. + + @warning This function takes a linear search time. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_get(yyjson_mut_val *obj, + const char *key); + +/** Returns the value to which the specified key is mapped. + Returns NULL if this object contains no mapping for the key. + Returns NULL if `obj/key` is NULL, or type is not object. + + The `key` should be a UTF-8 string, null-terminator is not required. + The `key_len` should be the length of the key, in bytes. + + @warning This function takes a linear search time. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(yyjson_mut_val *obj, + const char *key, + size_t key_len); + + + +/*============================================================================== + * MARK: - Mutable JSON Object Iterator API + *============================================================================*/ + +/** + A mutable JSON object iterator. + + @warning You should not modify the object while iterating over it, but you can + use `yyjson_mut_obj_iter_remove()` to remove current value. + + @b Example + @code + yyjson_mut_val *key, *val; + yyjson_mut_obj_iter iter = yyjson_mut_obj_iter_with(obj); + while ((key = yyjson_mut_obj_iter_next(&iter))) { + val = yyjson_mut_obj_iter_get_val(key); + your_func(key, val); + if (your_val_is_unused(key, val)) { + yyjson_mut_obj_iter_remove(&iter); + } + } + @endcode + + If the ordering of the keys is known at compile-time, you can use this method + to speed up value lookups: + @code + // {"k1":1, "k2": 3, "k3": 3} + yyjson_mut_val *key, *val; + yyjson_mut_obj_iter iter = yyjson_mut_obj_iter_with(obj); + yyjson_mut_val *v1 = yyjson_mut_obj_iter_get(&iter, "k1"); + yyjson_mut_val *v3 = yyjson_mut_obj_iter_get(&iter, "k3"); + @endcode + @see `yyjson_mut_obj_iter_get()` and `yyjson_mut_obj_iter_getn()` + */ +typedef struct yyjson_mut_obj_iter { + size_t idx; /**< next key's index */ + size_t max; /**< maximum key index (obj.size) */ + yyjson_mut_val *cur; /**< current key */ + yyjson_mut_val *pre; /**< previous key */ + yyjson_mut_val *obj; /**< the object being iterated */ +} yyjson_mut_obj_iter; + +/** + Initialize an iterator for this object. + + @param obj The object to be iterated over. + If this parameter is NULL or not an array, `iter` will be set to empty. + @param iter The iterator to be initialized. + If this parameter is NULL, the function will fail and return false. + @return true if the `iter` has been successfully initialized. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline bool yyjson_mut_obj_iter_init(yyjson_mut_val *obj, + yyjson_mut_obj_iter *iter); + +/** + Create an iterator with an object, same as `yyjson_obj_iter_init()`. + + @param obj The object to be iterated over. + If this parameter is NULL or not an object, an empty iterator will returned. + @return A new iterator for the object. + + @note The iterator does not need to be destroyed. + */ +yyjson_api_inline yyjson_mut_obj_iter yyjson_mut_obj_iter_with( + yyjson_mut_val *obj); + +/** + Returns whether the iteration has more elements. + If `iter` is NULL, this function will return false. + */ +yyjson_api_inline bool yyjson_mut_obj_iter_has_next( + yyjson_mut_obj_iter *iter); + +/** + Returns the next key in the iteration, or NULL on end. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_next( + yyjson_mut_obj_iter *iter); + +/** + Returns the value for key inside the iteration. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_get_val( + yyjson_mut_val *key); + +/** + Removes current key-value pair in the iteration, returns the removed value. + If `iter` is NULL, this function will return NULL. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_remove( + yyjson_mut_obj_iter *iter); + +/** + Iterates to a specified key and returns the value. + + This function does the same thing as `yyjson_mut_obj_get()`, but is much faster + if the ordering of the keys is known at compile-time and you are using the same + order to look up the values. If the key exists in this object, then the + iterator will stop at the next key, otherwise the iterator will not change and + NULL is returned. + + @param iter The object iterator, should not be NULL. + @param key The key, should be a UTF-8 string with null-terminator. + @return The value to which the specified key is mapped. + NULL if this object contains no mapping for the key or input is invalid. + + @warning This function takes a linear search time if the key is not nearby. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_get( + yyjson_mut_obj_iter *iter, const char *key); + +/** + Iterates to a specified key and returns the value. + + This function does the same thing as `yyjson_mut_obj_getn()` but is much faster + if the ordering of the keys is known at compile-time and you are using the same + order to look up the values. If the key exists in this object, then the + iterator will stop at the next key, otherwise the iterator will not change and + NULL is returned. + + @param iter The object iterator, should not be NULL. + @param key The key, should be a UTF-8 string, null-terminator is not required. + @param key_len The the length of `key`, in bytes. + @return The value to which the specified key is mapped. + NULL if this object contains no mapping for the key or input is invalid. + + @warning This function takes a linear search time if the key is not nearby. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_getn( + yyjson_mut_obj_iter *iter, const char *key, size_t key_len); + +/** + Macro for iterating over an object. + It works like iterator, but with a more intuitive API. + + @warning You should not modify the object while iterating over it. + + @b Example + @code + size_t idx, max; + yyjson_mut_val *key, *val; + yyjson_mut_obj_foreach(obj, idx, max, key, val) { + your_func(key, val); + } + @endcode + */ +#define yyjson_mut_obj_foreach(obj, idx, max, key, val) \ + for ((idx) = 0, \ + (max) = yyjson_mut_obj_size(obj), \ + (key) = (max) ? ((yyjson_mut_val *)(obj)->uni.ptr)->next->next : NULL, \ + (val) = (key) ? (key)->next : NULL; \ + (idx) < (max); \ + (idx)++, \ + (key) = (val)->next, \ + (val) = (key)->next) + + + +/*============================================================================== + * MARK: - Mutable JSON Object Creation API + *============================================================================*/ + +/** Creates and returns a mutable object, returns NULL on error. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj(yyjson_mut_doc *doc); + +/** + Creates and returns a mutable object with keys and values, returns NULL on + error. The keys and values are not copied. The strings should be a + null-terminated UTF-8 string. + + @warning The input string is not copied, you should keep this string + unmodified for the lifetime of this JSON document. + + @b Example + @code + const char *keys[2] = { "id", "name" }; + const char *vals[2] = { "01", "Harry" }; + yyjson_mut_val *obj = yyjson_mut_obj_with_str(doc, keys, vals, 2); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_str(yyjson_mut_doc *doc, + const char **keys, + const char **vals, + size_t count); + +/** + Creates and returns a mutable object with key-value pairs and pair count, + returns NULL on error. The keys and values are not copied. The strings should + be a null-terminated UTF-8 string. + + @warning The input string is not copied, you should keep this string + unmodified for the lifetime of this JSON document. + + @b Example + @code + const char *kv_pairs[4] = { "id", "01", "name", "Harry" }; + yyjson_mut_val *obj = yyjson_mut_obj_with_kv(doc, kv_pairs, 2); + @endcode + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_kv(yyjson_mut_doc *doc, + const char **kv_pairs, + size_t pair_count); + + + +/*============================================================================== + * MARK: - Mutable JSON Object Modification API + *============================================================================*/ + +/** + Adds a key-value pair at the end of the object. + This function allows duplicated key in one object. + @param obj The object to which the new key-value pair is to be added. + @param key The key, should be a string which is created by `yyjson_mut_str()`, + `yyjson_mut_strn()`, `yyjson_mut_strcpy()` or `yyjson_mut_strncpy()`. + @param val The value to add to the object. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_obj_add(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val); +/** + Sets a key-value pair at the end of the object. + This function may remove all key-value pairs for the given key before add. + @param obj The object to which the new key-value pair is to be added. + @param key The key, should be a string which is created by `yyjson_mut_str()`, + `yyjson_mut_strn()`, `yyjson_mut_strcpy()` or `yyjson_mut_strncpy()`. + @param val The value to add to the object. If this value is null, the behavior + is same as `yyjson_mut_obj_remove()`. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_obj_put(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val); + +/** + Inserts a key-value pair to the object at the given position. + This function allows duplicated key in one object. + @param obj The object to which the new key-value pair is to be added. + @param key The key, should be a string which is created by `yyjson_mut_str()`, + `yyjson_mut_strn()`, `yyjson_mut_strcpy()` or `yyjson_mut_strncpy()`. + @param val The value to add to the object. + @param idx The index to which to insert the new pair. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_obj_insert(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val, + size_t idx); + +/** + Removes all key-value pair from the object with given key. + @param obj The object from which the key-value pair is to be removed. + @param key The key, should be a string value. + @return The first matched value, or NULL if no matched value. + @warning This function takes a linear search time. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove(yyjson_mut_val *obj, + yyjson_mut_val *key); + +/** + Removes all key-value pair from the object with given key. + @param obj The object from which the key-value pair is to be removed. + @param key The key, should be a UTF-8 string with null-terminator. + @return The first matched value, or NULL if no matched value. + @warning This function takes a linear search time. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_key( + yyjson_mut_val *obj, const char *key); + +/** + Removes all key-value pair from the object with given key. + @param obj The object from which the key-value pair is to be removed. + @param key The key, should be a UTF-8 string, null-terminator is not required. + @param key_len The length of the key. + @return The first matched value, or NULL if no matched value. + @warning This function takes a linear search time. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_keyn( + yyjson_mut_val *obj, const char *key, size_t key_len); + +/** + Removes all key-value pairs in this object. + @param obj The object from which all of the values are to be removed. + @return Whether successful. + */ +yyjson_api_inline bool yyjson_mut_obj_clear(yyjson_mut_val *obj); + +/** + Replaces value from the object with given key. + If the key is not exist, or the value is NULL, it will fail. + @param obj The object to which the value is to be replaced. + @param key The key, should be a string value. + @param val The value to replace into the object. + @return Whether successful. + @warning This function takes a linear search time. + */ +yyjson_api_inline bool yyjson_mut_obj_replace(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val); + +/** + Rotates key-value pairs in the object for the given number of times. + For example: `{"a":1,"b":2,"c":3,"d":4}` rotate 1 is + `{"b":2,"c":3,"d":4,"a":1}`. + @param obj The object to be rotated. + @param idx Index (or times) to rotate. + @return Whether successful. + @warning This function takes a linear search time. + */ +yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj, + size_t idx); + + + +/*============================================================================== + * MARK: - Mutable JSON Object Modification Convenience API + *============================================================================*/ + +/** Adds a `null` value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + +/** Adds a `true` value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_true(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + +/** Adds a `false` value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_false(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + +/** Adds a bool value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, bool val); + +/** Adds an unsigned integer value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_uint(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, uint64_t val); + +/** Adds a signed integer value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_sint(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, int64_t val); + +/** Adds an int value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, int64_t val); + +/** Adds a float value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_float(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, float val); + +/** Adds a double value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_double(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, double val); + +/** Adds a real value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_real(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, double val); + +/** Adds a string value at the end of the object. + The `key` and `val` should be null-terminated UTF-8 strings. + This function allows duplicated key in one object. + + @warning The key/value strings are not copied, you should keep these strings + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, const char *val); + +/** Adds a string value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + The `val` should be a UTF-8 string, null-terminator is not required. + The `len` should be the length of the `val`, in bytes. + This function allows duplicated key in one object. + + @warning The key/value strings are not copied, you should keep these strings + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_strn(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + const char *val, size_t len); + +/** Adds a string value at the end of the object. + The `key` and `val` should be null-terminated UTF-8 strings. + The value string is copied. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + const char *val); + +/** Adds a string value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + The `val` should be a UTF-8 string, null-terminator is not required. + The `len` should be the length of the `val`, in bytes. + This function allows duplicated key in one object. + + @warning The key strings are not copied, you should keep these strings + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + const char *val, size_t len); + +/** + Creates and adds a new array to the target object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep these strings + unmodified for the lifetime of this JSON document. + @return The new array, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + +/** + Creates and adds a new object to the target object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep these strings + unmodified for the lifetime of this JSON document. + @return The new object, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key); + +/** Adds a JSON value at the end of the object. + The `key` should be a null-terminated UTF-8 string. + This function allows duplicated key in one object. + + @warning The key string is not copied, you should keep the string + unmodified for the lifetime of this JSON document. */ +yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + yyjson_mut_val *val); + +/** Removes all key-value pairs for the given key. + Returns the first value to which the specified key is mapped or NULL if this + object contains no mapping for the key. + The `key` should be a null-terminated UTF-8 string. + + @warning This function takes a linear search time. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_str( + yyjson_mut_val *obj, const char *key); + +/** Removes all key-value pairs for the given key. + Returns the first value to which the specified key is mapped or NULL if this + object contains no mapping for the key. + The `key` should be a UTF-8 string, null-terminator is not required. + The `len` should be the length of the key, in bytes. + + @warning This function takes a linear search time. */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_strn( + yyjson_mut_val *obj, const char *key, size_t len); + +/** Replaces all matching keys with the new key. + Returns true if at least one key was renamed. + The `key` and `new_key` should be a null-terminated UTF-8 string. + The `new_key` is copied and held by doc. + + @warning This function takes a linear search time. + If `new_key` already exists, it will cause duplicate keys. + */ +yyjson_api_inline bool yyjson_mut_obj_rename_key(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + const char *new_key); + +/** Replaces all matching keys with the new key. + Returns true if at least one key was renamed. + The `key` and `new_key` should be a UTF-8 string, + null-terminator is not required. The `new_key` is copied and held by doc. + + @warning This function takes a linear search time. + If `new_key` already exists, it will cause duplicate keys. + */ +yyjson_api_inline bool yyjson_mut_obj_rename_keyn(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + size_t len, + const char *new_key, + size_t new_len); + + + +#if !defined(YYJSON_DISABLE_UTILS) || !YYJSON_DISABLE_UTILS + +/*============================================================================== + * MARK: - JSON Pointer API (RFC 6901) + * https://tools.ietf.org/html/rfc6901 + *============================================================================*/ + +/** JSON Pointer error code. */ +typedef uint32_t yyjson_ptr_code; + +/** No JSON pointer error. */ +static const yyjson_ptr_code YYJSON_PTR_ERR_NONE = 0; + +/** Invalid input parameter, such as NULL input. */ +static const yyjson_ptr_code YYJSON_PTR_ERR_PARAMETER = 1; + +/** JSON pointer syntax error, such as invalid escape, token no prefix. */ +static const yyjson_ptr_code YYJSON_PTR_ERR_SYNTAX = 2; + +/** JSON pointer resolve failed, such as index out of range, key not found. */ +static const yyjson_ptr_code YYJSON_PTR_ERR_RESOLVE = 3; + +/** Document's root is NULL, but it is required for the function call. */ +static const yyjson_ptr_code YYJSON_PTR_ERR_NULL_ROOT = 4; + +/** Cannot set root as the target is not a document. */ +static const yyjson_ptr_code YYJSON_PTR_ERR_SET_ROOT = 5; + +/** The memory allocation failed and a new value could not be created. */ +static const yyjson_ptr_code YYJSON_PTR_ERR_MEMORY_ALLOCATION = 6; + +/** Error information for JSON pointer. */ +typedef struct yyjson_ptr_err { + /** Error code, see `yyjson_ptr_code` for all possible values. */ + yyjson_ptr_code code; + /** Error message, constant, no need to free (NULL if no error). */ + const char *msg; + /** Error byte position for input JSON pointer (0 if no error). */ + size_t pos; +} yyjson_ptr_err; + +/** + A context for JSON pointer operation. + + This struct stores the context of JSON Pointer operation result. The struct + can be used with three helper functions: `ctx_append()`, `ctx_replace()`, and + `ctx_remove()`, which perform the corresponding operations on the container + without re-parsing the JSON Pointer. + + For example: + @code + // doc before: {"a":[0,1,null]} + // ptr: "/a/2" + val = yyjson_mut_doc_ptr_getx(doc, ptr, strlen(ptr), &ctx, &err); + if (yyjson_is_null(val)) { + yyjson_ptr_ctx_remove(&ctx); + } + // doc after: {"a":[0,1]} + @endcode + */ +typedef struct yyjson_ptr_ctx { + /** + The container (parent) of the target value. It can be either an array or + an object. If the target location has no value, but all its parent + containers exist, and the target location can be used to insert a new + value, then `ctn` is the parent container of the target location. + Otherwise, `ctn` is NULL. + */ + yyjson_mut_val *ctn; + /** + The previous sibling of the target value. It can be either a value in an + array or a key in an object. As the container is a `circular linked list` + of elements, `pre` is the previous node of the target value. If the + operation is `add` or `set`, then `pre` is the previous node of the new + value, not the original target value. If the target value does not exist, + `pre` is NULL. + */ + yyjson_mut_val *pre; + /** + The removed value if the operation is `set`, `replace` or `remove`. It can + be used to restore the original state of the document if needed. + */ + yyjson_mut_val *old; +} yyjson_ptr_ctx; + +/** + Get value by a JSON Pointer. + @param doc The JSON document to be queried. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @return The value referenced by the JSON pointer. + NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_val *yyjson_doc_ptr_get(yyjson_doc *doc, + const char *ptr); + +/** + Get value by a JSON Pointer. + @param doc The JSON document to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @return The value referenced by the JSON pointer. + NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getn(yyjson_doc *doc, + const char *ptr, size_t len); + +/** + Get value by a JSON Pointer. + @param doc The JSON document to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param err A pointer to store the error information, or NULL if not needed. + @return The value referenced by the JSON pointer. + NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(yyjson_doc *doc, + const char *ptr, size_t len, + yyjson_ptr_err *err); + +/** + Get value by a JSON Pointer. + @param val The JSON value to be queried. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @return The value referenced by the JSON pointer. + NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_val *yyjson_ptr_get(yyjson_val *val, + const char *ptr); + +/** + Get value by a JSON Pointer. + @param val The JSON value to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @return The value referenced by the JSON pointer. + NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_val *yyjson_ptr_getn(yyjson_val *val, + const char *ptr, size_t len); + +/** + Get value by a JSON Pointer. + @param val The JSON value to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param err A pointer to store the error information, or NULL if not needed. + @return The value referenced by the JSON pointer. + NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_val *yyjson_ptr_getx(yyjson_val *val, + const char *ptr, size_t len, + yyjson_ptr_err *err); + +/** + Get value by a JSON Pointer. + @param doc The JSON document to be queried. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @return The value referenced by the JSON pointer. + NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_get(yyjson_mut_doc *doc, + const char *ptr); + +/** + Get value by a JSON Pointer. + @param doc The JSON document to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @return The value referenced by the JSON pointer. + NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getn(yyjson_mut_doc *doc, + const char *ptr, + size_t len); + +/** + Get value by a JSON Pointer. + @param doc The JSON document to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return The value referenced by the JSON pointer. + NULL if `doc` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx(yyjson_mut_doc *doc, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/** + Get value by a JSON Pointer. + @param val The JSON value to be queried. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @return The value referenced by the JSON pointer. + NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_get(yyjson_mut_val *val, + const char *ptr); + +/** + Get value by a JSON Pointer. + @param val The JSON value to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @return The value referenced by the JSON pointer. + NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getn(yyjson_mut_val *val, + const char *ptr, + size_t len); + +/** + Get value by a JSON Pointer. + @param val The JSON value to be queried. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return The value referenced by the JSON pointer. + NULL if `val` or `ptr` is NULL, or the JSON pointer cannot be resolved. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getx(yyjson_mut_val *val, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/** + Add (insert) value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @param new_val The value to be added. + @return true if JSON pointer is valid and new value is added, false otherwise. + @note The parent nodes will be created if they do not exist. + */ +yyjson_api_inline bool yyjson_mut_doc_ptr_add(yyjson_mut_doc *doc, + const char *ptr, + yyjson_mut_val *new_val); + +/** + Add (insert) value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The value to be added. + @return true if JSON pointer is valid and new value is added, false otherwise. + @note The parent nodes will be created if they do not exist. + */ +yyjson_api_inline bool yyjson_mut_doc_ptr_addn(yyjson_mut_doc *doc, + const char *ptr, size_t len, + yyjson_mut_val *new_val); + +/** + Add (insert) value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The value to be added. + @param create_parent Whether to create parent nodes if not exist. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return true if JSON pointer is valid and new value is added, false otherwise. + */ +yyjson_api_inline bool yyjson_mut_doc_ptr_addx(yyjson_mut_doc *doc, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/** + Add (insert) value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @param doc Only used to create new values when needed. + @param new_val The value to be added. + @return true if JSON pointer is valid and new value is added, false otherwise. + @note The parent nodes will be created if they do not exist. + */ +yyjson_api_inline bool yyjson_mut_ptr_add(yyjson_mut_val *val, + const char *ptr, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc); + +/** + Add (insert) value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param doc Only used to create new values when needed. + @param new_val The value to be added. + @return true if JSON pointer is valid and new value is added, false otherwise. + @note The parent nodes will be created if they do not exist. + */ +yyjson_api_inline bool yyjson_mut_ptr_addn(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc); + +/** + Add (insert) value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param doc Only used to create new values when needed. + @param new_val The value to be added. + @param create_parent Whether to create parent nodes if not exist. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return true if JSON pointer is valid and new value is added, false otherwise. + */ +yyjson_api_inline bool yyjson_mut_ptr_addx(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/** + Set value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @param new_val The value to be set, pass NULL to remove. + @return true if JSON pointer is valid and new value is set, false otherwise. + @note The parent nodes will be created if they do not exist. + If the target value already exists, it will be replaced by the new value. + */ +yyjson_api_inline bool yyjson_mut_doc_ptr_set(yyjson_mut_doc *doc, + const char *ptr, + yyjson_mut_val *new_val); + +/** + Set value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The value to be set, pass NULL to remove. + @return true if JSON pointer is valid and new value is set, false otherwise. + @note The parent nodes will be created if they do not exist. + If the target value already exists, it will be replaced by the new value. + */ +yyjson_api_inline bool yyjson_mut_doc_ptr_setn(yyjson_mut_doc *doc, + const char *ptr, size_t len, + yyjson_mut_val *new_val); + +/** + Set value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The value to be set, pass NULL to remove. + @param create_parent Whether to create parent nodes if not exist. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return true if JSON pointer is valid and new value is set, false otherwise. + @note If the target value already exists, it will be replaced by the new value. + */ +yyjson_api_inline bool yyjson_mut_doc_ptr_setx(yyjson_mut_doc *doc, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/** + Set value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @param new_val The value to be set, pass NULL to remove. + @param doc Only used to create new values when needed. + @return true if JSON pointer is valid and new value is set, false otherwise. + @note The parent nodes will be created if they do not exist. + If the target value already exists, it will be replaced by the new value. + */ +yyjson_api_inline bool yyjson_mut_ptr_set(yyjson_mut_val *val, + const char *ptr, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc); + +/** + Set value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The value to be set, pass NULL to remove. + @param doc Only used to create new values when needed. + @return true if JSON pointer is valid and new value is set, false otherwise. + @note The parent nodes will be created if they do not exist. + If the target value already exists, it will be replaced by the new value. + */ +yyjson_api_inline bool yyjson_mut_ptr_setn(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc); + +/** + Set value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The value to be set, pass NULL to remove. + @param doc Only used to create new values when needed. + @param create_parent Whether to create parent nodes if not exist. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return true if JSON pointer is valid and new value is set, false otherwise. + @note If the target value already exists, it will be replaced by the new value. + */ +yyjson_api_inline bool yyjson_mut_ptr_setx(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/** + Replace value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @param new_val The new value to replace the old one. + @return The old value that was replaced, or NULL if not found. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_replace( + yyjson_mut_doc *doc, const char *ptr, yyjson_mut_val *new_val); + +/** + Replace value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The new value to replace the old one. + @return The old value that was replaced, or NULL if not found. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_replacen( + yyjson_mut_doc *doc, const char *ptr, size_t len, yyjson_mut_val *new_val); + +/** + Replace value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The new value to replace the old one. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return The old value that was replaced, or NULL if not found. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_replacex( + yyjson_mut_doc *doc, const char *ptr, size_t len, yyjson_mut_val *new_val, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err); + +/** + Replace value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @param new_val The new value to replace the old one. + @return The old value that was replaced, or NULL if not found. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_replace( + yyjson_mut_val *val, const char *ptr, yyjson_mut_val *new_val); + +/** + Replace value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The new value to replace the old one. + @return The old value that was replaced, or NULL if not found. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_replacen( + yyjson_mut_val *val, const char *ptr, size_t len, yyjson_mut_val *new_val); + +/** + Replace value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param new_val The new value to replace the old one. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return The old value that was replaced, or NULL if not found. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_replacex( + yyjson_mut_val *val, const char *ptr, size_t len, yyjson_mut_val *new_val, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err); + +/** + Remove value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @return The removed value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_remove( + yyjson_mut_doc *doc, const char *ptr); + +/** + Remove value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @return The removed value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_removen( + yyjson_mut_doc *doc, const char *ptr, size_t len); + +/** + Remove value by a JSON pointer. + @param doc The target JSON document. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return The removed value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_removex( + yyjson_mut_doc *doc, const char *ptr, size_t len, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err); + +/** + Remove value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8 with null-terminator). + @return The removed value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_remove(yyjson_mut_val *val, + const char *ptr); + +/** + Remove value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @return The removed value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_removen(yyjson_mut_val *val, + const char *ptr, + size_t len); + +/** + Remove value by a JSON pointer. + @param val The target JSON value. + @param ptr The JSON pointer string (UTF-8, null-terminator is not required). + @param len The length of `ptr` in bytes. + @param ctx A pointer to store the result context, or NULL if not needed. + @param err A pointer to store the error information, or NULL if not needed. + @return The removed value, or NULL on error. + */ +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_removex(yyjson_mut_val *val, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/** + Append value by JSON pointer context. + @param ctx The context from the `yyjson_mut_ptr_xxx()` calls. + @param key New key if `ctx->ctn` is object, or NULL if `ctx->ctn` is array. + @param val New value to be added. + @return true on success or false on fail. + */ +yyjson_api_inline bool yyjson_ptr_ctx_append(yyjson_ptr_ctx *ctx, + yyjson_mut_val *key, + yyjson_mut_val *val); + +/** + Replace value by JSON pointer context. + @param ctx The context from the `yyjson_mut_ptr_xxx()` calls. + @param val New value to be replaced. + @return true on success or false on fail. + @note If success, the old value will be returned via `ctx->old`. + */ +yyjson_api_inline bool yyjson_ptr_ctx_replace(yyjson_ptr_ctx *ctx, + yyjson_mut_val *val); + +/** + Remove value by JSON pointer context. + @param ctx The context from the `yyjson_mut_ptr_xxx()` calls. + @return true on success or false on fail. + @note If success, the old value will be returned via `ctx->old`. + */ +yyjson_api_inline bool yyjson_ptr_ctx_remove(yyjson_ptr_ctx *ctx); + + + +/*============================================================================== + * MARK: - JSON Patch API (RFC 6902) + * https://tools.ietf.org/html/rfc6902 + *============================================================================*/ + +/** Result code for JSON patch. */ +typedef uint32_t yyjson_patch_code; + +/** Success, no error. */ +static const yyjson_patch_code YYJSON_PATCH_SUCCESS = 0; + +/** Invalid parameter, such as NULL input or non-array patch. */ +static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_PARAMETER = 1; + +/** Memory allocation failure occurs. */ +static const yyjson_patch_code YYJSON_PATCH_ERROR_MEMORY_ALLOCATION = 2; + +/** JSON patch operation is not object type. */ +static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_OPERATION = 3; + +/** JSON patch operation is missing a required key. */ +static const yyjson_patch_code YYJSON_PATCH_ERROR_MISSING_KEY = 4; + +/** JSON patch operation member is invalid. */ +static const yyjson_patch_code YYJSON_PATCH_ERROR_INVALID_MEMBER = 5; + +/** JSON patch operation `test` not equal. */ +static const yyjson_patch_code YYJSON_PATCH_ERROR_EQUAL = 6; + +/** JSON patch operation failed on JSON pointer. */ +static const yyjson_patch_code YYJSON_PATCH_ERROR_POINTER = 7; + +/** Error information for JSON patch. */ +typedef struct yyjson_patch_err { + /** Error code, see `yyjson_patch_code` for all possible values. */ + yyjson_patch_code code; + /** Index of the error operation (0 if no error). */ + size_t idx; + /** Error message, constant, no need to free (NULL if no error). */ + const char *msg; + /** JSON pointer error if `code == YYJSON_PATCH_ERROR_POINTER`. */ + yyjson_ptr_err ptr; +} yyjson_patch_err; + +/** + Creates and returns a patched JSON value (RFC 6902). + The memory of the returned value is allocated by the `doc`. + The `err` is used to receive error information, pass NULL if not needed. + Returns NULL if the patch could not be applied. + */ +yyjson_api yyjson_mut_val *yyjson_patch(yyjson_mut_doc *doc, + yyjson_val *orig, + yyjson_val *patch, + yyjson_patch_err *err); + +/** + Creates and returns a patched JSON value (RFC 6902). + The memory of the returned value is allocated by the `doc`. + The `err` is used to receive error information, pass NULL if not needed. + Returns NULL if the patch could not be applied. + */ +yyjson_api yyjson_mut_val *yyjson_mut_patch(yyjson_mut_doc *doc, + yyjson_mut_val *orig, + yyjson_mut_val *patch, + yyjson_patch_err *err); + + + +/*============================================================================== + * MARK: - JSON Merge-Patch API (RFC 7386) + * https://tools.ietf.org/html/rfc7386 + *============================================================================*/ + +/** + Creates and returns a merge-patched JSON value (RFC 7386). + The memory of the returned value is allocated by the `doc`. + Returns NULL if the patch could not be applied. + + @warning This function is recursive and may cause a stack overflow if the + object level is too deep. + */ +yyjson_api yyjson_mut_val *yyjson_merge_patch(yyjson_mut_doc *doc, + yyjson_val *orig, + yyjson_val *patch); + +/** + Creates and returns a merge-patched JSON value (RFC 7386). + The memory of the returned value is allocated by the `doc`. + Returns NULL if the patch could not be applied. + + @warning This function is recursive and may cause a stack overflow if the + object level is too deep. + */ +yyjson_api yyjson_mut_val *yyjson_mut_merge_patch(yyjson_mut_doc *doc, + yyjson_mut_val *orig, + yyjson_mut_val *patch); + +#endif /* YYJSON_DISABLE_UTILS */ + + + +/*============================================================================== + * MARK: - JSON Structure (Implementation) + *============================================================================*/ + +/** Payload of a JSON value (8 bytes). */ +typedef union yyjson_val_uni { + uint64_t u64; + int64_t i64; + double f64; + const char *str; + void *ptr; + size_t ofs; +} yyjson_val_uni; + +/** + Immutable JSON value, 16 bytes. + */ +struct yyjson_val { + uint64_t tag; /**< type, subtype and length */ + yyjson_val_uni uni; /**< payload */ +}; + +struct yyjson_doc { + /** Root value of the document (nonnull). */ + yyjson_val *root; + /** Allocator used by document (nonnull). */ + yyjson_alc alc; + /** The total number of bytes read when parsing JSON (nonzero). */ + size_t dat_read; + /** The total number of value read when parsing JSON (nonzero). */ + size_t val_read; + /** The string pool used by JSON values (nullable). */ + char *str_pool; +}; + + + +/*============================================================================== + * MARK: - Unsafe JSON Value API (Implementation) + *============================================================================*/ + +/* + Whether the string does not need to be escaped for serialization. + This function is used to optimize the writing speed of small constant strings. + This function works only if the compiler can evaluate it at compile time. + + Clang supports it since v8.0, + earlier versions do not support constant_p(strlen) and return false. + GCC supports it since at least v4.4, + earlier versions may compile it as run-time instructions. + ICC supports it since at least v16, + earlier versions are uncertain. + + @param str The C string. + @param len The returnd value from strlen(str). + */ +yyjson_api_inline bool unsafe_yyjson_is_str_noesc(const char *str, size_t len) { +#if YYJSON_HAS_CONSTANT_P && \ + (!YYJSON_IS_REAL_GCC || yyjson_gcc_available(4, 4, 0)) + if (yyjson_constant_p(len) && len <= 32) { + /* + Same as the following loop: + + for (size_t i = 0; i < len; i++) { + char c = str[i]; + if (c < ' ' || c > '~' || c == '"' || c == '\\') return false; + } + + GCC evaluates it at compile time only if the string length is within 17 + and -O3 (which turns on the -fpeel-loops flag) is used. + So the loop is unrolled for GCC. + */ +# define yyjson_repeat32_incr(x) \ + x(0) x(1) x(2) x(3) x(4) x(5) x(6) x(7) \ + x(8) x(9) x(10) x(11) x(12) x(13) x(14) x(15) \ + x(16) x(17) x(18) x(19) x(20) x(21) x(22) x(23) \ + x(24) x(25) x(26) x(27) x(28) x(29) x(30) x(31) +# define yyjson_check_char_noesc(i) \ + if (i < len) { \ + char c = str[i]; \ + if (c < ' ' || c > '~' || c == '"' || c == '\\') return false; } + yyjson_repeat32_incr(yyjson_check_char_noesc) +# undef yyjson_repeat32_incr +# undef yyjson_check_char_noesc + return true; + } +#else + (void)str; + (void)len; +#endif + return false; +} + +yyjson_api_inline double unsafe_yyjson_u64_to_f64(uint64_t num) { +#if YYJSON_U64_TO_F64_NO_IMPL + uint64_t msb = ((uint64_t)1) << 63; + if ((num & msb) == 0) { + return (double)(int64_t)num; + } else { + return ((double)(int64_t)((num >> 1) | (num & 1))) * (double)2.0; + } +#else + return (double)num; +#endif +} + +yyjson_api_inline yyjson_type unsafe_yyjson_get_type(void *val) { + uint8_t tag = (uint8_t)((yyjson_val *)val)->tag; + return (yyjson_type)(tag & YYJSON_TYPE_MASK); +} + +yyjson_api_inline yyjson_subtype unsafe_yyjson_get_subtype(void *val) { + uint8_t tag = (uint8_t)((yyjson_val *)val)->tag; + return (yyjson_subtype)(tag & YYJSON_SUBTYPE_MASK); +} + +yyjson_api_inline uint8_t unsafe_yyjson_get_tag(void *val) { + uint8_t tag = (uint8_t)((yyjson_val *)val)->tag; + return (uint8_t)(tag & YYJSON_TAG_MASK); +} + +yyjson_api_inline bool unsafe_yyjson_is_raw(void *val) { + return unsafe_yyjson_get_type(val) == YYJSON_TYPE_RAW; +} + +yyjson_api_inline bool unsafe_yyjson_is_null(void *val) { + return unsafe_yyjson_get_type(val) == YYJSON_TYPE_NULL; +} + +yyjson_api_inline bool unsafe_yyjson_is_bool(void *val) { + return unsafe_yyjson_get_type(val) == YYJSON_TYPE_BOOL; +} + +yyjson_api_inline bool unsafe_yyjson_is_num(void *val) { + return unsafe_yyjson_get_type(val) == YYJSON_TYPE_NUM; +} + +yyjson_api_inline bool unsafe_yyjson_is_str(void *val) { + return unsafe_yyjson_get_type(val) == YYJSON_TYPE_STR; +} + +yyjson_api_inline bool unsafe_yyjson_is_arr(void *val) { + return unsafe_yyjson_get_type(val) == YYJSON_TYPE_ARR; +} + +yyjson_api_inline bool unsafe_yyjson_is_obj(void *val) { + return unsafe_yyjson_get_type(val) == YYJSON_TYPE_OBJ; +} + +yyjson_api_inline bool unsafe_yyjson_is_ctn(void *val) { + uint8_t mask = YYJSON_TYPE_ARR & YYJSON_TYPE_OBJ; + return (unsafe_yyjson_get_tag(val) & mask) == mask; +} + +yyjson_api_inline bool unsafe_yyjson_is_uint(void *val) { + const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT; + return unsafe_yyjson_get_tag(val) == patt; +} + +yyjson_api_inline bool unsafe_yyjson_is_sint(void *val) { + const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT; + return unsafe_yyjson_get_tag(val) == patt; +} + +yyjson_api_inline bool unsafe_yyjson_is_int(void *val) { + const uint8_t mask = YYJSON_TAG_MASK & (~YYJSON_SUBTYPE_SINT); + const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT; + return (unsafe_yyjson_get_tag(val) & mask) == patt; +} + +yyjson_api_inline bool unsafe_yyjson_is_real(void *val) { + const uint8_t patt = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; + return unsafe_yyjson_get_tag(val) == patt; +} + +yyjson_api_inline bool unsafe_yyjson_is_true(void *val) { + const uint8_t patt = YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_TRUE; + return unsafe_yyjson_get_tag(val) == patt; +} + +yyjson_api_inline bool unsafe_yyjson_is_false(void *val) { + const uint8_t patt = YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_FALSE; + return unsafe_yyjson_get_tag(val) == patt; +} + +yyjson_api_inline bool unsafe_yyjson_arr_is_flat(yyjson_val *val) { + size_t ofs = val->uni.ofs; + size_t len = (size_t)(val->tag >> YYJSON_TAG_BIT); + return len * sizeof(yyjson_val) + sizeof(yyjson_val) == ofs; +} + +yyjson_api_inline const char *unsafe_yyjson_get_raw(void *val) { + return ((yyjson_val *)val)->uni.str; +} + +yyjson_api_inline bool unsafe_yyjson_get_bool(void *val) { + uint8_t tag = unsafe_yyjson_get_tag(val); + return (bool)((tag & YYJSON_SUBTYPE_MASK) >> YYJSON_TYPE_BIT); +} + +yyjson_api_inline uint64_t unsafe_yyjson_get_uint(void *val) { + return ((yyjson_val *)val)->uni.u64; +} + +yyjson_api_inline int64_t unsafe_yyjson_get_sint(void *val) { + return ((yyjson_val *)val)->uni.i64; +} + +yyjson_api_inline int unsafe_yyjson_get_int(void *val) { + return (int)((yyjson_val *)val)->uni.i64; +} + +yyjson_api_inline double unsafe_yyjson_get_real(void *val) { + return ((yyjson_val *)val)->uni.f64; +} + +yyjson_api_inline double unsafe_yyjson_get_num(void *val) { + uint8_t tag = unsafe_yyjson_get_tag(val); + if (tag == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL)) { + return ((yyjson_val *)val)->uni.f64; + } else if (tag == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT)) { + return (double)((yyjson_val *)val)->uni.i64; + } else if (tag == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT)) { + return unsafe_yyjson_u64_to_f64(((yyjson_val *)val)->uni.u64); + } + return 0.0; +} + +yyjson_api_inline const char *unsafe_yyjson_get_str(void *val) { + return ((yyjson_val *)val)->uni.str; +} + +yyjson_api_inline size_t unsafe_yyjson_get_len(void *val) { + return (size_t)(((yyjson_val *)val)->tag >> YYJSON_TAG_BIT); +} + +yyjson_api_inline yyjson_val *unsafe_yyjson_get_first(yyjson_val *ctn) { + return ctn + 1; +} + +yyjson_api_inline yyjson_val *unsafe_yyjson_get_next(yyjson_val *val) { + bool is_ctn = unsafe_yyjson_is_ctn(val); + size_t ctn_ofs = val->uni.ofs; + size_t ofs = (is_ctn ? ctn_ofs : sizeof(yyjson_val)); + return (yyjson_val *)(void *)((uint8_t *)val + ofs); +} + +yyjson_api_inline bool unsafe_yyjson_equals_strn(void *val, const char *str, + size_t len) { + return unsafe_yyjson_get_len(val) == len && + memcmp(((yyjson_val *)val)->uni.str, str, len) == 0; +} + +yyjson_api_inline bool unsafe_yyjson_equals_str(void *val, const char *str) { + return unsafe_yyjson_equals_strn(val, str, strlen(str)); +} + +yyjson_api_inline void unsafe_yyjson_set_type(void *val, yyjson_type type, + yyjson_subtype subtype) { + uint8_t tag = (type | subtype); + uint64_t new_tag = ((yyjson_val *)val)->tag; + new_tag = (new_tag & (~(uint64_t)YYJSON_TAG_MASK)) | (uint64_t)tag; + ((yyjson_val *)val)->tag = new_tag; +} + +yyjson_api_inline void unsafe_yyjson_set_len(void *val, size_t len) { + uint64_t tag = ((yyjson_val *)val)->tag & YYJSON_TAG_MASK; + tag |= (uint64_t)len << YYJSON_TAG_BIT; + ((yyjson_val *)val)->tag = tag; +} + +yyjson_api_inline void unsafe_yyjson_set_tag(void *val, yyjson_type type, + yyjson_subtype subtype, + size_t len) { + uint64_t tag = (uint64_t)len << YYJSON_TAG_BIT; + tag |= (type | subtype); + ((yyjson_val *)val)->tag = tag; +} + +yyjson_api_inline void unsafe_yyjson_inc_len(void *val) { + uint64_t tag = ((yyjson_val *)val)->tag; + tag += (uint64_t)(1 << YYJSON_TAG_BIT); + ((yyjson_val *)val)->tag = tag; +} + +yyjson_api_inline void unsafe_yyjson_set_raw(void *val, const char *raw, + size_t len) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_RAW, YYJSON_SUBTYPE_NONE, len); + ((yyjson_val *)val)->uni.str = raw; +} + +yyjson_api_inline void unsafe_yyjson_set_null(void *val) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_NULL, YYJSON_SUBTYPE_NONE, 0); +} + +yyjson_api_inline void unsafe_yyjson_set_bool(void *val, bool num) { + yyjson_subtype subtype = num ? YYJSON_SUBTYPE_TRUE : YYJSON_SUBTYPE_FALSE; + unsafe_yyjson_set_tag(val, YYJSON_TYPE_BOOL, subtype, 0); +} + +yyjson_api_inline void unsafe_yyjson_set_uint(void *val, uint64_t num) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_NUM, YYJSON_SUBTYPE_UINT, 0); + ((yyjson_val *)val)->uni.u64 = num; +} + +yyjson_api_inline void unsafe_yyjson_set_sint(void *val, int64_t num) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_NUM, YYJSON_SUBTYPE_SINT, 0); + ((yyjson_val *)val)->uni.i64 = num; +} + +yyjson_api_inline void unsafe_yyjson_set_fp_to_fixed(void *val, int prec) { + ((yyjson_val *)val)->tag &= ~((uint64_t)YYJSON_WRITE_FP_TO_FIXED(15) << 32); + ((yyjson_val *)val)->tag |= (uint64_t)YYJSON_WRITE_FP_TO_FIXED(prec) << 32; +} + +yyjson_api_inline void unsafe_yyjson_set_fp_to_float(void *val, bool flt) { + uint64_t flag = (uint64_t)YYJSON_WRITE_FP_TO_FLOAT << 32; + if (flt) ((yyjson_val *)val)->tag |= flag; + else ((yyjson_val *)val)->tag &= ~flag; +} + +yyjson_api_inline void unsafe_yyjson_set_float(void *val, float num) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_NUM, YYJSON_SUBTYPE_REAL, 0); + ((yyjson_val *)val)->tag |= (uint64_t)YYJSON_WRITE_FP_TO_FLOAT << 32; + ((yyjson_val *)val)->uni.f64 = (double)num; +} + +yyjson_api_inline void unsafe_yyjson_set_double(void *val, double num) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_NUM, YYJSON_SUBTYPE_REAL, 0); + ((yyjson_val *)val)->uni.f64 = num; +} + +yyjson_api_inline void unsafe_yyjson_set_real(void *val, double num) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_NUM, YYJSON_SUBTYPE_REAL, 0); + ((yyjson_val *)val)->uni.f64 = num; +} + +yyjson_api_inline void unsafe_yyjson_set_str_noesc(void *val, bool noesc) { + ((yyjson_val *)val)->tag &= ~(uint64_t)YYJSON_SUBTYPE_MASK; + if (noesc) ((yyjson_val *)val)->tag |= (uint64_t)YYJSON_SUBTYPE_NOESC; +} + +yyjson_api_inline void unsafe_yyjson_set_strn(void *val, const char *str, + size_t len) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_STR, YYJSON_SUBTYPE_NONE, len); + ((yyjson_val *)val)->uni.str = str; +} + +yyjson_api_inline void unsafe_yyjson_set_str(void *val, const char *str) { + size_t len = strlen(str); + bool noesc = unsafe_yyjson_is_str_noesc(str, len); + yyjson_subtype subtype = noesc ? YYJSON_SUBTYPE_NOESC : YYJSON_SUBTYPE_NONE; + unsafe_yyjson_set_tag(val, YYJSON_TYPE_STR, subtype, len); + ((yyjson_val *)val)->uni.str = str; +} + +yyjson_api_inline void unsafe_yyjson_set_arr(void *val, size_t size) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_ARR, YYJSON_SUBTYPE_NONE, size); +} + +yyjson_api_inline void unsafe_yyjson_set_obj(void *val, size_t size) { + unsafe_yyjson_set_tag(val, YYJSON_TYPE_OBJ, YYJSON_SUBTYPE_NONE, size); +} + + + +/*============================================================================== + * MARK: - JSON Document API (Implementation) + *============================================================================*/ + +yyjson_api_inline yyjson_val *yyjson_doc_get_root(yyjson_doc *doc) { + return doc ? doc->root : NULL; +} + +yyjson_api_inline size_t yyjson_doc_get_read_size(yyjson_doc *doc) { + return doc ? doc->dat_read : 0; +} + +yyjson_api_inline size_t yyjson_doc_get_val_count(yyjson_doc *doc) { + return doc ? doc->val_read : 0; +} + +yyjson_api_inline void yyjson_doc_free(yyjson_doc *doc) { + if (doc) { + yyjson_alc alc = doc->alc; + memset(&doc->alc, 0, sizeof(alc)); + if (doc->str_pool) alc.free(alc.ctx, doc->str_pool); + alc.free(alc.ctx, doc); + } +} + + + +/*============================================================================== + * MARK: - JSON Value Type API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_is_raw(yyjson_val *val) { + return val ? unsafe_yyjson_is_raw(val) : false; +} + +yyjson_api_inline bool yyjson_is_null(yyjson_val *val) { + return val ? unsafe_yyjson_is_null(val) : false; +} + +yyjson_api_inline bool yyjson_is_true(yyjson_val *val) { + return val ? unsafe_yyjson_is_true(val) : false; +} + +yyjson_api_inline bool yyjson_is_false(yyjson_val *val) { + return val ? unsafe_yyjson_is_false(val) : false; +} + +yyjson_api_inline bool yyjson_is_bool(yyjson_val *val) { + return val ? unsafe_yyjson_is_bool(val) : false; +} + +yyjson_api_inline bool yyjson_is_uint(yyjson_val *val) { + return val ? unsafe_yyjson_is_uint(val) : false; +} + +yyjson_api_inline bool yyjson_is_sint(yyjson_val *val) { + return val ? unsafe_yyjson_is_sint(val) : false; +} + +yyjson_api_inline bool yyjson_is_int(yyjson_val *val) { + return val ? unsafe_yyjson_is_int(val) : false; +} + +yyjson_api_inline bool yyjson_is_real(yyjson_val *val) { + return val ? unsafe_yyjson_is_real(val) : false; +} + +yyjson_api_inline bool yyjson_is_num(yyjson_val *val) { + return val ? unsafe_yyjson_is_num(val) : false; +} + +yyjson_api_inline bool yyjson_is_str(yyjson_val *val) { + return val ? unsafe_yyjson_is_str(val) : false; +} + +yyjson_api_inline bool yyjson_is_arr(yyjson_val *val) { + return val ? unsafe_yyjson_is_arr(val) : false; +} + +yyjson_api_inline bool yyjson_is_obj(yyjson_val *val) { + return val ? unsafe_yyjson_is_obj(val) : false; +} + +yyjson_api_inline bool yyjson_is_ctn(yyjson_val *val) { + return val ? unsafe_yyjson_is_ctn(val) : false; +} + + + +/*============================================================================== + * MARK: - JSON Value Content API (Implementation) + *============================================================================*/ + +yyjson_api_inline yyjson_type yyjson_get_type(yyjson_val *val) { + return val ? unsafe_yyjson_get_type(val) : YYJSON_TYPE_NONE; +} + +yyjson_api_inline yyjson_subtype yyjson_get_subtype(yyjson_val *val) { + return val ? unsafe_yyjson_get_subtype(val) : YYJSON_SUBTYPE_NONE; +} + +yyjson_api_inline uint8_t yyjson_get_tag(yyjson_val *val) { + return val ? unsafe_yyjson_get_tag(val) : 0; +} + +yyjson_api_inline const char *yyjson_get_type_desc(yyjson_val *val) { + switch (yyjson_get_tag(val)) { + case YYJSON_TYPE_RAW | YYJSON_SUBTYPE_NONE: return "raw"; + case YYJSON_TYPE_NULL | YYJSON_SUBTYPE_NONE: return "null"; + case YYJSON_TYPE_STR | YYJSON_SUBTYPE_NONE: return "string"; + case YYJSON_TYPE_STR | YYJSON_SUBTYPE_NOESC: return "string"; + case YYJSON_TYPE_ARR | YYJSON_SUBTYPE_NONE: return "array"; + case YYJSON_TYPE_OBJ | YYJSON_SUBTYPE_NONE: return "object"; + case YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_TRUE: return "true"; + case YYJSON_TYPE_BOOL | YYJSON_SUBTYPE_FALSE: return "false"; + case YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT: return "uint"; + case YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT: return "sint"; + case YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL: return "real"; + default: return "unknown"; + } +} + +yyjson_api_inline const char *yyjson_get_raw(yyjson_val *val) { + return yyjson_is_raw(val) ? unsafe_yyjson_get_raw(val) : NULL; +} + +yyjson_api_inline bool yyjson_get_bool(yyjson_val *val) { + return yyjson_is_bool(val) ? unsafe_yyjson_get_bool(val) : false; +} + +yyjson_api_inline uint64_t yyjson_get_uint(yyjson_val *val) { + return yyjson_is_int(val) ? unsafe_yyjson_get_uint(val) : 0; +} + +yyjson_api_inline int64_t yyjson_get_sint(yyjson_val *val) { + return yyjson_is_int(val) ? unsafe_yyjson_get_sint(val) : 0; +} + +yyjson_api_inline int yyjson_get_int(yyjson_val *val) { + return yyjson_is_int(val) ? unsafe_yyjson_get_int(val) : 0; +} + +yyjson_api_inline double yyjson_get_real(yyjson_val *val) { + return yyjson_is_real(val) ? unsafe_yyjson_get_real(val) : 0.0; +} + +yyjson_api_inline double yyjson_get_num(yyjson_val *val) { + return val ? unsafe_yyjson_get_num(val) : 0.0; +} + +yyjson_api_inline const char *yyjson_get_str(yyjson_val *val) { + return yyjson_is_str(val) ? unsafe_yyjson_get_str(val) : NULL; +} + +yyjson_api_inline size_t yyjson_get_len(yyjson_val *val) { + return val ? unsafe_yyjson_get_len(val) : 0; +} + +yyjson_api_inline bool yyjson_equals_str(yyjson_val *val, const char *str) { + if (yyjson_likely(val && str)) { + return unsafe_yyjson_is_str(val) && + unsafe_yyjson_equals_str(val, str); + } + return false; +} + +yyjson_api_inline bool yyjson_equals_strn(yyjson_val *val, const char *str, + size_t len) { + if (yyjson_likely(val && str)) { + return unsafe_yyjson_is_str(val) && + unsafe_yyjson_equals_strn(val, str, len); + } + return false; +} + +yyjson_api bool unsafe_yyjson_equals(yyjson_val *lhs, yyjson_val *rhs); + +yyjson_api_inline bool yyjson_equals(yyjson_val *lhs, yyjson_val *rhs) { + if (yyjson_unlikely(!lhs || !rhs)) return false; + return unsafe_yyjson_equals(lhs, rhs); +} + +yyjson_api_inline bool yyjson_set_raw(yyjson_val *val, + const char *raw, size_t len) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_raw(val, raw, len); + return true; +} + +yyjson_api_inline bool yyjson_set_null(yyjson_val *val) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_null(val); + return true; +} + +yyjson_api_inline bool yyjson_set_bool(yyjson_val *val, bool num) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_bool(val, num); + return true; +} + +yyjson_api_inline bool yyjson_set_uint(yyjson_val *val, uint64_t num) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_uint(val, num); + return true; +} + +yyjson_api_inline bool yyjson_set_sint(yyjson_val *val, int64_t num) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_sint(val, num); + return true; +} + +yyjson_api_inline bool yyjson_set_int(yyjson_val *val, int num) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_sint(val, (int64_t)num); + return true; +} + +yyjson_api_inline bool yyjson_set_float(yyjson_val *val, float num) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_float(val, num); + return true; +} + +yyjson_api_inline bool yyjson_set_double(yyjson_val *val, double num) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_double(val, num); + return true; +} + +yyjson_api_inline bool yyjson_set_real(yyjson_val *val, double num) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + unsafe_yyjson_set_real(val, num); + return true; +} + +yyjson_api_inline bool yyjson_set_fp_to_fixed(yyjson_val *val, int prec) { + if (yyjson_unlikely(!yyjson_is_real(val))) return false; + unsafe_yyjson_set_fp_to_fixed(val, prec); + return true; +} + +yyjson_api_inline bool yyjson_set_fp_to_float(yyjson_val *val, bool flt) { + if (yyjson_unlikely(!yyjson_is_real(val))) return false; + unsafe_yyjson_set_fp_to_float(val, flt); + return true; +} + +yyjson_api_inline bool yyjson_set_str(yyjson_val *val, const char *str) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + if (yyjson_unlikely(!str)) return false; + unsafe_yyjson_set_str(val, str); + return true; +} + +yyjson_api_inline bool yyjson_set_strn(yyjson_val *val, + const char *str, size_t len) { + if (yyjson_unlikely(!val || unsafe_yyjson_is_ctn(val))) return false; + if (yyjson_unlikely(!str)) return false; + unsafe_yyjson_set_strn(val, str, len); + return true; +} + +yyjson_api_inline bool yyjson_set_str_noesc(yyjson_val *val, bool noesc) { + if (yyjson_unlikely(!yyjson_is_str(val))) return false; + unsafe_yyjson_set_str_noesc(val, noesc); + return true; +} + + + +/*============================================================================== + * MARK: - JSON Array API (Implementation) + *============================================================================*/ + +yyjson_api_inline size_t yyjson_arr_size(yyjson_val *arr) { + return yyjson_is_arr(arr) ? unsafe_yyjson_get_len(arr) : 0; +} + +yyjson_api_inline yyjson_val *yyjson_arr_get(yyjson_val *arr, size_t idx) { + if (yyjson_likely(yyjson_is_arr(arr))) { + if (yyjson_likely(unsafe_yyjson_get_len(arr) > idx)) { + yyjson_val *val = unsafe_yyjson_get_first(arr); + if (unsafe_yyjson_arr_is_flat(arr)) { + return val + idx; + } else { + while (idx-- > 0) val = unsafe_yyjson_get_next(val); + return val; + } + } + } + return NULL; +} + +yyjson_api_inline yyjson_val *yyjson_arr_get_first(yyjson_val *arr) { + if (yyjson_likely(yyjson_is_arr(arr))) { + if (yyjson_likely(unsafe_yyjson_get_len(arr) > 0)) { + return unsafe_yyjson_get_first(arr); + } + } + return NULL; +} + +yyjson_api_inline yyjson_val *yyjson_arr_get_last(yyjson_val *arr) { + if (yyjson_likely(yyjson_is_arr(arr))) { + size_t len = unsafe_yyjson_get_len(arr); + if (yyjson_likely(len > 0)) { + yyjson_val *val = unsafe_yyjson_get_first(arr); + if (unsafe_yyjson_arr_is_flat(arr)) { + return val + (len - 1); + } else { + while (len-- > 1) val = unsafe_yyjson_get_next(val); + return val; + } + } + } + return NULL; +} + + + +/*============================================================================== + * MARK: - JSON Array Iterator API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_arr_iter_init(yyjson_val *arr, + yyjson_arr_iter *iter) { + if (yyjson_likely(yyjson_is_arr(arr) && iter)) { + iter->idx = 0; + iter->max = unsafe_yyjson_get_len(arr); + iter->cur = unsafe_yyjson_get_first(arr); + return true; + } + if (iter) memset(iter, 0, sizeof(yyjson_arr_iter)); + return false; +} + +yyjson_api_inline yyjson_arr_iter yyjson_arr_iter_with(yyjson_val *arr) { + yyjson_arr_iter iter; + yyjson_arr_iter_init(arr, &iter); + return iter; +} + +yyjson_api_inline bool yyjson_arr_iter_has_next(yyjson_arr_iter *iter) { + return iter ? iter->idx < iter->max : false; +} + +yyjson_api_inline yyjson_val *yyjson_arr_iter_next(yyjson_arr_iter *iter) { + yyjson_val *val; + if (iter && iter->idx < iter->max) { + val = iter->cur; + iter->cur = unsafe_yyjson_get_next(val); + iter->idx++; + return val; + } + return NULL; +} + + + +/*============================================================================== + * MARK: - JSON Object API (Implementation) + *============================================================================*/ + +yyjson_api_inline size_t yyjson_obj_size(yyjson_val *obj) { + return yyjson_is_obj(obj) ? unsafe_yyjson_get_len(obj) : 0; +} + +yyjson_api_inline yyjson_val *yyjson_obj_get(yyjson_val *obj, + const char *key) { + return yyjson_obj_getn(obj, key, key ? strlen(key) : 0); +} + +yyjson_api_inline yyjson_val *yyjson_obj_getn(yyjson_val *obj, + const char *_key, + size_t key_len) { + if (yyjson_likely(yyjson_is_obj(obj) && _key)) { + size_t len = unsafe_yyjson_get_len(obj); + yyjson_val *key = unsafe_yyjson_get_first(obj); + while (len-- > 0) { + if (unsafe_yyjson_equals_strn(key, _key, key_len)) return key + 1; + key = unsafe_yyjson_get_next(key + 1); + } + } + return NULL; +} + + + +/*============================================================================== + * MARK: - JSON Object Iterator API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_obj_iter_init(yyjson_val *obj, + yyjson_obj_iter *iter) { + if (yyjson_likely(yyjson_is_obj(obj) && iter)) { + iter->idx = 0; + iter->max = unsafe_yyjson_get_len(obj); + iter->cur = unsafe_yyjson_get_first(obj); + iter->obj = obj; + return true; + } + if (iter) memset(iter, 0, sizeof(yyjson_obj_iter)); + return false; +} + +yyjson_api_inline yyjson_obj_iter yyjson_obj_iter_with(yyjson_val *obj) { + yyjson_obj_iter iter; + yyjson_obj_iter_init(obj, &iter); + return iter; +} + +yyjson_api_inline bool yyjson_obj_iter_has_next(yyjson_obj_iter *iter) { + return iter ? iter->idx < iter->max : false; +} + +yyjson_api_inline yyjson_val *yyjson_obj_iter_next(yyjson_obj_iter *iter) { + if (iter && iter->idx < iter->max) { + yyjson_val *key = iter->cur; + iter->idx++; + iter->cur = unsafe_yyjson_get_next(key + 1); + return key; + } + return NULL; +} + +yyjson_api_inline yyjson_val *yyjson_obj_iter_get_val(yyjson_val *key) { + return key ? key + 1 : NULL; +} + +yyjson_api_inline yyjson_val *yyjson_obj_iter_get(yyjson_obj_iter *iter, + const char *key) { + return yyjson_obj_iter_getn(iter, key, key ? strlen(key) : 0); +} + +yyjson_api_inline yyjson_val *yyjson_obj_iter_getn(yyjson_obj_iter *iter, + const char *key, + size_t key_len) { + if (iter && key) { + size_t idx = iter->idx; + size_t max = iter->max; + yyjson_val *cur = iter->cur; + if (yyjson_unlikely(idx == max)) { + idx = 0; + cur = unsafe_yyjson_get_first(iter->obj); + } + while (idx++ < max) { + yyjson_val *next = unsafe_yyjson_get_next(cur + 1); + if (unsafe_yyjson_equals_strn(cur, key, key_len)) { + iter->idx = idx; + iter->cur = next; + return cur + 1; + } + cur = next; + if (idx == iter->max && iter->idx < iter->max) { + idx = 0; + max = iter->idx; + cur = unsafe_yyjson_get_first(iter->obj); + } + } + } + return NULL; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Structure (Implementation) + *============================================================================*/ + +/** + Mutable JSON value, 24 bytes. + The 'tag' and 'uni' field is same as immutable value. + The 'next' field links all elements inside the container to be a cycle. + */ +struct yyjson_mut_val { + uint64_t tag; /**< type, subtype and length */ + yyjson_val_uni uni; /**< payload */ + yyjson_mut_val *next; /**< the next value in circular linked list */ +}; + +/** + A memory chunk in string memory pool. + */ +typedef struct yyjson_str_chunk { + struct yyjson_str_chunk *next; /* next chunk linked list */ + size_t chunk_size; /* chunk size in bytes */ + /* char str[]; flexible array member */ +} yyjson_str_chunk; + +/** + A memory pool to hold all strings in a mutable document. + */ +typedef struct yyjson_str_pool { + char *cur; /* cursor inside current chunk */ + char *end; /* the end of current chunk */ + size_t chunk_size; /* chunk size in bytes while creating new chunk */ + size_t chunk_size_max; /* maximum chunk size in bytes */ + yyjson_str_chunk *chunks; /* a linked list of chunks, nullable */ +} yyjson_str_pool; + +/** + A memory chunk in value memory pool. + `sizeof(yyjson_val_chunk)` should not larger than `sizeof(yyjson_mut_val)`. + */ +typedef struct yyjson_val_chunk { + struct yyjson_val_chunk *next; /* next chunk linked list */ + size_t chunk_size; /* chunk size in bytes */ + /* char pad[sizeof(yyjson_mut_val) - sizeof(yyjson_val_chunk)]; padding */ + /* yyjson_mut_val vals[]; flexible array member */ +} yyjson_val_chunk; + +/** + A memory pool to hold all values in a mutable document. + */ +typedef struct yyjson_val_pool { + yyjson_mut_val *cur; /* cursor inside current chunk */ + yyjson_mut_val *end; /* the end of current chunk */ + size_t chunk_size; /* chunk size in bytes while creating new chunk */ + size_t chunk_size_max; /* maximum chunk size in bytes */ + yyjson_val_chunk *chunks; /* a linked list of chunks, nullable */ +} yyjson_val_pool; + +struct yyjson_mut_doc { + yyjson_mut_val *root; /**< root value of the JSON document, nullable */ + yyjson_alc alc; /**< a valid allocator, nonnull */ + yyjson_str_pool str_pool; /**< string memory pool */ + yyjson_val_pool val_pool; /**< value memory pool */ +}; + +/* Ensures the capacity to at least equal to the specified byte length. */ +yyjson_api bool unsafe_yyjson_str_pool_grow(yyjson_str_pool *pool, + const yyjson_alc *alc, + size_t len); + +/* Ensures the capacity to at least equal to the specified value count. */ +yyjson_api bool unsafe_yyjson_val_pool_grow(yyjson_val_pool *pool, + const yyjson_alc *alc, + size_t count); + +/* Allocate memory for string. */ +yyjson_api_inline char *unsafe_yyjson_mut_str_alc(yyjson_mut_doc *doc, + size_t len) { + char *mem; + const yyjson_alc *alc = &doc->alc; + yyjson_str_pool *pool = &doc->str_pool; + if (yyjson_unlikely((size_t)(pool->end - pool->cur) <= len)) { + if (yyjson_unlikely(!unsafe_yyjson_str_pool_grow(pool, alc, len + 1))) { + return NULL; + } + } + mem = pool->cur; + pool->cur = mem + len + 1; + return mem; +} + +yyjson_api_inline char *unsafe_yyjson_mut_strncpy(yyjson_mut_doc *doc, + const char *str, size_t len) { + char *mem = unsafe_yyjson_mut_str_alc(doc, len); + if (yyjson_unlikely(!mem)) return NULL; + memcpy((void *)mem, (const void *)str, len); + mem[len] = '\0'; + return mem; +} + +yyjson_api_inline yyjson_mut_val *unsafe_yyjson_mut_val(yyjson_mut_doc *doc, + size_t count) { + yyjson_mut_val *val; + yyjson_alc *alc = &doc->alc; + yyjson_val_pool *pool = &doc->val_pool; + if (yyjson_unlikely((size_t)(pool->end - pool->cur) < count)) { + if (yyjson_unlikely(!unsafe_yyjson_val_pool_grow(pool, alc, count))) { + return NULL; + } + } + val = pool->cur; + pool->cur += count; + return val; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Document API (Implementation) + *============================================================================*/ + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_get_root(yyjson_mut_doc *doc) { + return doc ? doc->root : NULL; +} + +yyjson_api_inline void yyjson_mut_doc_set_root(yyjson_mut_doc *doc, + yyjson_mut_val *root) { + if (doc) doc->root = root; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Value Type API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_mut_is_raw(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_raw(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_null(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_null(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_true(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_true(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_false(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_false(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_bool(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_bool(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_uint(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_uint(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_sint(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_sint(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_int(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_int(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_real(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_real(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_num(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_num(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_str(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_str(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_arr(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_arr(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_obj(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_obj(val) : false; +} + +yyjson_api_inline bool yyjson_mut_is_ctn(yyjson_mut_val *val) { + return val ? unsafe_yyjson_is_ctn(val) : false; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Value Content API (Implementation) + *============================================================================*/ + +yyjson_api_inline yyjson_type yyjson_mut_get_type(yyjson_mut_val *val) { + return yyjson_get_type((yyjson_val *)val); +} + +yyjson_api_inline yyjson_subtype yyjson_mut_get_subtype(yyjson_mut_val *val) { + return yyjson_get_subtype((yyjson_val *)val); +} + +yyjson_api_inline uint8_t yyjson_mut_get_tag(yyjson_mut_val *val) { + return yyjson_get_tag((yyjson_val *)val); +} + +yyjson_api_inline const char *yyjson_mut_get_type_desc(yyjson_mut_val *val) { + return yyjson_get_type_desc((yyjson_val *)val); +} + +yyjson_api_inline const char *yyjson_mut_get_raw(yyjson_mut_val *val) { + return yyjson_get_raw((yyjson_val *)val); +} + +yyjson_api_inline bool yyjson_mut_get_bool(yyjson_mut_val *val) { + return yyjson_get_bool((yyjson_val *)val); +} + +yyjson_api_inline uint64_t yyjson_mut_get_uint(yyjson_mut_val *val) { + return yyjson_get_uint((yyjson_val *)val); +} + +yyjson_api_inline int64_t yyjson_mut_get_sint(yyjson_mut_val *val) { + return yyjson_get_sint((yyjson_val *)val); +} + +yyjson_api_inline int yyjson_mut_get_int(yyjson_mut_val *val) { + return yyjson_get_int((yyjson_val *)val); +} + +yyjson_api_inline double yyjson_mut_get_real(yyjson_mut_val *val) { + return yyjson_get_real((yyjson_val *)val); +} + +yyjson_api_inline double yyjson_mut_get_num(yyjson_mut_val *val) { + return yyjson_get_num((yyjson_val *)val); +} + +yyjson_api_inline const char *yyjson_mut_get_str(yyjson_mut_val *val) { + return yyjson_get_str((yyjson_val *)val); +} + +yyjson_api_inline size_t yyjson_mut_get_len(yyjson_mut_val *val) { + return yyjson_get_len((yyjson_val *)val); +} + +yyjson_api_inline bool yyjson_mut_equals_str(yyjson_mut_val *val, + const char *str) { + return yyjson_equals_str((yyjson_val *)val, str); +} + +yyjson_api_inline bool yyjson_mut_equals_strn(yyjson_mut_val *val, + const char *str, size_t len) { + return yyjson_equals_strn((yyjson_val *)val, str, len); +} + +yyjson_api bool unsafe_yyjson_mut_equals(yyjson_mut_val *lhs, + yyjson_mut_val *rhs); + +yyjson_api_inline bool yyjson_mut_equals(yyjson_mut_val *lhs, + yyjson_mut_val *rhs) { + if (yyjson_unlikely(!lhs || !rhs)) return false; + return unsafe_yyjson_mut_equals(lhs, rhs); +} + +yyjson_api_inline bool yyjson_mut_set_raw(yyjson_mut_val *val, + const char *raw, size_t len) { + if (yyjson_unlikely(!val || !raw)) return false; + unsafe_yyjson_set_raw(val, raw, len); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_null(yyjson_mut_val *val) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_null(val); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_bool(yyjson_mut_val *val, bool num) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_bool(val, num); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_uint(yyjson_mut_val *val, uint64_t num) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_uint(val, num); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_sint(yyjson_mut_val *val, int64_t num) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_sint(val, num); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_int(yyjson_mut_val *val, int num) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_sint(val, (int64_t)num); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_float(yyjson_mut_val *val, float num) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_float(val, num); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_double(yyjson_mut_val *val, double num) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_double(val, num); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_real(yyjson_mut_val *val, double num) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_real(val, num); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_fp_to_fixed(yyjson_mut_val *val, + int prec) { + if (yyjson_unlikely(!yyjson_mut_is_real(val))) return false; + unsafe_yyjson_set_fp_to_fixed(val, prec); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_fp_to_float(yyjson_mut_val *val, + bool flt) { + if (yyjson_unlikely(!yyjson_mut_is_real(val))) return false; + unsafe_yyjson_set_fp_to_float(val, flt); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_str(yyjson_mut_val *val, + const char *str) { + if (yyjson_unlikely(!val || !str)) return false; + unsafe_yyjson_set_str(val, str); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_strn(yyjson_mut_val *val, + const char *str, size_t len) { + if (yyjson_unlikely(!val || !str)) return false; + unsafe_yyjson_set_strn(val, str, len); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_str_noesc(yyjson_mut_val *val, + bool noesc) { + if (yyjson_unlikely(!yyjson_mut_is_str(val))) return false; + unsafe_yyjson_set_str_noesc(val, noesc); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_arr(yyjson_mut_val *val) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_arr(val, 0); + return true; +} + +yyjson_api_inline bool yyjson_mut_set_obj(yyjson_mut_val *val) { + if (yyjson_unlikely(!val)) return false; + unsafe_yyjson_set_obj(val, 0); + return true; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Value Creation API (Implementation) + *============================================================================*/ + +#define yyjson_mut_val_one(func) \ + if (yyjson_likely(doc)) { \ + yyjson_mut_val *val = unsafe_yyjson_mut_val(doc, 1); \ + if (yyjson_likely(val)) { \ + func \ + return val; \ + } \ + } \ + return NULL + +#define yyjson_mut_val_one_str(func) \ + if (yyjson_likely(doc && str)) { \ + yyjson_mut_val *val = unsafe_yyjson_mut_val(doc, 1); \ + if (yyjson_likely(val)) { \ + func \ + return val; \ + } \ + } \ + return NULL + +yyjson_api_inline yyjson_mut_val *yyjson_mut_raw(yyjson_mut_doc *doc, + const char *str) { + yyjson_mut_val_one_str({ unsafe_yyjson_set_raw(val, str, strlen(str)); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_rawn(yyjson_mut_doc *doc, + const char *str, + size_t len) { + yyjson_mut_val_one_str({ unsafe_yyjson_set_raw(val, str, len); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_rawcpy(yyjson_mut_doc *doc, + const char *str) { + yyjson_mut_val_one_str({ + size_t len = strlen(str); + char *new_str = unsafe_yyjson_mut_strncpy(doc, str, len); + if (yyjson_unlikely(!new_str)) return NULL; + unsafe_yyjson_set_raw(val, new_str, len); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_rawncpy(yyjson_mut_doc *doc, + const char *str, + size_t len) { + yyjson_mut_val_one_str({ + char *new_str = unsafe_yyjson_mut_strncpy(doc, str, len); + if (yyjson_unlikely(!new_str)) return NULL; + unsafe_yyjson_set_raw(val, new_str, len); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_null(yyjson_mut_doc *doc) { + yyjson_mut_val_one({ unsafe_yyjson_set_null(val); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_true(yyjson_mut_doc *doc) { + yyjson_mut_val_one({ unsafe_yyjson_set_bool(val, true); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_false(yyjson_mut_doc *doc) { + yyjson_mut_val_one({ unsafe_yyjson_set_bool(val, false); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_bool(yyjson_mut_doc *doc, + bool _val) { + yyjson_mut_val_one({ unsafe_yyjson_set_bool(val, _val); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_uint(yyjson_mut_doc *doc, + uint64_t num) { + yyjson_mut_val_one({ unsafe_yyjson_set_uint(val, num); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_sint(yyjson_mut_doc *doc, + int64_t num) { + yyjson_mut_val_one({ unsafe_yyjson_set_sint(val, num); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_int(yyjson_mut_doc *doc, + int64_t num) { + yyjson_mut_val_one({ unsafe_yyjson_set_sint(val, num); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_float(yyjson_mut_doc *doc, + float num) { + yyjson_mut_val_one({ unsafe_yyjson_set_float(val, num); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_double(yyjson_mut_doc *doc, + double num) { + yyjson_mut_val_one({ unsafe_yyjson_set_double(val, num); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_real(yyjson_mut_doc *doc, + double num) { + yyjson_mut_val_one({ unsafe_yyjson_set_real(val, num); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_str(yyjson_mut_doc *doc, + const char *str) { + yyjson_mut_val_one_str({ unsafe_yyjson_set_str(val, str); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_strn(yyjson_mut_doc *doc, + const char *str, + size_t len) { + yyjson_mut_val_one_str({ unsafe_yyjson_set_strn(val, str, len); }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_strcpy(yyjson_mut_doc *doc, + const char *str) { + yyjson_mut_val_one_str({ + size_t len = strlen(str); + bool noesc = unsafe_yyjson_is_str_noesc(str, len); + yyjson_subtype sub = noesc ? YYJSON_SUBTYPE_NOESC : YYJSON_SUBTYPE_NONE; + char *new_str = unsafe_yyjson_mut_strncpy(doc, str, len); + if (yyjson_unlikely(!new_str)) return NULL; + unsafe_yyjson_set_tag(val, YYJSON_TYPE_STR, sub, len); + val->uni.str = new_str; + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_strncpy(yyjson_mut_doc *doc, + const char *str, + size_t len) { + yyjson_mut_val_one_str({ + char *new_str = unsafe_yyjson_mut_strncpy(doc, str, len); + if (yyjson_unlikely(!new_str)) return NULL; + unsafe_yyjson_set_strn(val, new_str, len); + }); +} + +#undef yyjson_mut_val_one +#undef yyjson_mut_val_one_str + + + +/*============================================================================== + * MARK: - Mutable JSON Array API (Implementation) + *============================================================================*/ + +yyjson_api_inline size_t yyjson_mut_arr_size(yyjson_mut_val *arr) { + return yyjson_mut_is_arr(arr) ? unsafe_yyjson_get_len(arr) : 0; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get(yyjson_mut_val *arr, + size_t idx) { + if (yyjson_likely(idx < yyjson_mut_arr_size(arr))) { + yyjson_mut_val *val = (yyjson_mut_val *)arr->uni.ptr; + while (idx-- > 0) val = val->next; + return val->next; + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_first( + yyjson_mut_val *arr) { + if (yyjson_likely(yyjson_mut_arr_size(arr) > 0)) { + return ((yyjson_mut_val *)arr->uni.ptr)->next; + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_get_last( + yyjson_mut_val *arr) { + if (yyjson_likely(yyjson_mut_arr_size(arr) > 0)) { + return ((yyjson_mut_val *)arr->uni.ptr); + } + return NULL; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Array Iterator API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_mut_arr_iter_init(yyjson_mut_val *arr, + yyjson_mut_arr_iter *iter) { + if (yyjson_likely(yyjson_mut_is_arr(arr) && iter)) { + iter->idx = 0; + iter->max = unsafe_yyjson_get_len(arr); + iter->cur = iter->max ? (yyjson_mut_val *)arr->uni.ptr : NULL; + iter->pre = NULL; + iter->arr = arr; + return true; + } + if (iter) memset(iter, 0, sizeof(yyjson_mut_arr_iter)); + return false; +} + +yyjson_api_inline yyjson_mut_arr_iter yyjson_mut_arr_iter_with( + yyjson_mut_val *arr) { + yyjson_mut_arr_iter iter; + yyjson_mut_arr_iter_init(arr, &iter); + return iter; +} + +yyjson_api_inline bool yyjson_mut_arr_iter_has_next(yyjson_mut_arr_iter *iter) { + return iter ? iter->idx < iter->max : false; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_next( + yyjson_mut_arr_iter *iter) { + if (iter && iter->idx < iter->max) { + yyjson_mut_val *val = iter->cur; + iter->pre = val; + iter->cur = val->next; + iter->idx++; + return iter->cur; + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_iter_remove( + yyjson_mut_arr_iter *iter) { + if (yyjson_likely(iter && 0 < iter->idx && iter->idx <= iter->max)) { + yyjson_mut_val *prev = iter->pre; + yyjson_mut_val *cur = iter->cur; + yyjson_mut_val *next = cur->next; + if (yyjson_unlikely(iter->idx == iter->max)) iter->arr->uni.ptr = prev; + iter->idx--; + iter->max--; + unsafe_yyjson_set_len(iter->arr, iter->max); + prev->next = next; + iter->cur = prev; + return cur; + } + return NULL; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Array Creation API (Implementation) + *============================================================================*/ + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr(yyjson_mut_doc *doc) { + if (yyjson_likely(doc)) { + yyjson_mut_val *val = unsafe_yyjson_mut_val(doc, 1); + if (yyjson_likely(val)) { + val->tag = YYJSON_TYPE_ARR | YYJSON_SUBTYPE_NONE; + return val; + } + } + return NULL; +} + +#define yyjson_mut_arr_with_func(func) \ + if (yyjson_likely(doc && ((0 < count && count < \ + (~(size_t)0) / sizeof(yyjson_mut_val) && vals) || count == 0))) { \ + yyjson_mut_val *arr = unsafe_yyjson_mut_val(doc, 1 + count); \ + if (yyjson_likely(arr)) { \ + arr->tag = ((uint64_t)count << YYJSON_TAG_BIT) | YYJSON_TYPE_ARR; \ + if (count > 0) { \ + size_t i; \ + for (i = 0; i < count; i++) { \ + yyjson_mut_val *val = arr + i + 1; \ + func \ + val->next = val + 1; \ + } \ + arr[count].next = arr + 1; \ + arr->uni.ptr = arr + count; \ + } \ + return arr; \ + } \ + } \ + return NULL + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_bool( + yyjson_mut_doc *doc, const bool *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_bool(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint( + yyjson_mut_doc *doc, const int64_t *vals, size_t count) { + return yyjson_mut_arr_with_sint64(doc, vals, count); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint( + yyjson_mut_doc *doc, const uint64_t *vals, size_t count) { + return yyjson_mut_arr_with_uint64(doc, vals, count); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_real( + yyjson_mut_doc *doc, const double *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_real(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint8( + yyjson_mut_doc *doc, const int8_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_sint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint16( + yyjson_mut_doc *doc, const int16_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_sint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint32( + yyjson_mut_doc *doc, const int32_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_sint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_sint64( + yyjson_mut_doc *doc, const int64_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_sint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint8( + yyjson_mut_doc *doc, const uint8_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_uint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint16( + yyjson_mut_doc *doc, const uint16_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_uint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint32( + yyjson_mut_doc *doc, const uint32_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_uint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_uint64( + yyjson_mut_doc *doc, const uint64_t *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_uint(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_float( + yyjson_mut_doc *doc, const float *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_float(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_double( + yyjson_mut_doc *doc, const double *vals, size_t count) { + yyjson_mut_arr_with_func({ + unsafe_yyjson_set_double(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_str( + yyjson_mut_doc *doc, const char **vals, size_t count) { + yyjson_mut_arr_with_func({ + if (yyjson_unlikely(!vals[i])) return NULL; + unsafe_yyjson_set_str(val, vals[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strn( + yyjson_mut_doc *doc, const char **vals, const size_t *lens, size_t count) { + if (yyjson_unlikely(count > 0 && !lens)) return NULL; + yyjson_mut_arr_with_func({ + if (yyjson_unlikely(!vals[i])) return NULL; + unsafe_yyjson_set_strn(val, vals[i], lens[i]); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strcpy( + yyjson_mut_doc *doc, const char **vals, size_t count) { + size_t len; + const char *str, *new_str; + yyjson_mut_arr_with_func({ + str = vals[i]; + if (yyjson_unlikely(!str)) return NULL; + len = strlen(str); + new_str = unsafe_yyjson_mut_strncpy(doc, str, len); + if (yyjson_unlikely(!new_str)) return NULL; + unsafe_yyjson_set_strn(val, new_str, len); + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_with_strncpy( + yyjson_mut_doc *doc, const char **vals, const size_t *lens, size_t count) { + size_t len; + const char *str, *new_str; + if (yyjson_unlikely(count > 0 && !lens)) return NULL; + yyjson_mut_arr_with_func({ + str = vals[i]; + if (yyjson_unlikely(!str)) return NULL; + len = lens[i]; + new_str = unsafe_yyjson_mut_strncpy(doc, str, len); + if (yyjson_unlikely(!new_str)) return NULL; + unsafe_yyjson_set_strn(val, new_str, len); + }); +} + +#undef yyjson_mut_arr_with_func + + + +/*============================================================================== + * MARK: - Mutable JSON Array Modification API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_mut_arr_insert(yyjson_mut_val *arr, + yyjson_mut_val *val, size_t idx) { + if (yyjson_likely(yyjson_mut_is_arr(arr) && val)) { + size_t len = unsafe_yyjson_get_len(arr); + if (yyjson_likely(idx <= len)) { + unsafe_yyjson_set_len(arr, len + 1); + if (len == 0) { + val->next = val; + arr->uni.ptr = val; + } else { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + yyjson_mut_val *next = prev->next; + if (idx == len) { + prev->next = val; + val->next = next; + arr->uni.ptr = val; + } else { + while (idx-- > 0) { + prev = next; + next = next->next; + } + prev->next = val; + val->next = next; + } + } + return true; + } + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_append(yyjson_mut_val *arr, + yyjson_mut_val *val) { + if (yyjson_likely(yyjson_mut_is_arr(arr) && val)) { + size_t len = unsafe_yyjson_get_len(arr); + unsafe_yyjson_set_len(arr, len + 1); + if (len == 0) { + val->next = val; + } else { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + yyjson_mut_val *next = prev->next; + prev->next = val; + val->next = next; + } + arr->uni.ptr = val; + return true; + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_prepend(yyjson_mut_val *arr, + yyjson_mut_val *val) { + if (yyjson_likely(yyjson_mut_is_arr(arr) && val)) { + size_t len = unsafe_yyjson_get_len(arr); + unsafe_yyjson_set_len(arr, len + 1); + if (len == 0) { + val->next = val; + arr->uni.ptr = val; + } else { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + yyjson_mut_val *next = prev->next; + prev->next = val; + val->next = next; + } + return true; + } + return false; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_replace(yyjson_mut_val *arr, + size_t idx, + yyjson_mut_val *val) { + if (yyjson_likely(yyjson_mut_is_arr(arr) && val)) { + size_t len = unsafe_yyjson_get_len(arr); + if (yyjson_likely(idx < len)) { + if (yyjson_likely(len > 1)) { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + yyjson_mut_val *next = prev->next; + while (idx-- > 0) { + prev = next; + next = next->next; + } + prev->next = val; + val->next = next->next; + if ((void *)next == arr->uni.ptr) arr->uni.ptr = val; + return next; + } else { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + val->next = val; + arr->uni.ptr = val; + return prev; + } + } + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_remove(yyjson_mut_val *arr, + size_t idx) { + if (yyjson_likely(yyjson_mut_is_arr(arr))) { + size_t len = unsafe_yyjson_get_len(arr); + if (yyjson_likely(idx < len)) { + unsafe_yyjson_set_len(arr, len - 1); + if (yyjson_likely(len > 1)) { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + yyjson_mut_val *next = prev->next; + while (idx-- > 0) { + prev = next; + next = next->next; + } + prev->next = next->next; + if ((void *)next == arr->uni.ptr) arr->uni.ptr = prev; + return next; + } else { + return ((yyjson_mut_val *)arr->uni.ptr); + } + } + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_remove_first( + yyjson_mut_val *arr) { + if (yyjson_likely(yyjson_mut_is_arr(arr))) { + size_t len = unsafe_yyjson_get_len(arr); + if (len > 1) { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + yyjson_mut_val *next = prev->next; + prev->next = next->next; + unsafe_yyjson_set_len(arr, len - 1); + return next; + } else if (len == 1) { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + unsafe_yyjson_set_len(arr, 0); + return prev; + } + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_remove_last( + yyjson_mut_val *arr) { + if (yyjson_likely(yyjson_mut_is_arr(arr))) { + size_t len = unsafe_yyjson_get_len(arr); + if (yyjson_likely(len > 1)) { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + yyjson_mut_val *next = prev->next; + unsafe_yyjson_set_len(arr, len - 1); + while (--len > 0) prev = prev->next; + prev->next = next; + next = (yyjson_mut_val *)arr->uni.ptr; + arr->uni.ptr = prev; + return next; + } else if (len == 1) { + yyjson_mut_val *prev = ((yyjson_mut_val *)arr->uni.ptr); + unsafe_yyjson_set_len(arr, 0); + return prev; + } + } + return NULL; +} + +yyjson_api_inline bool yyjson_mut_arr_remove_range(yyjson_mut_val *arr, + size_t _idx, size_t _len) { + if (yyjson_likely(yyjson_mut_is_arr(arr))) { + yyjson_mut_val *prev, *next; + bool tail_removed; + size_t len = unsafe_yyjson_get_len(arr); + if (yyjson_unlikely(_idx + _len > len)) return false; + if (yyjson_unlikely(_len == 0)) return true; + unsafe_yyjson_set_len(arr, len - _len); + if (yyjson_unlikely(len == _len)) return true; + tail_removed = (_idx + _len == len); + prev = ((yyjson_mut_val *)arr->uni.ptr); + while (_idx-- > 0) prev = prev->next; + next = prev->next; + while (_len-- > 0) next = next->next; + prev->next = next; + if (yyjson_unlikely(tail_removed)) arr->uni.ptr = prev; + return true; + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_clear(yyjson_mut_val *arr) { + if (yyjson_likely(yyjson_mut_is_arr(arr))) { + unsafe_yyjson_set_len(arr, 0); + return true; + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_rotate(yyjson_mut_val *arr, + size_t idx) { + if (yyjson_likely(yyjson_mut_is_arr(arr) && + unsafe_yyjson_get_len(arr) > idx)) { + yyjson_mut_val *val = (yyjson_mut_val *)arr->uni.ptr; + while (idx-- > 0) val = val->next; + arr->uni.ptr = (void *)val; + return true; + } + return false; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Array Modification Convenience API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_mut_arr_add_val(yyjson_mut_val *arr, + yyjson_mut_val *val) { + return yyjson_mut_arr_append(arr, val); +} + +yyjson_api_inline bool yyjson_mut_arr_add_null(yyjson_mut_doc *doc, + yyjson_mut_val *arr) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_null(doc); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_true(yyjson_mut_doc *doc, + yyjson_mut_val *arr) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_true(doc); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_false(yyjson_mut_doc *doc, + yyjson_mut_val *arr) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_false(doc); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_bool(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + bool _val) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_bool(doc, _val); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_uint(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + uint64_t num) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_uint(doc, num); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_sint(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + int64_t num) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_sint(doc, num); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_int(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + int64_t num) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_sint(doc, num); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_float(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + float num) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_float(doc, num); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_double(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + double num) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_double(doc, num); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_real(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + double num) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_real(doc, num); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_str(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_str(doc, str); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_strn(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str, size_t len) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_strn(doc, str, len); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_strcpy(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_strcpy(doc, str); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_arr_add_strncpy(yyjson_mut_doc *doc, + yyjson_mut_val *arr, + const char *str, size_t len) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_strncpy(doc, str, len); + return yyjson_mut_arr_append(arr, val); + } + return false; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *arr) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_arr(doc); + return yyjson_mut_arr_append(arr, val) ? val : NULL; + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_arr_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *arr) { + if (yyjson_likely(doc && yyjson_mut_is_arr(arr))) { + yyjson_mut_val *val = yyjson_mut_obj(doc); + return yyjson_mut_arr_append(arr, val) ? val : NULL; + } + return NULL; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Object API (Implementation) + *============================================================================*/ + +yyjson_api_inline size_t yyjson_mut_obj_size(yyjson_mut_val *obj) { + return yyjson_mut_is_obj(obj) ? unsafe_yyjson_get_len(obj) : 0; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_get(yyjson_mut_val *obj, + const char *key) { + return yyjson_mut_obj_getn(obj, key, key ? strlen(key) : 0); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_getn(yyjson_mut_val *obj, + const char *_key, + size_t key_len) { + size_t len = yyjson_mut_obj_size(obj); + if (yyjson_likely(len && _key)) { + yyjson_mut_val *key = ((yyjson_mut_val *)obj->uni.ptr)->next->next; + while (len-- > 0) { + if (unsafe_yyjson_equals_strn(key, _key, key_len)) return key->next; + key = key->next->next; + } + } + return NULL; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Object Iterator API (Implementation) + *============================================================================*/ + +yyjson_api_inline bool yyjson_mut_obj_iter_init(yyjson_mut_val *obj, + yyjson_mut_obj_iter *iter) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && iter)) { + iter->idx = 0; + iter->max = unsafe_yyjson_get_len(obj); + iter->cur = iter->max ? (yyjson_mut_val *)obj->uni.ptr : NULL; + iter->pre = NULL; + iter->obj = obj; + return true; + } + if (iter) memset(iter, 0, sizeof(yyjson_mut_obj_iter)); + return false; +} + +yyjson_api_inline yyjson_mut_obj_iter yyjson_mut_obj_iter_with( + yyjson_mut_val *obj) { + yyjson_mut_obj_iter iter; + yyjson_mut_obj_iter_init(obj, &iter); + return iter; +} + +yyjson_api_inline bool yyjson_mut_obj_iter_has_next(yyjson_mut_obj_iter *iter) { + return iter ? iter->idx < iter->max : false; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_next( + yyjson_mut_obj_iter *iter) { + if (iter && iter->idx < iter->max) { + yyjson_mut_val *key = iter->cur; + iter->pre = key; + iter->cur = key->next->next; + iter->idx++; + return iter->cur; + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_get_val( + yyjson_mut_val *key) { + return key ? key->next : NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_remove( + yyjson_mut_obj_iter *iter) { + if (yyjson_likely(iter && 0 < iter->idx && iter->idx <= iter->max)) { + yyjson_mut_val *prev = iter->pre; + yyjson_mut_val *cur = iter->cur; + yyjson_mut_val *next = cur->next->next; + if (yyjson_unlikely(iter->idx == iter->max)) iter->obj->uni.ptr = prev; + iter->idx--; + iter->max--; + unsafe_yyjson_set_len(iter->obj, iter->max); + prev->next->next = next; + iter->cur = prev; + return cur->next; + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_get( + yyjson_mut_obj_iter *iter, const char *key) { + return yyjson_mut_obj_iter_getn(iter, key, key ? strlen(key) : 0); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_iter_getn( + yyjson_mut_obj_iter *iter, const char *key, size_t key_len) { + if (iter && key) { + size_t idx = 0; + size_t max = iter->max; + yyjson_mut_val *pre, *cur = iter->cur; + while (idx++ < max) { + pre = cur; + cur = cur->next->next; + if (unsafe_yyjson_equals_strn(cur, key, key_len)) { + iter->idx += idx; + if (iter->idx > max) iter->idx -= max + 1; + iter->pre = pre; + iter->cur = cur; + return cur->next; + } + } + } + return NULL; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Object Creation API (Implementation) + *============================================================================*/ + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj(yyjson_mut_doc *doc) { + if (yyjson_likely(doc)) { + yyjson_mut_val *val = unsafe_yyjson_mut_val(doc, 1); + if (yyjson_likely(val)) { + val->tag = YYJSON_TYPE_OBJ | YYJSON_SUBTYPE_NONE; + return val; + } + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_str(yyjson_mut_doc *doc, + const char **keys, + const char **vals, + size_t count) { + if (yyjson_likely(doc && ((count > 0 && keys && vals) || (count == 0)))) { + yyjson_mut_val *obj = unsafe_yyjson_mut_val(doc, 1 + count * 2); + if (yyjson_likely(obj)) { + obj->tag = ((uint64_t)count << YYJSON_TAG_BIT) | YYJSON_TYPE_OBJ; + if (count > 0) { + size_t i; + for (i = 0; i < count; i++) { + yyjson_mut_val *key = obj + (i * 2 + 1); + yyjson_mut_val *val = obj + (i * 2 + 2); + uint64_t key_len = (uint64_t)strlen(keys[i]); + uint64_t val_len = (uint64_t)strlen(vals[i]); + key->tag = (key_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + val->tag = (val_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + key->uni.str = keys[i]; + val->uni.str = vals[i]; + key->next = val; + val->next = val + 1; + } + obj[count * 2].next = obj + 1; + obj->uni.ptr = obj + (count * 2 - 1); + } + return obj; + } + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_with_kv(yyjson_mut_doc *doc, + const char **pairs, + size_t count) { + if (yyjson_likely(doc && ((count > 0 && pairs) || (count == 0)))) { + yyjson_mut_val *obj = unsafe_yyjson_mut_val(doc, 1 + count * 2); + if (yyjson_likely(obj)) { + obj->tag = ((uint64_t)count << YYJSON_TAG_BIT) | YYJSON_TYPE_OBJ; + if (count > 0) { + size_t i; + for (i = 0; i < count; i++) { + yyjson_mut_val *key = obj + (i * 2 + 1); + yyjson_mut_val *val = obj + (i * 2 + 2); + const char *key_str = pairs[i * 2 + 0]; + const char *val_str = pairs[i * 2 + 1]; + uint64_t key_len = (uint64_t)strlen(key_str); + uint64_t val_len = (uint64_t)strlen(val_str); + key->tag = (key_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + val->tag = (val_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + key->uni.str = key_str; + val->uni.str = val_str; + key->next = val; + val->next = val + 1; + } + obj[count * 2].next = obj + 1; + obj->uni.ptr = obj + (count * 2 - 1); + } + return obj; + } + } + return NULL; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Object Modification API (Implementation) + *============================================================================*/ + +yyjson_api_inline void unsafe_yyjson_mut_obj_add(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val, + size_t len) { + if (yyjson_likely(len)) { + yyjson_mut_val *prev_val = ((yyjson_mut_val *)obj->uni.ptr)->next; + yyjson_mut_val *next_key = prev_val->next; + prev_val->next = key; + val->next = next_key; + } else { + val->next = key; + } + key->next = val; + obj->uni.ptr = (void *)key; + unsafe_yyjson_set_len(obj, len + 1); +} + +yyjson_api_inline yyjson_mut_val *unsafe_yyjson_mut_obj_remove( + yyjson_mut_val *obj, const char *key, size_t key_len) { + size_t obj_len = unsafe_yyjson_get_len(obj); + if (obj_len) { + yyjson_mut_val *pre_key = (yyjson_mut_val *)obj->uni.ptr; + yyjson_mut_val *cur_key = pre_key->next->next; + yyjson_mut_val *removed_item = NULL; + size_t i; + for (i = 0; i < obj_len; i++) { + if (unsafe_yyjson_equals_strn(cur_key, key, key_len)) { + if (!removed_item) removed_item = cur_key->next; + cur_key = cur_key->next->next; + pre_key->next->next = cur_key; + if (i + 1 == obj_len) obj->uni.ptr = pre_key; + i--; + obj_len--; + } else { + pre_key = cur_key; + cur_key = cur_key->next->next; + } + } + unsafe_yyjson_set_len(obj, obj_len); + return removed_item; + } else { + return NULL; + } +} + +yyjson_api_inline bool unsafe_yyjson_mut_obj_replace(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val) { + size_t key_len = unsafe_yyjson_get_len(key); + size_t obj_len = unsafe_yyjson_get_len(obj); + if (obj_len) { + yyjson_mut_val *pre_key = (yyjson_mut_val *)obj->uni.ptr; + yyjson_mut_val *cur_key = pre_key->next->next; + size_t i; + for (i = 0; i < obj_len; i++) { + if (unsafe_yyjson_equals_strn(cur_key, key->uni.str, key_len)) { + cur_key->next->tag = val->tag; + cur_key->next->uni.u64 = val->uni.u64; + return true; + } else { + cur_key = cur_key->next->next; + } + } + } + return false; +} + +yyjson_api_inline void unsafe_yyjson_mut_obj_rotate(yyjson_mut_val *obj, + size_t idx) { + yyjson_mut_val *key = (yyjson_mut_val *)obj->uni.ptr; + while (idx-- > 0) key = key->next->next; + obj->uni.ptr = (void *)key; +} + +yyjson_api_inline bool yyjson_mut_obj_add(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && + yyjson_mut_is_str(key) && val)) { + unsafe_yyjson_mut_obj_add(obj, key, val, unsafe_yyjson_get_len(obj)); + return true; + } + return false; +} + +yyjson_api_inline bool yyjson_mut_obj_put(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val) { + bool replaced = false; + size_t key_len; + yyjson_mut_obj_iter iter; + yyjson_mut_val *cur_key; + if (yyjson_unlikely(!yyjson_mut_is_obj(obj) || + !yyjson_mut_is_str(key))) return false; + key_len = unsafe_yyjson_get_len(key); + yyjson_mut_obj_iter_init(obj, &iter); + while ((cur_key = yyjson_mut_obj_iter_next(&iter)) != 0) { + if (unsafe_yyjson_equals_strn(cur_key, key->uni.str, key_len)) { + if (!replaced && val) { + replaced = true; + val->next = cur_key->next->next; + cur_key->next = val; + } else { + yyjson_mut_obj_iter_remove(&iter); + } + } + } + if (!replaced && val) unsafe_yyjson_mut_obj_add(obj, key, val, iter.max); + return true; +} + +yyjson_api_inline bool yyjson_mut_obj_insert(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val, + size_t idx) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && + yyjson_mut_is_str(key) && val)) { + size_t len = unsafe_yyjson_get_len(obj); + if (yyjson_likely(len >= idx)) { + if (len > idx) { + void *ptr = obj->uni.ptr; + unsafe_yyjson_mut_obj_rotate(obj, idx); + unsafe_yyjson_mut_obj_add(obj, key, val, len); + obj->uni.ptr = ptr; + } else { + unsafe_yyjson_mut_obj_add(obj, key, val, len); + } + return true; + } + } + return false; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove(yyjson_mut_val *obj, + yyjson_mut_val *key) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && yyjson_mut_is_str(key))) { + return unsafe_yyjson_mut_obj_remove(obj, key->uni.str, + unsafe_yyjson_get_len(key)); + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_key( + yyjson_mut_val *obj, const char *key) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && key)) { + size_t key_len = strlen(key); + return unsafe_yyjson_mut_obj_remove(obj, key, key_len); + } + return NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_keyn( + yyjson_mut_val *obj, const char *key, size_t key_len) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && key)) { + return unsafe_yyjson_mut_obj_remove(obj, key, key_len); + } + return NULL; +} + +yyjson_api_inline bool yyjson_mut_obj_clear(yyjson_mut_val *obj) { + if (yyjson_likely(yyjson_mut_is_obj(obj))) { + unsafe_yyjson_set_len(obj, 0); + return true; + } + return false; +} + +yyjson_api_inline bool yyjson_mut_obj_replace(yyjson_mut_val *obj, + yyjson_mut_val *key, + yyjson_mut_val *val) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && + yyjson_mut_is_str(key) && val)) { + return unsafe_yyjson_mut_obj_replace(obj, key, val); + } + return false; +} + +yyjson_api_inline bool yyjson_mut_obj_rotate(yyjson_mut_val *obj, + size_t idx) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && + unsafe_yyjson_get_len(obj) > idx)) { + unsafe_yyjson_mut_obj_rotate(obj, idx); + return true; + } + return false; +} + + + +/*============================================================================== + * MARK: - Mutable JSON Object Modification Convenience API (Implementation) + *============================================================================*/ + +#define yyjson_mut_obj_add_func(func) \ + if (yyjson_likely(doc && yyjson_mut_is_obj(obj) && _key)) { \ + yyjson_mut_val *key = unsafe_yyjson_mut_val(doc, 2); \ + if (yyjson_likely(key)) { \ + size_t len = unsafe_yyjson_get_len(obj); \ + yyjson_mut_val *val = key + 1; \ + size_t key_len = strlen(_key); \ + bool noesc = unsafe_yyjson_is_str_noesc(_key, key_len); \ + key->tag = YYJSON_TYPE_STR; \ + key->tag |= noesc ? YYJSON_SUBTYPE_NOESC : YYJSON_SUBTYPE_NONE; \ + key->tag |= (uint64_t)strlen(_key) << YYJSON_TAG_BIT; \ + key->uni.str = _key; \ + func \ + unsafe_yyjson_mut_obj_add(obj, key, val, len); \ + return true; \ + } \ + } \ + return false + +yyjson_api_inline bool yyjson_mut_obj_add_null(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_null(val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_true(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_bool(val, true); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_false(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_bool(val, false); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_bool(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + bool _val) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_bool(val, _val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_uint(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + uint64_t _val) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_uint(val, _val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_sint(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + int64_t _val) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_sint(val, _val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_int(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + int64_t _val) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_sint(val, _val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_float(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + float _val) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_float(val, _val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_double(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + double _val) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_double(val, _val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_real(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + double _val) { + yyjson_mut_obj_add_func({ unsafe_yyjson_set_real(val, _val); }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_str(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + const char *_val) { + if (yyjson_unlikely(!_val)) return false; + yyjson_mut_obj_add_func({ + size_t val_len = strlen(_val); + bool val_noesc = unsafe_yyjson_is_str_noesc(_val, val_len); + val->tag = ((uint64_t)strlen(_val) << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + val->tag |= val_noesc ? YYJSON_SUBTYPE_NOESC : YYJSON_SUBTYPE_NONE; + val->uni.str = _val; + }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_strn(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + const char *_val, + size_t _len) { + if (yyjson_unlikely(!_val)) return false; + yyjson_mut_obj_add_func({ + val->tag = ((uint64_t)_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + val->uni.str = _val; + }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_strcpy(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + const char *_val) { + if (yyjson_unlikely(!_val)) return false; + yyjson_mut_obj_add_func({ + size_t _len = strlen(_val); + val->uni.str = unsafe_yyjson_mut_strncpy(doc, _val, _len); + if (yyjson_unlikely(!val->uni.str)) return false; + val->tag = ((uint64_t)_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + }); +} + +yyjson_api_inline bool yyjson_mut_obj_add_strncpy(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + const char *_val, + size_t _len) { + if (yyjson_unlikely(!_val)) return false; + yyjson_mut_obj_add_func({ + val->uni.str = unsafe_yyjson_mut_strncpy(doc, _val, _len); + if (yyjson_unlikely(!val->uni.str)) return false; + val->tag = ((uint64_t)_len << YYJSON_TAG_BIT) | YYJSON_TYPE_STR; + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_arr(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_val *key = yyjson_mut_str(doc, _key); + yyjson_mut_val *val = yyjson_mut_arr(doc); + return yyjson_mut_obj_add(obj, key, val) ? val : NULL; +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_add_obj(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key) { + yyjson_mut_val *key = yyjson_mut_str(doc, _key); + yyjson_mut_val *val = yyjson_mut_obj(doc); + return yyjson_mut_obj_add(obj, key, val) ? val : NULL; +} + +yyjson_api_inline bool yyjson_mut_obj_add_val(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *_key, + yyjson_mut_val *_val) { + if (yyjson_unlikely(!_val)) return false; + yyjson_mut_obj_add_func({ + val = _val; + }); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_str(yyjson_mut_val *obj, + const char *key) { + return yyjson_mut_obj_remove_strn(obj, key, key ? strlen(key) : 0); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_obj_remove_strn( + yyjson_mut_val *obj, const char *_key, size_t _len) { + if (yyjson_likely(yyjson_mut_is_obj(obj) && _key)) { + yyjson_mut_val *key; + yyjson_mut_obj_iter iter; + yyjson_mut_val *val_removed = NULL; + yyjson_mut_obj_iter_init(obj, &iter); + while ((key = yyjson_mut_obj_iter_next(&iter)) != NULL) { + if (unsafe_yyjson_equals_strn(key, _key, _len)) { + if (!val_removed) val_removed = key->next; + yyjson_mut_obj_iter_remove(&iter); + } + } + return val_removed; + } + return NULL; +} + +yyjson_api_inline bool yyjson_mut_obj_rename_key(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + const char *new_key) { + if (!key || !new_key) return false; + return yyjson_mut_obj_rename_keyn(doc, obj, key, strlen(key), + new_key, strlen(new_key)); +} + +yyjson_api_inline bool yyjson_mut_obj_rename_keyn(yyjson_mut_doc *doc, + yyjson_mut_val *obj, + const char *key, + size_t len, + const char *new_key, + size_t new_len) { + char *cpy_key = NULL; + yyjson_mut_val *old_key; + yyjson_mut_obj_iter iter; + if (!doc || !obj || !key || !new_key) return false; + yyjson_mut_obj_iter_init(obj, &iter); + while ((old_key = yyjson_mut_obj_iter_next(&iter))) { + if (unsafe_yyjson_equals_strn((void *)old_key, key, len)) { + if (!cpy_key) { + cpy_key = unsafe_yyjson_mut_strncpy(doc, new_key, new_len); + if (!cpy_key) return false; + } + yyjson_mut_set_strn(old_key, cpy_key, new_len); + } + } + return cpy_key != NULL; +} + + + +#if !defined(YYJSON_DISABLE_UTILS) || !YYJSON_DISABLE_UTILS + +/*============================================================================== + * MARK: - JSON Pointer API (Implementation) + *============================================================================*/ + +#define yyjson_ptr_set_err(_code, _msg) do { \ + if (err) { \ + err->code = YYJSON_PTR_ERR_##_code; \ + err->msg = _msg; \ + err->pos = 0; \ + } \ +} while(false) + +/* require: val != NULL, *ptr == '/', len > 0 */ +yyjson_api yyjson_val *unsafe_yyjson_ptr_getx(yyjson_val *val, + const char *ptr, size_t len, + yyjson_ptr_err *err); + +/* require: val != NULL, *ptr == '/', len > 0 */ +yyjson_api yyjson_mut_val *unsafe_yyjson_mut_ptr_getx(yyjson_mut_val *val, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/* require: val/new_val/doc != NULL, *ptr == '/', len > 0 */ +yyjson_api bool unsafe_yyjson_mut_ptr_putx(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc, + bool create_parent, bool insert_new, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +/* require: val/err != NULL, *ptr == '/', len > 0 */ +yyjson_api yyjson_mut_val *unsafe_yyjson_mut_ptr_replacex( + yyjson_mut_val *val, const char *ptr, size_t len, yyjson_mut_val *new_val, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err); + +/* require: val/err != NULL, *ptr == '/', len > 0 */ +yyjson_api yyjson_mut_val *unsafe_yyjson_mut_ptr_removex(yyjson_mut_val *val, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err); + +yyjson_api_inline yyjson_val *yyjson_doc_ptr_get(yyjson_doc *doc, + const char *ptr) { + if (yyjson_unlikely(!ptr)) return NULL; + return yyjson_doc_ptr_getn(doc, ptr, strlen(ptr)); +} + +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getn(yyjson_doc *doc, + const char *ptr, size_t len) { + return yyjson_doc_ptr_getx(doc, ptr, len, NULL); +} + +yyjson_api_inline yyjson_val *yyjson_doc_ptr_getx(yyjson_doc *doc, + const char *ptr, size_t len, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (yyjson_unlikely(!doc || !ptr)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(!doc->root)) { + yyjson_ptr_set_err(NULL_ROOT, "document's root is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + return doc->root; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_ptr_getx(doc->root, ptr, len, err); +} + +yyjson_api_inline yyjson_val *yyjson_ptr_get(yyjson_val *val, + const char *ptr) { + if (yyjson_unlikely(!ptr)) return NULL; + return yyjson_ptr_getn(val, ptr, strlen(ptr)); +} + +yyjson_api_inline yyjson_val *yyjson_ptr_getn(yyjson_val *val, + const char *ptr, size_t len) { + return yyjson_ptr_getx(val, ptr, len, NULL); +} + +yyjson_api_inline yyjson_val *yyjson_ptr_getx(yyjson_val *val, + const char *ptr, size_t len, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (yyjson_unlikely(!val || !ptr)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + return val; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_ptr_getx(val, ptr, len, err); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_get(yyjson_mut_doc *doc, + const char *ptr) { + if (!ptr) return NULL; + return yyjson_mut_doc_ptr_getn(doc, ptr, strlen(ptr)); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getn(yyjson_mut_doc *doc, + const char *ptr, + size_t len) { + return yyjson_mut_doc_ptr_getx(doc, ptr, len, NULL, NULL); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_getx(yyjson_mut_doc *doc, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!doc || !ptr)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(!doc->root)) { + yyjson_ptr_set_err(NULL_ROOT, "document's root is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + return doc->root; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_mut_ptr_getx(doc->root, ptr, len, ctx, err); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_get(yyjson_mut_val *val, + const char *ptr) { + if (!ptr) return NULL; + return yyjson_mut_ptr_getn(val, ptr, strlen(ptr)); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getn(yyjson_mut_val *val, + const char *ptr, + size_t len) { + return yyjson_mut_ptr_getx(val, ptr, len, NULL, NULL); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_getx(yyjson_mut_val *val, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!val || !ptr)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + return val; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_mut_ptr_getx(val, ptr, len, ctx, err); +} + +yyjson_api_inline bool yyjson_mut_doc_ptr_add(yyjson_mut_doc *doc, + const char *ptr, + yyjson_mut_val *new_val) { + if (yyjson_unlikely(!ptr)) return false; + return yyjson_mut_doc_ptr_addn(doc, ptr, strlen(ptr), new_val); +} + +yyjson_api_inline bool yyjson_mut_doc_ptr_addn(yyjson_mut_doc *doc, + const char *ptr, + size_t len, + yyjson_mut_val *new_val) { + return yyjson_mut_doc_ptr_addx(doc, ptr, len, new_val, true, NULL, NULL); +} + +yyjson_api_inline bool yyjson_mut_doc_ptr_addx(yyjson_mut_doc *doc, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!doc || !ptr || !new_val)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return false; + } + if (yyjson_unlikely(len == 0)) { + if (doc->root) { + yyjson_ptr_set_err(SET_ROOT, "cannot set document's root"); + return false; + } else { + doc->root = new_val; + return true; + } + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return false; + } + if (yyjson_unlikely(!doc->root && !create_parent)) { + yyjson_ptr_set_err(NULL_ROOT, "document's root is NULL"); + return false; + } + if (yyjson_unlikely(!doc->root)) { + yyjson_mut_val *root = yyjson_mut_obj(doc); + if (yyjson_unlikely(!root)) { + yyjson_ptr_set_err(MEMORY_ALLOCATION, "failed to create value"); + return false; + } + if (unsafe_yyjson_mut_ptr_putx(root, ptr, len, new_val, doc, + create_parent, true, ctx, err)) { + doc->root = root; + return true; + } + return false; + } + return unsafe_yyjson_mut_ptr_putx(doc->root, ptr, len, new_val, doc, + create_parent, true, ctx, err); +} + +yyjson_api_inline bool yyjson_mut_ptr_add(yyjson_mut_val *val, + const char *ptr, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc) { + if (yyjson_unlikely(!ptr)) return false; + return yyjson_mut_ptr_addn(val, ptr, strlen(ptr), new_val, doc); +} + +yyjson_api_inline bool yyjson_mut_ptr_addn(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc) { + return yyjson_mut_ptr_addx(val, ptr, len, new_val, doc, true, NULL, NULL); +} + +yyjson_api_inline bool yyjson_mut_ptr_addx(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!val || !ptr || !new_val || !doc)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return false; + } + if (yyjson_unlikely(len == 0)) { + yyjson_ptr_set_err(SET_ROOT, "cannot set root"); + return false; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return false; + } + return unsafe_yyjson_mut_ptr_putx(val, ptr, len, new_val, + doc, create_parent, true, ctx, err); +} + +yyjson_api_inline bool yyjson_mut_doc_ptr_set(yyjson_mut_doc *doc, + const char *ptr, + yyjson_mut_val *new_val) { + if (yyjson_unlikely(!ptr)) return false; + return yyjson_mut_doc_ptr_setn(doc, ptr, strlen(ptr), new_val); +} + +yyjson_api_inline bool yyjson_mut_doc_ptr_setn(yyjson_mut_doc *doc, + const char *ptr, size_t len, + yyjson_mut_val *new_val) { + return yyjson_mut_doc_ptr_setx(doc, ptr, len, new_val, true, NULL, NULL); +} + +yyjson_api_inline bool yyjson_mut_doc_ptr_setx(yyjson_mut_doc *doc, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!doc || !ptr)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return false; + } + if (yyjson_unlikely(len == 0)) { + if (ctx) ctx->old = doc->root; + doc->root = new_val; + return true; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return false; + } + if (!new_val) { + if (!doc->root) { + yyjson_ptr_set_err(RESOLVE, "JSON pointer cannot be resolved"); + return false; + } + return !!unsafe_yyjson_mut_ptr_removex(doc->root, ptr, len, ctx, err); + } + if (yyjson_unlikely(!doc->root && !create_parent)) { + yyjson_ptr_set_err(NULL_ROOT, "document's root is NULL"); + return false; + } + if (yyjson_unlikely(!doc->root)) { + yyjson_mut_val *root = yyjson_mut_obj(doc); + if (yyjson_unlikely(!root)) { + yyjson_ptr_set_err(MEMORY_ALLOCATION, "failed to create value"); + return false; + } + if (unsafe_yyjson_mut_ptr_putx(root, ptr, len, new_val, doc, + create_parent, false, ctx, err)) { + doc->root = root; + return true; + } + return false; + } + return unsafe_yyjson_mut_ptr_putx(doc->root, ptr, len, new_val, doc, + create_parent, false, ctx, err); +} + +yyjson_api_inline bool yyjson_mut_ptr_set(yyjson_mut_val *val, + const char *ptr, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc) { + if (yyjson_unlikely(!ptr)) return false; + return yyjson_mut_ptr_setn(val, ptr, strlen(ptr), new_val, doc); +} + +yyjson_api_inline bool yyjson_mut_ptr_setn(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc) { + return yyjson_mut_ptr_setx(val, ptr, len, new_val, doc, true, NULL, NULL); +} + +yyjson_api_inline bool yyjson_mut_ptr_setx(yyjson_mut_val *val, + const char *ptr, size_t len, + yyjson_mut_val *new_val, + yyjson_mut_doc *doc, + bool create_parent, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!val || !ptr || !doc)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return false; + } + if (yyjson_unlikely(len == 0)) { + yyjson_ptr_set_err(SET_ROOT, "cannot set root"); + return false; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return false; + } + if (!new_val) { + return !!unsafe_yyjson_mut_ptr_removex(val, ptr, len, ctx, err); + } + return unsafe_yyjson_mut_ptr_putx(val, ptr, len, new_val, doc, + create_parent, false, ctx, err); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_replace( + yyjson_mut_doc *doc, const char *ptr, yyjson_mut_val *new_val) { + if (!ptr) return NULL; + return yyjson_mut_doc_ptr_replacen(doc, ptr, strlen(ptr), new_val); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_replacen( + yyjson_mut_doc *doc, const char *ptr, size_t len, yyjson_mut_val *new_val) { + return yyjson_mut_doc_ptr_replacex(doc, ptr, len, new_val, NULL, NULL); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_replacex( + yyjson_mut_doc *doc, const char *ptr, size_t len, yyjson_mut_val *new_val, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { + + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!doc || !ptr || !new_val)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + yyjson_mut_val *root = doc->root; + if (yyjson_unlikely(!root)) { + yyjson_ptr_set_err(RESOLVE, "JSON pointer cannot be resolved"); + return NULL; + } + if (ctx) ctx->old = root; + doc->root = new_val; + return root; + } + if (yyjson_unlikely(!doc->root)) { + yyjson_ptr_set_err(NULL_ROOT, "document's root is NULL"); + return NULL; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_mut_ptr_replacex(doc->root, ptr, len, new_val, + ctx, err); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_replace( + yyjson_mut_val *val, const char *ptr, yyjson_mut_val *new_val) { + if (!ptr) return NULL; + return yyjson_mut_ptr_replacen(val, ptr, strlen(ptr), new_val); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_replacen( + yyjson_mut_val *val, const char *ptr, size_t len, yyjson_mut_val *new_val) { + return yyjson_mut_ptr_replacex(val, ptr, len, new_val, NULL, NULL); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_replacex( + yyjson_mut_val *val, const char *ptr, size_t len, yyjson_mut_val *new_val, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { + + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!val || !ptr || !new_val)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + yyjson_ptr_set_err(SET_ROOT, "cannot set root"); + return NULL; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_mut_ptr_replacex(val, ptr, len, new_val, ctx, err); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_remove( + yyjson_mut_doc *doc, const char *ptr) { + if (!ptr) return NULL; + return yyjson_mut_doc_ptr_removen(doc, ptr, strlen(ptr)); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_removen( + yyjson_mut_doc *doc, const char *ptr, size_t len) { + return yyjson_mut_doc_ptr_removex(doc, ptr, len, NULL, NULL); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_ptr_removex( + yyjson_mut_doc *doc, const char *ptr, size_t len, + yyjson_ptr_ctx *ctx, yyjson_ptr_err *err) { + + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!doc || !ptr)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(!doc->root)) { + yyjson_ptr_set_err(NULL_ROOT, "document's root is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + yyjson_mut_val *root = doc->root; + if (ctx) ctx->old = root; + doc->root = NULL; + return root; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_mut_ptr_removex(doc->root, ptr, len, ctx, err); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_remove(yyjson_mut_val *val, + const char *ptr) { + if (!ptr) return NULL; + return yyjson_mut_ptr_removen(val, ptr, strlen(ptr)); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_removen(yyjson_mut_val *val, + const char *ptr, + size_t len) { + return yyjson_mut_ptr_removex(val, ptr, len, NULL, NULL); +} + +yyjson_api_inline yyjson_mut_val *yyjson_mut_ptr_removex(yyjson_mut_val *val, + const char *ptr, + size_t len, + yyjson_ptr_ctx *ctx, + yyjson_ptr_err *err) { + yyjson_ptr_set_err(NONE, NULL); + if (ctx) memset(ctx, 0, sizeof(*ctx)); + + if (yyjson_unlikely(!val || !ptr)) { + yyjson_ptr_set_err(PARAMETER, "input parameter is NULL"); + return NULL; + } + if (yyjson_unlikely(len == 0)) { + yyjson_ptr_set_err(SET_ROOT, "cannot set root"); + return NULL; + } + if (yyjson_unlikely(*ptr != '/')) { + yyjson_ptr_set_err(SYNTAX, "no prefix '/'"); + return NULL; + } + return unsafe_yyjson_mut_ptr_removex(val, ptr, len, ctx, err); +} + +yyjson_api_inline bool yyjson_ptr_ctx_append(yyjson_ptr_ctx *ctx, + yyjson_mut_val *key, + yyjson_mut_val *val) { + yyjson_mut_val *ctn, *pre_key, *pre_val, *cur_key, *cur_val; + if (!ctx || !ctx->ctn || !val) return false; + ctn = ctx->ctn; + + if (yyjson_mut_is_obj(ctn)) { + if (!key) return false; + key->next = val; + pre_key = ctx->pre; + if (unsafe_yyjson_get_len(ctn) == 0) { + val->next = key; + ctn->uni.ptr = key; + ctx->pre = key; + } else if (!pre_key) { + pre_key = (yyjson_mut_val *)ctn->uni.ptr; + pre_val = pre_key->next; + val->next = pre_val->next; + pre_val->next = key; + ctn->uni.ptr = key; + ctx->pre = pre_key; + } else { + cur_key = pre_key->next->next; + cur_val = cur_key->next; + val->next = cur_val->next; + cur_val->next = key; + if (ctn->uni.ptr == cur_key) ctn->uni.ptr = key; + ctx->pre = cur_key; + } + } else { + pre_val = ctx->pre; + if (unsafe_yyjson_get_len(ctn) == 0) { + val->next = val; + ctn->uni.ptr = val; + ctx->pre = val; + } else if (!pre_val) { + pre_val = (yyjson_mut_val *)ctn->uni.ptr; + val->next = pre_val->next; + pre_val->next = val; + ctn->uni.ptr = val; + ctx->pre = pre_val; + } else { + cur_val = pre_val->next; + val->next = cur_val->next; + cur_val->next = val; + if (ctn->uni.ptr == cur_val) ctn->uni.ptr = val; + ctx->pre = cur_val; + } + } + unsafe_yyjson_inc_len(ctn); + return true; +} + +yyjson_api_inline bool yyjson_ptr_ctx_replace(yyjson_ptr_ctx *ctx, + yyjson_mut_val *val) { + yyjson_mut_val *ctn, *pre_key, *cur_key, *pre_val, *cur_val; + if (!ctx || !ctx->ctn || !ctx->pre || !val) return false; + ctn = ctx->ctn; + if (yyjson_mut_is_obj(ctn)) { + pre_key = ctx->pre; + pre_val = pre_key->next; + cur_key = pre_val->next; + cur_val = cur_key->next; + /* replace current value */ + cur_key->next = val; + val->next = cur_val->next; + ctx->old = cur_val; + } else { + pre_val = ctx->pre; + cur_val = pre_val->next; + /* replace current value */ + if (pre_val != cur_val) { + val->next = cur_val->next; + pre_val->next = val; + if (ctn->uni.ptr == cur_val) ctn->uni.ptr = val; + } else { + val->next = val; + ctn->uni.ptr = val; + ctx->pre = val; + } + ctx->old = cur_val; + } + return true; +} + +yyjson_api_inline bool yyjson_ptr_ctx_remove(yyjson_ptr_ctx *ctx) { + yyjson_mut_val *ctn, *pre_key, *pre_val, *cur_key, *cur_val; + size_t len; + if (!ctx || !ctx->ctn || !ctx->pre) return false; + ctn = ctx->ctn; + if (yyjson_mut_is_obj(ctn)) { + pre_key = ctx->pre; + pre_val = pre_key->next; + cur_key = pre_val->next; + cur_val = cur_key->next; + /* remove current key-value */ + pre_val->next = cur_val->next; + if (ctn->uni.ptr == cur_key) ctn->uni.ptr = pre_key; + ctx->pre = NULL; + ctx->old = cur_val; + } else { + pre_val = ctx->pre; + cur_val = pre_val->next; + /* remove current key-value */ + pre_val->next = cur_val->next; + if (ctn->uni.ptr == cur_val) ctn->uni.ptr = pre_val; + ctx->pre = NULL; + ctx->old = cur_val; + } + len = unsafe_yyjson_get_len(ctn) - 1; + if (len == 0) ctn->uni.ptr = NULL; + unsafe_yyjson_set_len(ctn, len); + return true; +} + +#undef yyjson_ptr_set_err + + + +/*============================================================================== + * MARK: - JSON Value at Pointer API (Implementation) + *============================================================================*/ + +/** + Set provided `value` if the JSON Pointer (RFC 6901) exists and is type bool. + Returns true if value at `ptr` exists and is the correct type, otherwise false. + */ +yyjson_api_inline bool yyjson_ptr_get_bool( + yyjson_val *root, const char *ptr, bool *value) { + yyjson_val *val = yyjson_ptr_get(root, ptr); + if (value && yyjson_is_bool(val)) { + *value = unsafe_yyjson_get_bool(val); + return true; + } else { + return false; + } +} + +/** + Set provided `value` if the JSON Pointer (RFC 6901) exists and is an integer + that fits in `uint64_t`. Returns true if successful, otherwise false. + */ +yyjson_api_inline bool yyjson_ptr_get_uint( + yyjson_val *root, const char *ptr, uint64_t *value) { + yyjson_val *val = yyjson_ptr_get(root, ptr); + if (value && val) { + uint64_t ret = val->uni.u64; + if (unsafe_yyjson_is_uint(val) || + (unsafe_yyjson_is_sint(val) && !(ret >> 63))) { + *value = ret; + return true; + } + } + return false; +} + +/** + Set provided `value` if the JSON Pointer (RFC 6901) exists and is an integer + that fits in `int64_t`. Returns true if successful, otherwise false. + */ +yyjson_api_inline bool yyjson_ptr_get_sint( + yyjson_val *root, const char *ptr, int64_t *value) { + yyjson_val *val = yyjson_ptr_get(root, ptr); + if (value && val) { + int64_t ret = val->uni.i64; + if (unsafe_yyjson_is_sint(val) || + (unsafe_yyjson_is_uint(val) && ret >= 0)) { + *value = ret; + return true; + } + } + return false; +} + +/** + Set provided `value` if the JSON Pointer (RFC 6901) exists and is type real. + Returns true if value at `ptr` exists and is the correct type, otherwise false. + */ +yyjson_api_inline bool yyjson_ptr_get_real( + yyjson_val *root, const char *ptr, double *value) { + yyjson_val *val = yyjson_ptr_get(root, ptr); + if (value && yyjson_is_real(val)) { + *value = unsafe_yyjson_get_real(val); + return true; + } else { + return false; + } +} + +/** + Set provided `value` if the JSON Pointer (RFC 6901) exists and is type sint, + uint or real. + Returns true if value at `ptr` exists and is the correct type, otherwise false. + */ +yyjson_api_inline bool yyjson_ptr_get_num( + yyjson_val *root, const char *ptr, double *value) { + yyjson_val *val = yyjson_ptr_get(root, ptr); + if (value && yyjson_is_num(val)) { + *value = unsafe_yyjson_get_num(val); + return true; + } else { + return false; + } +} + +/** + Set provided `value` if the JSON Pointer (RFC 6901) exists and is type string. + Returns true if value at `ptr` exists and is the correct type, otherwise false. + */ +yyjson_api_inline bool yyjson_ptr_get_str( + yyjson_val *root, const char *ptr, const char **value) { + yyjson_val *val = yyjson_ptr_get(root, ptr); + if (value && yyjson_is_str(val)) { + *value = unsafe_yyjson_get_str(val); + return true; + } else { + return false; + } +} + + + +/*============================================================================== + * MARK: - Deprecated + *============================================================================*/ + +/** @deprecated renamed to `yyjson_doc_ptr_get` */ +yyjson_deprecated("renamed to yyjson_doc_ptr_get") +yyjson_api_inline yyjson_val *yyjson_doc_get_pointer(yyjson_doc *doc, + const char *ptr) { + return yyjson_doc_ptr_get(doc, ptr); +} + +/** @deprecated renamed to `yyjson_doc_ptr_getn` */ +yyjson_deprecated("renamed to yyjson_doc_ptr_getn") +yyjson_api_inline yyjson_val *yyjson_doc_get_pointern(yyjson_doc *doc, + const char *ptr, + size_t len) { + return yyjson_doc_ptr_getn(doc, ptr, len); +} + +/** @deprecated renamed to `yyjson_mut_doc_ptr_get` */ +yyjson_deprecated("renamed to yyjson_mut_doc_ptr_get") +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_get_pointer( + yyjson_mut_doc *doc, const char *ptr) { + return yyjson_mut_doc_ptr_get(doc, ptr); +} + +/** @deprecated renamed to `yyjson_mut_doc_ptr_getn` */ +yyjson_deprecated("renamed to yyjson_mut_doc_ptr_getn") +yyjson_api_inline yyjson_mut_val *yyjson_mut_doc_get_pointern( + yyjson_mut_doc *doc, const char *ptr, size_t len) { + return yyjson_mut_doc_ptr_getn(doc, ptr, len); +} + +/** @deprecated renamed to `yyjson_ptr_get` */ +yyjson_deprecated("renamed to yyjson_ptr_get") +yyjson_api_inline yyjson_val *yyjson_get_pointer(yyjson_val *val, + const char *ptr) { + return yyjson_ptr_get(val, ptr); +} + +/** @deprecated renamed to `yyjson_ptr_getn` */ +yyjson_deprecated("renamed to yyjson_ptr_getn") +yyjson_api_inline yyjson_val *yyjson_get_pointern(yyjson_val *val, + const char *ptr, + size_t len) { + return yyjson_ptr_getn(val, ptr, len); +} + +/** @deprecated renamed to `yyjson_mut_ptr_get` */ +yyjson_deprecated("renamed to yyjson_mut_ptr_get") +yyjson_api_inline yyjson_mut_val *yyjson_mut_get_pointer(yyjson_mut_val *val, + const char *ptr) { + return yyjson_mut_ptr_get(val, ptr); +} + +/** @deprecated renamed to `yyjson_mut_ptr_getn` */ +yyjson_deprecated("renamed to yyjson_mut_ptr_getn") +yyjson_api_inline yyjson_mut_val *yyjson_mut_get_pointern(yyjson_mut_val *val, + const char *ptr, + size_t len) { + return yyjson_mut_ptr_getn(val, ptr, len); +} + +/** @deprecated renamed to `yyjson_mut_ptr_getn` */ +yyjson_deprecated("renamed to unsafe_yyjson_ptr_getn") +yyjson_api_inline yyjson_val *unsafe_yyjson_get_pointer(yyjson_val *val, + const char *ptr, + size_t len) { + yyjson_ptr_err err; + return unsafe_yyjson_ptr_getx(val, ptr, len, &err); +} + +/** @deprecated renamed to `unsafe_yyjson_mut_ptr_getx` */ +yyjson_deprecated("renamed to unsafe_yyjson_mut_ptr_getx") +yyjson_api_inline yyjson_mut_val *unsafe_yyjson_mut_get_pointer( + yyjson_mut_val *val, const char *ptr, size_t len) { + yyjson_ptr_err err; + return unsafe_yyjson_mut_ptr_getx(val, ptr, len, NULL, &err); +} + +#endif /* YYJSON_DISABLE_UTILS */ + + + +/*============================================================================== + * MARK: - Compiler Hint End + *============================================================================*/ + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) +# pragma GCC diagnostic pop +# endif +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif /* warning suppress end */ + +#ifdef __cplusplus +} +#endif /* extern "C" end */ + +#endif /* YYJSON_H */ diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/summary.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/summary.json new file mode 100644 index 0000000..908043b --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/summary.json @@ -0,0 +1,4006 @@ +[ + { + "case": "metadata_heavy", + "variant": "borrow", + "samples": 5, + "peak_reserved_bytes": { + "median": 1208192, + "min": 1208192, + "max": 1208192 + }, + "source_peak_bytes": { + "median": 1059888, + "min": 1059888, + "max": 1059888 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "rss_peak_bytes": { + "median": 2785280, + "min": 2768896, + "max": 2801664 + }, + "elapsed_ns": { + "median": 66395496, + "min": 66302663, + "max": 66397387 + }, + "cpu_ns": { + "median": 14840713, + "min": 14680433, + "max": 14949295 + }, + "parse_call_ns": { + "median": 10710339, + "min": 10679603, + "max": 10748920 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 6312628, + "min": 6312628, + "max": 6312628 + }, + "parser_realloc_copy_bytes": { + "median": 14086144, + "min": 14086144, + "max": 14086144 + }, + "source_byte_seconds": { + "median": 67622.063537, + "min": 67603.216745, + "max": 67632.156584 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 26533.881, + "latency_p99_upper_ns": 36864 + }, + { + "peak_count": 64, + "latency_mean_ns": 5055556.934, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8039215.434, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "compact", + "samples": 5, + "peak_reserved_bytes": { + "median": 1209200, + "min": 1209200, + "max": 1209200 + }, + "source_peak_bytes": { + "median": 1059888, + "min": 1059888, + "max": 1059888 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "rss_peak_bytes": { + "median": 2916352, + "min": 2899968, + "max": 2932736 + }, + "elapsed_ns": { + "median": 66425702, + "min": 66346982, + "max": 66647996 + }, + "cpu_ns": { + "median": 15174956, + "min": 15115580, + "max": 15645769 + }, + "parse_call_ns": { + "median": 10767457, + "min": 10730381, + "max": 10897093 + }, + "selection_copy_bytes": { + "median": 28672, + "min": 28672, + "max": 28672 + }, + "reader_copy_bytes": { + "median": 6312628, + "min": 6312628, + "max": 6312628 + }, + "parser_realloc_copy_bytes": { + "median": 14086144, + "min": 14086144, + "max": 14086144 + }, + "source_byte_seconds": { + "median": 42524.965388, + "min": 42518.234312, + "max": 42539.689103 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 512, + "min": 512, + "max": 512 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 26833.023, + "latency_p99_upper_ns": 36864 + }, + { + "peak_count": 64, + "latency_mean_ns": 5056115.033, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8039588.77, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 187312, + "min": 187312, + "max": 187312 + }, + "source_peak_bytes": { + "median": 24672, + "min": 24672, + "max": 24672 + }, + "output_peak_bytes": { + "median": 14896, + "min": 14896, + "max": 14896 + }, + "parser_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "rss_peak_bytes": { + "median": 1835008, + "min": 1818624, + "max": 1835008 + }, + "elapsed_ns": { + "median": 66358054, + "min": 66338315, + "max": 66437867 + }, + "cpu_ns": { + "median": 14754424, + "min": 14688841, + "max": 14887131 + }, + "parse_call_ns": { + "median": 10588699, + "min": 10558757, + "max": 10719986 + }, + "selection_copy_bytes": { + "median": 319488, + "min": 319488, + "max": 319488 + }, + "reader_copy_bytes": { + "median": 6312628, + "min": 6312628, + "max": 6312628 + }, + "parser_realloc_copy_bytes": { + "median": 14086144, + "min": 14086144, + "max": 14086144 + }, + "source_byte_seconds": { + "median": 190.767691, + "min": 190.237303, + "max": 192.97609 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 26285.051, + "latency_p99_upper_ns": 36864 + }, + { + "peak_count": 64, + "latency_mean_ns": 5055200.059, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8039957.693, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "copy_no_pool", + "samples": 5, + "peak_reserved_bytes": { + "median": 170672, + "min": 170672, + "max": 170672 + }, + "source_peak_bytes": { + "median": 24672, + "min": 24672, + "max": 24672 + }, + "output_peak_bytes": { + "median": 14896, + "min": 14896, + "max": 14896 + }, + "parser_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "rss_peak_bytes": { + "median": 1835008, + "min": 1835008, + "max": 1851392 + }, + "elapsed_ns": { + "median": 66421906, + "min": 66367572, + "max": 69246534 + }, + "cpu_ns": { + "median": 14937944, + "min": 14844612, + "max": 16610834 + }, + "parse_call_ns": { + "median": 10714630, + "min": 10626817, + "max": 11758115 + }, + "selection_copy_bytes": { + "median": 319488, + "min": 319488, + "max": 319488 + }, + "reader_copy_bytes": { + "median": 6312628, + "min": 6312628, + "max": 6312628 + }, + "parser_realloc_copy_bytes": { + "median": 14086144, + "min": 14086144, + "max": 14086144 + }, + "source_byte_seconds": { + "median": 193.34471, + "min": 191.787957, + "max": 215.365911 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 26630.812, + "latency_p99_upper_ns": 40960 + }, + { + "peak_count": 64, + "latency_mean_ns": 5055539.021, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8039399.533, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "shared_copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 180144, + "min": 180144, + "max": 180144 + }, + "source_peak_bytes": { + "median": 24672, + "min": 24672, + "max": 24672 + }, + "output_peak_bytes": { + "median": 7728, + "min": 7728, + "max": 7728 + }, + "parser_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "rss_peak_bytes": { + "median": 1835008, + "min": 1835008, + "max": 1835008 + }, + "elapsed_ns": { + "median": 66509552, + "min": 66340017, + "max": 67338922 + }, + "cpu_ns": { + "median": 14641398, + "min": 14565611, + "max": 15543047 + }, + "parse_call_ns": { + "median": 10583202, + "min": 10558947, + "max": 10965512 + }, + "selection_copy_bytes": { + "median": 290816, + "min": 290816, + "max": 290816 + }, + "reader_copy_bytes": { + "median": 6312628, + "min": 6312628, + "max": 6312628 + }, + "parser_realloc_copy_bytes": { + "median": 14086144, + "min": 14086144, + "max": 14086144 + }, + "source_byte_seconds": { + "median": 189.850967, + "min": 189.196651, + "max": 198.833427 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 512, + "min": 512, + "max": 512 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 26126.43, + "latency_p99_upper_ns": 32768 + }, + { + "peak_count": 64, + "latency_mean_ns": 5054896.006, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8039936.652, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold2", + "samples": 5, + "peak_reserved_bytes": { + "median": 180144, + "min": 180144, + "max": 180144 + }, + "source_peak_bytes": { + "median": 24672, + "min": 24672, + "max": 24672 + }, + "output_peak_bytes": { + "median": 7728, + "min": 7728, + "max": 7728 + }, + "parser_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "rss_peak_bytes": { + "median": 1835008, + "min": 1835008, + "max": 1851392 + }, + "elapsed_ns": { + "median": 66354498, + "min": 66341459, + "max": 66675868 + }, + "cpu_ns": { + "median": 14610428, + "min": 14573003, + "max": 15617313 + }, + "parse_call_ns": { + "median": 10568715, + "min": 10552983, + "max": 10794500 + }, + "selection_copy_bytes": { + "median": 290816, + "min": 290816, + "max": 290816 + }, + "reader_copy_bytes": { + "median": 6312628, + "min": 6312628, + "max": 6312628 + }, + "parser_realloc_copy_bytes": { + "median": 14086144, + "min": 14086144, + "max": 14086144 + }, + "source_byte_seconds": { + "median": 189.501318, + "min": 189.19959, + "max": 196.368374 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 512, + "min": 512, + "max": 512 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 26103.785, + "latency_p99_upper_ns": 30720 + }, + { + "peak_count": 64, + "latency_mean_ns": 5054791.455, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8037890.609, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "metadata_heavy", + "variant": "threshold8", + "samples": 5, + "peak_reserved_bytes": { + "median": 180144, + "min": 180144, + "max": 180144 + }, + "source_peak_bytes": { + "median": 24672, + "min": 24672, + "max": 24672 + }, + "output_peak_bytes": { + "median": 7728, + "min": 7728, + "max": 7728 + }, + "parser_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "rss_peak_bytes": { + "median": 1835008, + "min": 1835008, + "max": 1851392 + }, + "elapsed_ns": { + "median": 66346091, + "min": 66342183, + "max": 66620533 + }, + "cpu_ns": { + "median": 14678765, + "min": 14624958, + "max": 15009658 + }, + "parse_call_ns": { + "median": 10599246, + "min": 10558072, + "max": 10663838 + }, + "selection_copy_bytes": { + "median": 290816, + "min": 290816, + "max": 290816 + }, + "reader_copy_bytes": { + "median": 6312628, + "min": 6312628, + "max": 6312628 + }, + "parser_realloc_copy_bytes": { + "median": 14086144, + "min": 14086144, + "max": 14086144 + }, + "source_byte_seconds": { + "median": 190.199351, + "min": 189.596919, + "max": 191.566293 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 512, + "min": 512, + "max": 512 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 26178.92, + "latency_p99_upper_ns": 36864 + }, + { + "peak_count": 64, + "latency_mean_ns": 5054797.43, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8038718.686, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "borrow", + "samples": 5, + "peak_reserved_bytes": { + "median": 2097024, + "min": 2097024, + "max": 2097072 + }, + "source_peak_bytes": { + "median": 2047296, + "min": 2047296, + "max": 2047296 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 524336, + "min": 524336, + "max": 524336 + }, + "rss_peak_bytes": { + "median": 3719168, + "min": 3702784, + "max": 3719168 + }, + "elapsed_ns": { + "median": 516876187, + "min": 516415971, + "max": 519737827 + }, + "cpu_ns": { + "median": 58189132, + "min": 56956626, + "max": 63401151 + }, + "parse_call_ns": { + "median": 413462854, + "min": 410873515, + "max": 414175159 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 114814580, + "min": 114814580, + "max": 114814580 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 866155.703795, + "min": 865936.173669, + "max": 869390.688093 + }, + "budget_waits": { + "median": 1258, + "min": 1250, + "max": 1267 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 496219.126, + "latency_p99_upper_ns": 7864320 + }, + { + "peak_count": 30, + "latency_mean_ns": 5528237.536, + "latency_p99_upper_ns": 12582912 + }, + { + "peak_count": 30, + "latency_mean_ns": 8542682.03, + "latency_p99_upper_ns": 15728640 + } + ] + }, + { + "case": "mixed_tight", + "variant": "compact", + "samples": 5, + "peak_reserved_bytes": { + "median": 2096656, + "min": 2095936, + "max": 2097104 + }, + "source_peak_bytes": { + "median": 2047296, + "min": 2047296, + "max": 2047296 + }, + "output_peak_bytes": { + "median": 3360, + "min": 3360, + "max": 3360 + }, + "parser_peak_bytes": { + "median": 524336, + "min": 524336, + "max": 524336 + }, + "rss_peak_bytes": { + "median": 3719168, + "min": 3702784, + "max": 3833856 + }, + "elapsed_ns": { + "median": 330507221, + "min": 330325402, + "max": 332809762 + }, + "cpu_ns": { + "median": 57227425, + "min": 56526552, + "max": 64506702 + }, + "parse_call_ns": { + "median": 257390641, + "min": 255295188, + "max": 258042238 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 114814580, + "min": 114814580, + "max": 114814580 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 542286.835871, + "min": 542057.64214, + "max": 544031.993873 + }, + "budget_waits": { + "median": 1246, + "min": 1227, + "max": 1262 + }, + "compacted": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 314160.033, + "latency_p99_upper_ns": 4718592 + }, + { + "peak_count": 30, + "latency_mean_ns": 5361784.028, + "latency_p99_upper_ns": 10485760 + }, + { + "peak_count": 49, + "latency_mean_ns": 8345801.06, + "latency_p99_upper_ns": 12582912 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 1104848, + "min": 1104736, + "max": 1104848 + }, + "source_peak_bytes": { + "median": 393312, + "min": 393312, + "max": 393312 + }, + "output_peak_bytes": { + "median": 79920, + "min": 79920, + "max": 79920 + }, + "parser_peak_bytes": { + "median": 524336, + "min": 524336, + "max": 524336 + }, + "rss_peak_bytes": { + "median": 1884160, + "min": 1884160, + "max": 1900544 + }, + "elapsed_ns": { + "median": 134798098, + "min": 134667407, + "max": 135397301 + }, + "cpu_ns": { + "median": 52545277, + "min": 52218674, + "max": 55535720 + }, + "parse_call_ns": { + "median": 26282225, + "min": 26241260, + "max": 28191073 + }, + "selection_copy_bytes": { + "median": 26872064, + "min": 26872064, + "max": 26872064 + }, + "reader_copy_bytes": { + "median": 114814580, + "min": 114814580, + "max": 114814580 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 6883.203763, + "min": 6810.984162, + "max": 7296.683374 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 47583.467, + "latency_p99_upper_ns": 147456 + }, + { + "peak_count": 64, + "latency_mean_ns": 5082343.96, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8083520.998, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "copy_no_pool", + "samples": 5, + "peak_reserved_bytes": { + "median": 842368, + "min": 842368, + "max": 842368 + }, + "source_peak_bytes": { + "median": 393312, + "min": 393312, + "max": 393312 + }, + "output_peak_bytes": { + "median": 79920, + "min": 79920, + "max": 79920 + }, + "parser_peak_bytes": { + "median": 524336, + "min": 524336, + "max": 524336 + }, + "rss_peak_bytes": { + "median": 1933312, + "min": 1916928, + "max": 1949696 + }, + "elapsed_ns": { + "median": 135708499, + "min": 135580728, + "max": 135818181 + }, + "cpu_ns": { + "median": 59498427, + "min": 59055276, + "max": 61310764 + }, + "parse_call_ns": { + "median": 27370335, + "min": 27293452, + "max": 27699522 + }, + "selection_copy_bytes": { + "median": 26872064, + "min": 26872064, + "max": 26872064 + }, + "reader_copy_bytes": { + "median": 114814580, + "min": 114814580, + "max": 114814580 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 8320.069469, + "min": 8268.860241, + "max": 8492.13471 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 54118.737, + "latency_p99_upper_ns": 180224 + }, + { + "peak_count": 64, + "latency_mean_ns": 5093193.132, + "latency_p99_upper_ns": 5767168 + }, + { + "peak_count": 64, + "latency_mean_ns": 8094306.969, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "shared_copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 1097792, + "min": 1097792, + "max": 1097792 + }, + "source_peak_bytes": { + "median": 393312, + "min": 393312, + "max": 393312 + }, + "output_peak_bytes": { + "median": 72752, + "min": 72752, + "max": 72752 + }, + "parser_peak_bytes": { + "median": 524336, + "min": 524336, + "max": 524336 + }, + "rss_peak_bytes": { + "median": 1884160, + "min": 1884160, + "max": 1900544 + }, + "elapsed_ns": { + "median": 134698503, + "min": 134660107, + "max": 134733970 + }, + "cpu_ns": { + "median": 52085095, + "min": 51849496, + "max": 52189389 + }, + "parse_call_ns": { + "median": 26235438, + "min": 26215409, + "max": 26273407 + }, + "selection_copy_bytes": { + "median": 26814720, + "min": 26814720, + "max": 26814720 + }, + "reader_copy_bytes": { + "median": 114814580, + "min": 114814580, + "max": 114814580 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 6831.305534, + "min": 6785.330268, + "max": 6865.482399 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 47354.551, + "latency_p99_upper_ns": 147456 + }, + { + "peak_count": 64, + "latency_mean_ns": 5081713.058, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8082113.784, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold2", + "samples": 5, + "peak_reserved_bytes": { + "median": 1097792, + "min": 1097792, + "max": 1097792 + }, + "source_peak_bytes": { + "median": 393312, + "min": 393312, + "max": 393312 + }, + "output_peak_bytes": { + "median": 72752, + "min": 72752, + "max": 72752 + }, + "parser_peak_bytes": { + "median": 524336, + "min": 524336, + "max": 524336 + }, + "rss_peak_bytes": { + "median": 1884160, + "min": 1867776, + "max": 1900544 + }, + "elapsed_ns": { + "median": 134646087, + "min": 134596389, + "max": 134805557 + }, + "cpu_ns": { + "median": 51969989, + "min": 51772899, + "max": 52099602 + }, + "parse_call_ns": { + "median": 26207832, + "min": 26192074, + "max": 26303805 + }, + "selection_copy_bytes": { + "median": 26814720, + "min": 26814720, + "max": 26814720 + }, + "reader_copy_bytes": { + "median": 114814580, + "min": 114814580, + "max": 114814580 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 6812.54134, + "min": 6795.293198, + "max": 6823.249522 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 47208.702, + "latency_p99_upper_ns": 147456 + }, + { + "peak_count": 64, + "latency_mean_ns": 5082291.09, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8082717.212, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "mixed_tight", + "variant": "threshold8", + "samples": 5, + "peak_reserved_bytes": { + "median": 1097792, + "min": 1097792, + "max": 1097792 + }, + "source_peak_bytes": { + "median": 393312, + "min": 393312, + "max": 393312 + }, + "output_peak_bytes": { + "median": 11312, + "min": 11312, + "max": 11312 + }, + "parser_peak_bytes": { + "median": 524336, + "min": 524336, + "max": 524336 + }, + "rss_peak_bytes": { + "median": 2048000, + "min": 2031616, + "max": 2064384 + }, + "elapsed_ns": { + "median": 134627181, + "min": 134514858, + "max": 135143135 + }, + "cpu_ns": { + "median": 51051158, + "min": 50901202, + "max": 54181725 + }, + "parse_call_ns": { + "median": 26232175, + "min": 26138215, + "max": 28073539 + }, + "selection_copy_bytes": { + "median": 1059072, + "min": 1059072, + "max": 1059072 + }, + "reader_copy_bytes": { + "median": 114814580, + "min": 114814580, + "max": 114814580 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 7499.787736, + "min": 7468.893211, + "max": 7971.142415 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 46365.068, + "latency_p99_upper_ns": 147456 + }, + { + "peak_count": 64, + "latency_mean_ns": 5080938.783, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8081990.295, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "borrow", + "samples": 5, + "peak_reserved_bytes": { + "median": 4427696, + "min": 4427696, + "max": 4427696 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 5537792, + "min": 5521408, + "max": 5554176 + }, + "elapsed_ns": { + "median": 132124751, + "min": 131976979, + "max": 132469040 + }, + "cpu_ns": { + "median": 38591495, + "min": 37612015, + "max": 41862764 + }, + "parse_call_ns": { + "median": 22573964, + "min": 22444229, + "max": 22964342 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 540860.949553, + "min": 540831.163899, + "max": 541099.43903 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 64, + "latency_mean_ns": 8058679.423, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "compact", + "samples": 5, + "peak_reserved_bytes": { + "median": 4427696, + "min": 4427696, + "max": 4427696 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 5537792, + "min": 5537792, + "max": 5554176 + }, + "elapsed_ns": { + "median": 132110379, + "min": 131996119, + "max": 132186905 + }, + "cpu_ns": { + "median": 38257181, + "min": 38004572, + "max": 38709650 + }, + "parse_call_ns": { + "median": 22486562, + "min": 22434299, + "max": 22557256 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 540815.06589, + "min": 540791.270719, + "max": 540907.798619 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 64, + "latency_mean_ns": 8057936.641, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 303072, + "min": 303072, + "max": 303072 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1818624, + "min": 1818624, + "max": 1818624 + }, + "elapsed_ns": { + "median": 131689453, + "min": 131600270, + "max": 131752053 + }, + "cpu_ns": { + "median": 34877739, + "min": 34455850, + "max": 35243759 + }, + "parse_call_ns": { + "median": 21993246, + "min": 21964978, + "max": 22030534 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 1845.194241, + "min": 1832.537919, + "max": 1857.757504 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 64, + "latency_mean_ns": 8053071.22, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "copy_no_pool", + "samples": 5, + "peak_reserved_bytes": { + "median": 237296, + "min": 237296, + "max": 237296 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1818624, + "min": 1802240, + "max": 1835008 + }, + "elapsed_ns": { + "median": 132246841, + "min": 132199048, + "max": 137450461 + }, + "cpu_ns": { + "median": 42202499, + "min": 41767998, + "max": 46306762 + }, + "parse_call_ns": { + "median": 24805054, + "min": 24673720, + "max": 25720817 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2084.967873, + "min": 2066.312998, + "max": 2217.245474 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 64, + "latency_mean_ns": 8059680.607, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "shared_copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 303072, + "min": 303072, + "max": 303072 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1818624, + "min": 1802240, + "max": 1835008 + }, + "elapsed_ns": { + "median": 132103712, + "min": 131708165, + "max": 134003964 + }, + "cpu_ns": { + "median": 35248408, + "min": 34456703, + "max": 37157688 + }, + "parse_call_ns": { + "median": 22044118, + "min": 21940218, + "max": 22141327 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 1858.44117, + "min": 1833.007769, + "max": 1917.711679 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 64, + "latency_mean_ns": 8054276.953, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold2", + "samples": 5, + "peak_reserved_bytes": { + "median": 303072, + "min": 303072, + "max": 303072 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1818624, + "min": 1802240, + "max": 1835008 + }, + "elapsed_ns": { + "median": 132093280, + "min": 131703634, + "max": 132588310 + }, + "cpu_ns": { + "median": 36810738, + "min": 34747849, + "max": 37673671 + }, + "parse_call_ns": { + "median": 22338927, + "min": 21989760, + "max": 22518430 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 1914.610493, + "min": 1842.615404, + "max": 1949.11739 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 64, + "latency_mean_ns": 8054865.09, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "one_slow", + "variant": "threshold8", + "samples": 5, + "peak_reserved_bytes": { + "median": 303072, + "min": 303072, + "max": 303072 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1818624, + "min": 1802240, + "max": 1818624 + }, + "elapsed_ns": { + "median": 131798127, + "min": 131620844, + "max": 131976855 + }, + "cpu_ns": { + "median": 34803856, + "min": 34446035, + "max": 36997295 + }, + "parse_call_ns": { + "median": 22050963, + "min": 21905479, + "max": 22094634 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 1846.36305, + "min": 1832.609103, + "max": 1904.528214 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 64, + "latency_mean_ns": 8054286.008, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "borrow", + "samples": 5, + "peak_reserved_bytes": { + "median": 4435888, + "min": 4435888, + "max": 4435888 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 4767744, + "min": 4767744, + "max": 4767744 + }, + "elapsed_ns": { + "median": 133814643, + "min": 133553215, + "max": 134337593 + }, + "cpu_ns": { + "median": 68888479, + "min": 68534602, + "max": 71105404 + }, + "parse_call_ns": { + "median": 22344284, + "min": 22264001, + "max": 22575073 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 541341.207084, + "min": 541236.958782, + "max": 541448.462493 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 52115.836, + "latency_p99_upper_ns": 81920 + }, + { + "peak_count": 64, + "latency_mean_ns": 5078986.712, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8067041.718, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "compact", + "samples": 5, + "peak_reserved_bytes": { + "median": 4730672, + "min": 4730672, + "max": 4730672 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 2001776, + "min": 1936144, + "max": 2034592 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 6340608, + "min": 6340608, + "max": 6340608 + }, + "elapsed_ns": { + "median": 134204038, + "min": 134074359, + "max": 135831514 + }, + "cpu_ns": { + "median": 74886880, + "min": 74679639, + "max": 84355786 + }, + "parse_call_ns": { + "median": 22736073, + "min": 22714288, + "max": 23107923 + }, + "selection_copy_bytes": { + "median": 33554432, + "min": 33554432, + "max": 33554432 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 341191.973388, + "min": 341111.908821, + "max": 341580.411374 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 54208.683, + "latency_p99_upper_ns": 90112 + }, + { + "peak_count": 64, + "latency_mean_ns": 5084673.227, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8069796.873, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 4504544, + "min": 4504544, + "max": 4504544 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 4233264, + "min": 4233264, + "max": 4233264 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 5292032, + "min": 5275648, + "max": 5292032 + }, + "elapsed_ns": { + "median": 134436483, + "min": 134254523, + "max": 139508137 + }, + "cpu_ns": { + "median": 76196602, + "min": 74842901, + "max": 88332555 + }, + "parse_call_ns": { + "median": 22285338, + "min": 22151327, + "max": 22864596 + }, + "selection_copy_bytes": { + "median": 100663296, + "min": 100663296, + "max": 100663296 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2295.795833, + "min": 2277.11069, + "max": 2528.253394 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 58635.959, + "latency_p99_upper_ns": 90112 + }, + { + "peak_count": 64, + "latency_mean_ns": 5084371.237, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8075247.27, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "copy_no_pool", + "samples": 5, + "peak_reserved_bytes": { + "median": 4373248, + "min": 4373248, + "max": 4373248 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 4233264, + "min": 4233264, + "max": 4233264 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 5816320, + "min": 5799936, + "max": 6078464 + }, + "elapsed_ns": { + "median": 134471775, + "min": 134289332, + "max": 216945564 + }, + "cpu_ns": { + "median": 76002725, + "min": 75355273, + "max": 116157398 + }, + "parse_call_ns": { + "median": 22740343, + "min": 22600489, + "max": 25703347 + }, + "selection_copy_bytes": { + "median": 100663296, + "min": 100663296, + "max": 100663296 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2334.193158, + "min": 2317.391148, + "max": 3917.681175 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 59008.762, + "latency_p99_upper_ns": 90112 + }, + { + "peak_count": 64, + "latency_mean_ns": 5084982.783, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8075279.097, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "shared_copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 2371504, + "min": 2371504, + "max": 2371504 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 2100224, + "min": 2100224, + "max": 2100224 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 3293184, + "min": 3178496, + "max": 3571712 + }, + "elapsed_ns": { + "median": 133593447, + "min": 133495777, + "max": 135030934 + }, + "cpu_ns": { + "median": 66640259, + "min": 66172548, + "max": 71236281 + }, + "parse_call_ns": { + "median": 22303696, + "min": 22176603, + "max": 22446961 + }, + "selection_copy_bytes": { + "median": 33554432, + "min": 33554432, + "max": 33554432 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2044.238204, + "min": 2031.917671, + "max": 2100.123381 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 2048, + "min": 2048, + "max": 2048 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 51484.234, + "latency_p99_upper_ns": 81920 + }, + { + "peak_count": 64, + "latency_mean_ns": 5078396.125, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8066089.677, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold2", + "samples": 5, + "peak_reserved_bytes": { + "median": 2371504, + "min": 2371504, + "max": 2371504 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 2100224, + "min": 2100224, + "max": 2100224 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 3194880, + "min": 3194880, + "max": 3244032 + }, + "elapsed_ns": { + "median": 133655494, + "min": 133441147, + "max": 134034428 + }, + "cpu_ns": { + "median": 66434679, + "min": 66234578, + "max": 69077852 + }, + "parse_call_ns": { + "median": 22159339, + "min": 22119254, + "max": 22386881 + }, + "selection_copy_bytes": { + "median": 33554432, + "min": 33554432, + "max": 33554432 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2045.927873, + "min": 2030.079604, + "max": 2071.219143 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 2048, + "min": 2048, + "max": 2048 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 51484.504, + "latency_p99_upper_ns": 73728 + }, + { + "peak_count": 64, + "latency_mean_ns": 5078118.015, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8066441.419, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "same_large_field", + "variant": "threshold8", + "samples": 5, + "peak_reserved_bytes": { + "median": 4435888, + "min": 4435888, + "max": 4435888 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 4767744, + "min": 4767744, + "max": 4833280 + }, + "elapsed_ns": { + "median": 133681327, + "min": 133447386, + "max": 134457305 + }, + "cpu_ns": { + "median": 69361342, + "min": 68765370, + "max": 70251788 + }, + "parse_call_ns": { + "median": 22353361, + "min": 22338685, + "max": 22437830 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 541359.828153, + "min": 541289.267391, + "max": 541408.186663 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 52372.427, + "latency_p99_upper_ns": 81920 + }, + { + "peak_count": 64, + "latency_mean_ns": 5079700.568, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8067374.837, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "borrow", + "samples": 5, + "peak_reserved_bytes": { + "median": 4435888, + "min": 4435888, + "max": 4435888 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 4767744, + "min": 4767744, + "max": 5390336 + }, + "elapsed_ns": { + "median": 133311566, + "min": 133254368, + "max": 133477288 + }, + "cpu_ns": { + "median": 53725285, + "min": 52234245, + "max": 54530428 + }, + "parse_call_ns": { + "median": 22266351, + "min": 22239925, + "max": 22789187 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 541551.170985, + "min": 541367.893955, + "max": 541575.408384 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 46306.509, + "latency_p99_upper_ns": 61440 + }, + { + "peak_count": 64, + "latency_mean_ns": 5067325.337, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8070214.071, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "compact", + "samples": 5, + "peak_reserved_bytes": { + "median": 4730672, + "min": 4730672, + "max": 4730672 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 2100224, + "min": 2067408, + "max": 2100224 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 6340608, + "min": 6340608, + "max": 6406144 + }, + "elapsed_ns": { + "median": 133282278, + "min": 133118662, + "max": 133472857 + }, + "cpu_ns": { + "median": 59607669, + "min": 59503628, + "max": 62602975 + }, + "parse_call_ns": { + "median": 22648269, + "min": 22558632, + "max": 23134595 + }, + "selection_copy_bytes": { + "median": 33554432, + "min": 33554432, + "max": 33554432 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 340524.867797, + "min": 340502.324772, + "max": 340709.17918 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 47236.674, + "latency_p99_upper_ns": 73728 + }, + { + "peak_count": 64, + "latency_mean_ns": 5071269.907, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8068767.35, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 2378784, + "min": 2378784, + "max": 2378784 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 2107504, + "min": 2107504, + "max": 2107504 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 3948544, + "min": 3751936, + "max": 3964928 + }, + "elapsed_ns": { + "median": 133179940, + "min": 133174357, + "max": 133407209 + }, + "cpu_ns": { + "median": 52486508, + "min": 52033688, + "max": 55263103 + }, + "parse_call_ns": { + "median": 22154380, + "min": 22117835, + "max": 22433685 + }, + "selection_copy_bytes": { + "median": 33669120, + "min": 33669120, + "max": 33669120 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2028.917653, + "min": 2021.284206, + "max": 2079.608844 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 46179.91, + "latency_p99_upper_ns": 61440 + }, + { + "peak_count": 64, + "latency_mean_ns": 5068142.38, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8068218.996, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "copy_no_pool", + "samples": 5, + "peak_reserved_bytes": { + "median": 2312896, + "min": 2312896, + "max": 2312896 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 2107504, + "min": 2107504, + "max": 2107504 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 3194880, + "min": 3178496, + "max": 3194880 + }, + "elapsed_ns": { + "median": 133540522, + "min": 133362779, + "max": 134281084 + }, + "cpu_ns": { + "median": 58472614, + "min": 57999768, + "max": 62190824 + }, + "parse_call_ns": { + "median": 24345793, + "min": 24134627, + "max": 25370210 + }, + "selection_copy_bytes": { + "median": 33669120, + "min": 33669120, + "max": 33669120 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2208.417867, + "min": 2191.645151, + "max": 2300.277794 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 51364.197, + "latency_p99_upper_ns": 81920 + }, + { + "peak_count": 64, + "latency_mean_ns": 5074603.185, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8073207.582, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "shared_copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 2378672, + "min": 2378672, + "max": 2378672 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 2107392, + "min": 2107392, + "max": 2107392 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 3899392, + "min": 3293184, + "max": 3948544 + }, + "elapsed_ns": { + "median": 133216315, + "min": 133064931, + "max": 154001095 + }, + "cpu_ns": { + "median": 52117164, + "min": 51631438, + "max": 65368726 + }, + "parse_call_ns": { + "median": 22128273, + "min": 22057182, + "max": 23437273 + }, + "selection_copy_bytes": { + "median": 33611776, + "min": 33611776, + "max": 33611776 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2026.862393, + "min": 2014.496107, + "max": 2683.230182 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 46182.627, + "latency_p99_upper_ns": 61440 + }, + { + "peak_count": 64, + "latency_mean_ns": 5068255.587, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8067189.304, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold2", + "samples": 5, + "peak_reserved_bytes": { + "median": 2378672, + "min": 2378672, + "max": 2378672 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 2107392, + "min": 2107392, + "max": 2107392 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 3751936, + "min": 3342336, + "max": 3932160 + }, + "elapsed_ns": { + "median": 133312135, + "min": 133094723, + "max": 134333542 + }, + "cpu_ns": { + "median": 52726291, + "min": 52362717, + "max": 53734268 + }, + "parse_call_ns": { + "median": 22219255, + "min": 22103023, + "max": 22533891 + }, + "selection_copy_bytes": { + "median": 33611776, + "min": 33611776, + "max": 33611776 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2039.714059, + "min": 2022.420003, + "max": 2071.170012 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 46192.459, + "latency_p99_upper_ns": 65536 + }, + { + "peak_count": 64, + "latency_mean_ns": 5068879.809, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8069081.323, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "slow_large_field", + "variant": "threshold8", + "samples": 5, + "peak_reserved_bytes": { + "median": 4443056, + "min": 4443056, + "max": 4443056 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 5292032, + "min": 4751360, + "max": 5439488 + }, + "elapsed_ns": { + "median": 133341496, + "min": 133297584, + "max": 134650847 + }, + "cpu_ns": { + "median": 54857945, + "min": 53670610, + "max": 58145169 + }, + "parse_call_ns": { + "median": 22618201, + "min": 22263776, + "max": 22801055 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 541518.37496, + "min": 541459.255198, + "max": 542663.104532 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 47550.497, + "latency_p99_upper_ns": 73728 + }, + { + "peak_count": 64, + "latency_mean_ns": 5070242.543, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8069675.14, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "borrow", + "samples": 5, + "peak_reserved_bytes": { + "median": 4435888, + "min": 4435888, + "max": 4435888 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 5373952, + "min": 5373952, + "max": 5373952 + }, + "elapsed_ns": { + "median": 132595906, + "min": 132489898, + "max": 133433884 + }, + "cpu_ns": { + "median": 49691116, + "min": 48808681, + "max": 51462618 + }, + "parse_call_ns": { + "median": 22667622, + "min": 22622066, + "max": 23747967 + }, + "selection_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 540593.356702, + "min": 540484.370521, + "max": 540814.68462 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 45356.652, + "latency_p99_upper_ns": 65536 + }, + { + "peak_count": 64, + "latency_mean_ns": 5069713.765, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8055489.045, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "compact", + "samples": 5, + "peak_reserved_bytes": { + "median": 4436784, + "min": 4436784, + "max": 4436784 + }, + "source_peak_bytes": { + "median": 4230192, + "min": 4230192, + "max": 4230192 + }, + "output_peak_bytes": { + "median": 6944, + "min": 6944, + "max": 7056 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 5537792, + "min": 5488640, + "max": 5603328 + }, + "elapsed_ns": { + "median": 132699783, + "min": 132597323, + "max": 132746292 + }, + "cpu_ns": { + "median": 50225388, + "min": 49739534, + "max": 50870250 + }, + "parse_call_ns": { + "median": 22671992, + "min": 22546544, + "max": 22749218 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 340108.105991, + "min": 340105.112239, + "max": 340246.674827 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 45463.711, + "latency_p99_upper_ns": 65536 + }, + { + "peak_count": 64, + "latency_mean_ns": 5069991.558, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8055038.37, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 318432, + "min": 318432, + "max": 318432 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 47152, + "min": 47152, + "max": 47152 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1851392, + "min": 1851392, + "max": 1867776 + }, + "elapsed_ns": { + "median": 132282510, + "min": 132122049, + "max": 132351078 + }, + "cpu_ns": { + "median": 46122525, + "min": 45960158, + "max": 46415608 + }, + "parse_call_ns": { + "median": 22062914, + "min": 22012014, + "max": 22082400 + }, + "selection_copy_bytes": { + "median": 33669120, + "min": 33669120, + "max": 33669120 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 1991.776071, + "min": 1985.913742, + "max": 1996.444688 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 42165.024, + "latency_p99_upper_ns": 53248 + }, + { + "peak_count": 64, + "latency_mean_ns": 5068352.743, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8050279.443, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "copy_no_pool", + "samples": 5, + "peak_reserved_bytes": { + "median": 252544, + "min": 252544, + "max": 252544 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 47152, + "min": 47152, + "max": 47152 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1900544, + "min": 1884160, + "max": 1933312 + }, + "elapsed_ns": { + "median": 132297747, + "min": 132221655, + "max": 132985160 + }, + "cpu_ns": { + "median": 46669804, + "min": 46345346, + "max": 49981054 + }, + "parse_call_ns": { + "median": 22170762, + "min": 22094470, + "max": 23442854 + }, + "selection_copy_bytes": { + "median": 33669120, + "min": 33669120, + "max": 33669120 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2009.648765, + "min": 1998.568774, + "max": 2129.142526 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 0, + "min": 0, + "max": 0 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 42678.905, + "latency_p99_upper_ns": 61440 + }, + { + "peak_count": 64, + "latency_mean_ns": 5068637.263, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8054142.04, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "shared_copy", + "samples": 5, + "peak_reserved_bytes": { + "median": 311264, + "min": 311264, + "max": 311264 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 39984, + "min": 39984, + "max": 39984 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1851392, + "min": 1835008, + "max": 1851392 + }, + "elapsed_ns": { + "median": 132284819, + "min": 132140503, + "max": 133179387 + }, + "cpu_ns": { + "median": 45991210, + "min": 45823432, + "max": 47009567 + }, + "parse_call_ns": { + "median": 22104890, + "min": 22053622, + "max": 22145612 + }, + "selection_copy_bytes": { + "median": 33611776, + "min": 33611776, + "max": 33611776 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 1991.371251, + "min": 1983.569581, + "max": 2001.098596 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 42139.616, + "latency_p99_upper_ns": 57344 + }, + { + "peak_count": 64, + "latency_mean_ns": 5068344.258, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8052022.729, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold2", + "samples": 5, + "peak_reserved_bytes": { + "median": 311264, + "min": 311264, + "max": 311264 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 39984, + "min": 39984, + "max": 39984 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1851392, + "min": 1835008, + "max": 1867776 + }, + "elapsed_ns": { + "median": 132243573, + "min": 132119197, + "max": 132423215 + }, + "cpu_ns": { + "median": 45811373, + "min": 45674856, + "max": 46202689 + }, + "parse_call_ns": { + "median": 22085581, + "min": 22029775, + "max": 22137673 + }, + "selection_copy_bytes": { + "median": 33611776, + "min": 33611776, + "max": 33611776 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 1984.871517, + "min": 1978.146096, + "max": 1992.776523 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 42043.254, + "latency_p99_upper_ns": 57344 + }, + { + "peak_count": 64, + "latency_mean_ns": 5068193.573, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8050581.195, + "latency_p99_upper_ns": 8388608 + } + ] + }, + { + "case": "three_distinct_speeds", + "variant": "threshold8", + "samples": 5, + "peak_reserved_bytes": { + "median": 311264, + "min": 311264, + "max": 311264 + }, + "source_peak_bytes": { + "median": 98400, + "min": 98400, + "max": 98400 + }, + "output_peak_bytes": { + "median": 7168, + "min": 7168, + "max": 7168 + }, + "parser_peak_bytes": { + "median": 131120, + "min": 131120, + "max": 131120 + }, + "rss_peak_bytes": { + "median": 1851392, + "min": 1851392, + "max": 1851392 + }, + "elapsed_ns": { + "median": 132177385, + "min": 132072752, + "max": 132687220 + }, + "cpu_ns": { + "median": 44977578, + "min": 44815017, + "max": 45610919 + }, + "parse_call_ns": { + "median": 22128368, + "min": 22007505, + "max": 22143479 + }, + "selection_copy_bytes": { + "median": 57344, + "min": 57344, + "max": 57344 + }, + "reader_copy_bytes": { + "median": 75711296, + "min": 75711296, + "max": 75711296 + }, + "parser_realloc_copy_bytes": { + "median": 0, + "min": 0, + "max": 0 + }, + "source_byte_seconds": { + "median": 2381.095196, + "min": 2372.402719, + "max": 2398.303122 + }, + "budget_waits": { + "median": 0, + "min": 0, + "max": 0 + }, + "compacted": { + "median": 0, + "min": 0, + "max": 0 + }, + "dedup_hits": { + "median": 1024, + "min": 1024, + "max": 1024 + }, + "queues": [ + { + "peak_count": 1, + "latency_mean_ns": 41268.295, + "latency_p99_upper_ns": 57344 + }, + { + "peak_count": 64, + "latency_mean_ns": 5067698.961, + "latency_p99_upper_ns": 5242880 + }, + { + "peak_count": 64, + "latency_mean_ns": 8050851.553, + "latency_p99_upper_ns": 8388608 + } + ] + } +] diff --git a/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/verification.json b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/verification.json new file mode 100644 index 0000000..ca94ae7 --- /dev/null +++ b/research/experiments/003-json-fanout/results/2026-09-22-linux-aarch64/verification.json @@ -0,0 +1,114 @@ +{ + "native": [ + "borrow: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "borrow: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "borrow: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "borrow: empty input, final JSON without newline, first duplicate member", + "borrow: eight invalid JSON/schema cases with retained owners", + "borrow: cancellation preserves each branch prefix and releases all shared owners", + "borrow: parser allocation charged and refused within total budget", + "copy: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy: empty input, final JSON without newline, first duplicate member", + "copy: eight invalid JSON/schema cases with retained owners", + "copy: cancellation preserves each branch prefix and releases all shared owners", + "copy: parser allocation charged and refused within total budget", + "shared_copy: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "shared_copy: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "shared_copy: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "shared_copy: empty input, final JSON without newline, first duplicate member", + "shared_copy: eight invalid JSON/schema cases with retained owners", + "shared_copy: cancellation preserves each branch prefix and releases all shared owners", + "shared_copy: parser allocation charged and refused within total budget", + "threshold2: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold2: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold2: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold2: empty input, final JSON without newline, first duplicate member", + "threshold2: eight invalid JSON/schema cases with retained owners", + "threshold2: cancellation preserves each branch prefix and releases all shared owners", + "threshold2: parser allocation charged and refused within total budget", + "threshold8: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold8: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold8: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold8: empty input, final JSON without newline, first duplicate member", + "threshold8: eight invalid JSON/schema cases with retained owners", + "threshold8: cancellation preserves each branch prefix and releases all shared owners", + "threshold8: parser allocation charged and refused within total budget", + "compact: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "compact: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "compact: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "compact: empty input, final JSON without newline, first duplicate member", + "compact: eight invalid JSON/schema cases with retained owners", + "compact: cancellation preserves each branch prefix and releases all shared owners", + "compact: parser allocation charged and refused within total budget", + "copy_no_pool: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy_no_pool: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy_no_pool: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy_no_pool: empty input, final JSON without newline, first duplicate member", + "copy_no_pool: eight invalid JSON/schema cases with retained owners", + "copy_no_pool: cancellation preserves each branch prefix and releases all shared owners", + "copy_no_pool: parser allocation charged and refused within total budget", + "identical selections copied once and live until the final consumer", + "last-owner compaction keeps the delayed decoded value valid", + "overlong NDJSON record refused without leaked source", + "actual /dev/full failure releases multiple owners", + "SIGTERM during delayed fan-out releases all owners" + ], + "asan_ubsan": [ + "borrow: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "borrow: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "borrow: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "borrow: empty input, final JSON without newline, first duplicate member", + "borrow: eight invalid JSON/schema cases with retained owners", + "borrow: cancellation preserves each branch prefix and releases all shared owners", + "borrow: parser allocation charged and refused within total budget", + "copy: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy: empty input, final JSON without newline, first duplicate member", + "copy: eight invalid JSON/schema cases with retained owners", + "copy: cancellation preserves each branch prefix and releases all shared owners", + "copy: parser allocation charged and refused within total budget", + "shared_copy: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "shared_copy: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "shared_copy: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "shared_copy: empty input, final JSON without newline, first duplicate member", + "shared_copy: eight invalid JSON/schema cases with retained owners", + "shared_copy: cancellation preserves each branch prefix and releases all shared owners", + "shared_copy: parser allocation charged and refused within total budget", + "threshold2: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold2: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold2: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold2: empty input, final JSON without newline, first duplicate member", + "threshold2: eight invalid JSON/schema cases with retained owners", + "threshold2: cancellation preserves each branch prefix and releases all shared owners", + "threshold2: parser allocation charged and refused within total budget", + "threshold8: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold8: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold8: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "threshold8: empty input, final JSON without newline, first duplicate member", + "threshold8: eight invalid JSON/schema cases with retained owners", + "threshold8: cancellation preserves each branch prefix and releases all shared owners", + "threshold8: parser allocation charged and refused within total budget", + "compact: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "compact: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "compact: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "compact: empty input, final JSON without newline, first duplicate member", + "compact: eight invalid JSON/schema cases with retained owners", + "compact: cancellation preserves each branch prefix and releases all shared owners", + "compact: parser allocation charged and refused within total budget", + "copy_no_pool: 1 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy_no_pool: 2 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy_no_pool: 3 consumer(s), UTF-8/escapes/NUL/empty, slot reuse", + "copy_no_pool: empty input, final JSON without newline, first duplicate member", + "copy_no_pool: eight invalid JSON/schema cases with retained owners", + "copy_no_pool: cancellation preserves each branch prefix and releases all shared owners", + "copy_no_pool: parser allocation charged and refused within total budget", + "identical selections copied once and live until the final consumer", + "last-owner compaction keeps the delayed decoded value valid", + "overlong NDJSON record refused without leaked source", + "actual /dev/full failure releases multiple owners", + "SIGTERM during delayed fan-out releases all owners" + ] +} |
