Skip to content

refactor: convert json_encode_item() from recursive to iterative#19839

Closed
mattn wants to merge 2 commits intovim:masterfrom
mattn:refactor-json-encode-iterative
Closed

refactor: convert json_encode_item() from recursive to iterative#19839
mattn wants to merge 2 commits intovim:masterfrom
mattn:refactor-json-encode-iterative

Conversation

@mattn
Copy link
Copy Markdown
Member

@mattn mattn commented Mar 27, 2026

Convert json_encode_item() from a recursive implementation to an iterative one using an explicit stack (garray_T of json_enc_frame_T frames).

The recursive version uses C call stack proportional to nesting depth, which can cause a stack overflow (segfault) on deeply nested structures even when maxfuncdepth is set high enough to allow it. The iterative version uses heap-allocated frames, so nesting depth is limited only by available memory.

Benchmark comparison (average of 3 runs, ops/sec):

Test case master this branch change
string 456,603 459,764 +0.7%
number 446,534 453,610 +1.6%
small_dict 424,878 414,664 -2.4%
nested_list 341,987 337,716 -1.2%
deep_100 325,402 327,886 +0.8%
wide_1000 12,940 12,755 -1.4%
dict_100 79,998 83,109 +3.9%
mixed 68,109 74,406 +9.2%
blob 443,559 446,154 +0.6%

No performance regression. Complex structures (mixed, dict_100) show improvement.

Replace recursive json_encode_item() with an explicit stack-based
iterative implementation. This prevents stack overflow on deeply
nested JSON structures and improves safety for LSP, ch_listen(),
and other external data sources.

The explicit stack (json_enc_frame_T) tracks list/tuple/dict
iterator state. On error, all pending copyIDs are properly cleaned
up, which the recursive version did not do.
@chrisbra
Copy link
Copy Markdown
Member

Thanks, but this goto enode_value is really ugly. Can you turn this into a loop instead?

Replace encode_value/item_done goto labels with for(;;) loops.
The outer loop replaces goto encode_value (via continue), and the
inner loop replaces goto item_done (via an advance flag to break
back to the outer loop when the next value is ready).
@chrisbra chrisbra closed this in 71c10dc Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants