From 9ac37cd62629ef732161a1cbbb0c6148292ce5e0 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 8 Jun 2026 09:11:20 +0200 Subject: [PATCH] [ruby/json] generator.c: don't start with a stack buffer in IO case It was never intended to be initialized with a stack buffer in that case, as when dumping into an IO we want larger chunks. https://github.com/ruby/json/commit/996bac686d --- ext/json/fbuffer/fbuffer.h | 43 +++++++++++++++++----------------- ext/json/generator/generator.c | 12 ++++------ 2 files changed, 25 insertions(+), 30 deletions(-) diff --git a/ext/json/fbuffer/fbuffer.h b/ext/json/fbuffer/fbuffer.h index b4f5266ca5..cc44ea0308 100644 --- a/ext/json/fbuffer/fbuffer.h +++ b/ext/json/fbuffer/fbuffer.h @@ -37,13 +37,17 @@ static void fbuffer_append_long(FBuffer *fb, long number); static inline void fbuffer_append_char(FBuffer *fb, char newchr); static VALUE fbuffer_finalize(FBuffer *fb); -static void fbuffer_stack_init(FBuffer *fb, size_t initial_length, char *stack_buffer, size_t stack_buffer_size) +static void fbuffer_init(FBuffer *fb, size_t initial_length, VALUE io, char *stack_buffer, size_t stack_buffer_size) { - fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT; - if (stack_buffer) { + if (RTEST(io)) { + JSON_ASSERT(fb->type == FBUFFER_HEAP_ALLOCATED); + fb->io = io; + fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_IO_BUFFER_SIZE; + } else { fb->type = FBUFFER_STACK_ALLOCATED; fb->ptr = stack_buffer; fb->capa = stack_buffer_size; + fb->initial_length = (initial_length > 0) ? initial_length : FBUFFER_INITIAL_LENGTH_DEFAULT; } #if JSON_DEBUG fb->requested = 0; @@ -79,45 +83,40 @@ static void fbuffer_flush(FBuffer *fb) fbuffer_clear(fb); } -static void fbuffer_realloc(FBuffer *fb, size_t required) +static void fbuffer_realloc(FBuffer *fb, size_t new_capa) { - if (required > fb->capa) { + if (new_capa > fb->capa) { if (fb->type == FBUFFER_STACK_ALLOCATED) { const char *old_buffer = fb->ptr; - fb->ptr = ALLOC_N(char, required); + fb->ptr = ALLOC_N(char, new_capa); fb->type = FBUFFER_HEAP_ALLOCATED; MEMCPY(fb->ptr, old_buffer, char, fb->len); } else { - JSON_SIZED_REALLOC_N(fb->ptr, char, required, fb->capa); + JSON_SIZED_REALLOC_N(fb->ptr, char, new_capa, fb->capa); } - fb->capa = required; + fb->capa = new_capa; } } static void fbuffer_do_inc_capa(FBuffer *fb, size_t requested) { if (RB_UNLIKELY(fb->io)) { - if (fb->capa < FBUFFER_IO_BUFFER_SIZE) { - fbuffer_realloc(fb, FBUFFER_IO_BUFFER_SIZE); - } else { + if (fb->capa != 0) { fbuffer_flush(fb); - } - - if (RB_LIKELY(requested < fb->capa)) { - return; + if (RB_LIKELY(requested < fb->capa)) { + return; + } } } - size_t required; + size_t new_capa = fb->capa ? fb->capa : fb->initial_length; + size_t needed_capa = requested + fb->len; - if (RB_UNLIKELY(!fb->ptr)) { - fb->ptr = ALLOC_N(char, fb->initial_length); - fb->capa = fb->initial_length; + while (new_capa < needed_capa) { + new_capa *= 2; } - for (required = fb->capa; requested > required - fb->len; required <<= 1); - - fbuffer_realloc(fb, required); + fbuffer_realloc(fb, new_capa); } static inline void fbuffer_inc_capa(FBuffer *fb, size_t requested) diff --git a/ext/json/generator/generator.c b/ext/json/generator/generator.c index 82853633ba..08c1fd0db6 100644 --- a/ext/json/generator/generator.c +++ b/ext/json/generator/generator.c @@ -1308,10 +1308,8 @@ static inline VALUE cState_partial_generate(VALUE self, VALUE obj, generator_fun GET_STATE(self); char stack_buffer[FBUFFER_STACK_SIZE]; - FBuffer buffer = { - .io = RTEST(io) ? io : Qfalse, - }; - fbuffer_stack_init(&buffer, state->buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); + FBuffer buffer = { 0 }; + fbuffer_init(&buffer, state->buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE); struct generate_json_data data = { .buffer = &buffer, @@ -1870,10 +1868,8 @@ static VALUE cState_m_do_generate(VALUE klass, VALUE obj, VALUE opts, VALUE io, configure_state(&state, Qfalse, opts); char stack_buffer[FBUFFER_STACK_SIZE]; - FBuffer buffer = { - .io = RTEST(io) ? io : Qfalse, - }; - fbuffer_stack_init(&buffer, state.buffer_initial_length, stack_buffer, FBUFFER_STACK_SIZE); + FBuffer buffer = { 0 }; + fbuffer_init(&buffer, state.buffer_initial_length, io, stack_buffer, FBUFFER_STACK_SIZE); struct generate_json_data data = { .buffer = &buffer,