[ruby/json] generator.c: store pretty strings in VALUE

Given we expect these to almost always be null, we might as
well keep them in RString.

And even when provided, assuming we're passed frozen strings
we'll save on copying them.

This also reduce the size of the struct from 112B to 72B.

https://github.com/ruby/json/commit/6382c231b0
This commit is contained in:
Jean Boussier 2024-10-29 15:03:42 +01:00 committed by Hiroshi SHIBATA
parent b042d9d9c1
commit 5dc3b15b3c
2 changed files with 74 additions and 112 deletions

View File

@ -297,14 +297,6 @@ static void convert_UTF8_to_ASCII_only_JSON(FBuffer *out_buffer, VALUE str, cons
RB_GC_GUARD(str); RB_GC_GUARD(str);
} }
static char *fstrndup(const char *ptr, unsigned long len) {
char *result;
if (len <= 0) return NULL;
result = ALLOC_N(char, len);
memcpy(result, ptr, len);
return result;
}
/* /*
* Document-module: JSON::Ext::Generator * Document-module: JSON::Ext::Generator
* *
@ -587,27 +579,35 @@ static VALUE mObject_to_json(int argc, VALUE *argv, VALUE self)
return cState_partial_generate(state, string, generate_json_string); return cState_partial_generate(state, string, generate_json_string);
} }
static void State_mark(void *ptr)
{
JSON_Generator_State *state = ptr;
rb_gc_mark_movable(state->indent);
rb_gc_mark_movable(state->space);
rb_gc_mark_movable(state->space_before);
rb_gc_mark_movable(state->object_nl);
rb_gc_mark_movable(state->array_nl);
}
static void State_compact(void *ptr)
{
JSON_Generator_State *state = ptr;
state->indent = rb_gc_location(state->indent);
state->space = rb_gc_location(state->space);
state->space_before = rb_gc_location(state->space_before);
state->object_nl = rb_gc_location(state->object_nl);
state->array_nl = rb_gc_location(state->array_nl);
}
static void State_free(void *ptr) static void State_free(void *ptr)
{ {
JSON_Generator_State *state = ptr; JSON_Generator_State *state = ptr;
if (state->indent) ruby_xfree(state->indent);
if (state->space) ruby_xfree(state->space);
if (state->space_before) ruby_xfree(state->space_before);
if (state->object_nl) ruby_xfree(state->object_nl);
if (state->array_nl) ruby_xfree(state->array_nl);
ruby_xfree(state); ruby_xfree(state);
} }
static size_t State_memsize(const void *ptr) static size_t State_memsize(const void *ptr)
{ {
const JSON_Generator_State *state = ptr; return sizeof(JSON_Generator_State);
size_t size = sizeof(*state);
if (state->indent) size += state->indent_len + 1;
if (state->space) size += state->space_len + 1;
if (state->space_before) size += state->space_before_len + 1;
if (state->object_nl) size += state->object_nl_len + 1;
if (state->array_nl) size += state->array_nl_len + 1;
return size;
} }
#ifndef HAVE_RB_EXT_RACTOR_SAFE #ifndef HAVE_RB_EXT_RACTOR_SAFE
@ -617,7 +617,12 @@ static size_t State_memsize(const void *ptr)
static const rb_data_type_t JSON_Generator_State_type = { static const rb_data_type_t JSON_Generator_State_type = {
"JSON/Generator/State", "JSON/Generator/State",
{NULL, State_free, State_memsize,}, {
.dmark = State_mark,
.dfree = State_free,
.dsize = State_memsize,
.dcompact = State_compact,
},
0, 0, 0, 0,
RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
}; };
@ -651,11 +656,11 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
if (arg->iter > 0) fbuffer_append_char(buffer, ','); if (arg->iter > 0) fbuffer_append_char(buffer, ',');
if (RB_UNLIKELY(state->object_nl)) { if (RB_UNLIKELY(state->object_nl)) {
fbuffer_append(buffer, state->object_nl, state->object_nl_len); fbuffer_append_str(buffer, state->object_nl);
} }
if (RB_UNLIKELY(state->indent)) { if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) { for (j = 0; j < depth; j++) {
fbuffer_append(buffer, state->indent, state->indent_len); fbuffer_append_str(buffer, state->indent);
} }
} }
@ -673,9 +678,9 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
} }
generate_json_string(buffer, Vstate, state, key_to_s); generate_json_string(buffer, Vstate, state, key_to_s);
if (RB_UNLIKELY(state->space_before)) fbuffer_append(buffer, state->space_before, state->space_before_len); if (RB_UNLIKELY(state->space_before)) fbuffer_append_str(buffer, state->space_before);
fbuffer_append_char(buffer, ':'); fbuffer_append_char(buffer, ':');
if (RB_UNLIKELY(state->space)) fbuffer_append(buffer, state->space, state->space_len); if (RB_UNLIKELY(state->space)) fbuffer_append_str(buffer, state->space);
generate_json(buffer, Vstate, state, val); generate_json(buffer, Vstate, state, val);
arg->iter++; arg->iter++;
@ -709,10 +714,10 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
depth = --state->depth; depth = --state->depth;
if (RB_UNLIKELY(state->object_nl)) { if (RB_UNLIKELY(state->object_nl)) {
fbuffer_append(buffer, state->object_nl, state->object_nl_len); fbuffer_append_str(buffer, state->object_nl);
if (RB_UNLIKELY(state->indent)) { if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) { for (j = 0; j < depth; j++) {
fbuffer_append(buffer, state->indent, state->indent_len); fbuffer_append_str(buffer, state->indent);
} }
} }
} }
@ -735,25 +740,25 @@ static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
} }
fbuffer_append_char(buffer, '['); fbuffer_append_char(buffer, '[');
if (RB_UNLIKELY(state->array_nl)) fbuffer_append(buffer, state->array_nl, state->array_nl_len); if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
for(i = 0; i < RARRAY_LEN(obj); i++) { for(i = 0; i < RARRAY_LEN(obj); i++) {
if (i > 0) { if (i > 0) {
fbuffer_append_char(buffer, ','); fbuffer_append_char(buffer, ',');
if (RB_UNLIKELY(state->array_nl)) fbuffer_append(buffer, state->array_nl, state->array_nl_len); if (RB_UNLIKELY(state->array_nl)) fbuffer_append_str(buffer, state->array_nl);
} }
if (RB_UNLIKELY(state->indent)) { if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) { for (j = 0; j < depth; j++) {
fbuffer_append(buffer, state->indent, state->indent_len); fbuffer_append_str(buffer, state->indent);
} }
} }
generate_json(buffer, Vstate, state, RARRAY_AREF(obj, i)); generate_json(buffer, Vstate, state, RARRAY_AREF(obj, i));
} }
state->depth = --depth; state->depth = --depth;
if (RB_UNLIKELY(state->array_nl)) { if (RB_UNLIKELY(state->array_nl)) {
fbuffer_append(buffer, state->array_nl, state->array_nl_len); fbuffer_append_str(buffer, state->array_nl);
if (RB_UNLIKELY(state->indent)) { if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) { for (j = 0; j < depth; j++) {
fbuffer_append(buffer, state->indent, state->indent_len); fbuffer_append_str(buffer, state->indent);
} }
} }
} }
@ -1007,11 +1012,11 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State"); if (!objState) rb_raise(rb_eArgError, "unallocated JSON::State");
MEMCPY(objState, origState, JSON_Generator_State, 1); MEMCPY(objState, origState, JSON_Generator_State, 1);
objState->indent = fstrndup(origState->indent, origState->indent_len); objState->indent = origState->indent;
objState->space = fstrndup(origState->space, origState->space_len); objState->space = origState->space;
objState->space_before = fstrndup(origState->space_before, origState->space_before_len); objState->space_before = origState->space_before;
objState->object_nl = fstrndup(origState->object_nl, origState->object_nl_len); objState->object_nl = origState->object_nl;
objState->array_nl = fstrndup(origState->array_nl, origState->array_nl_len); objState->array_nl = origState->array_nl;
return obj; return obj;
} }
@ -1041,7 +1046,7 @@ static VALUE cState_from_state_s(VALUE self, VALUE opts)
static VALUE cState_indent(VALUE self) static VALUE cState_indent(VALUE self)
{ {
GET_STATE(self); GET_STATE(self);
return state->indent ? rb_str_new(state->indent, state->indent_len) : rb_str_new2(""); return state->indent ? state->indent : rb_str_freeze(rb_utf8_str_new("", 0));
} }
/* /*
@ -1051,20 +1056,12 @@ static VALUE cState_indent(VALUE self)
*/ */
static VALUE cState_indent_set(VALUE self, VALUE indent) static VALUE cState_indent_set(VALUE self, VALUE indent)
{ {
unsigned long len;
GET_STATE(self); GET_STATE(self);
Check_Type(indent, T_STRING); Check_Type(indent, T_STRING);
len = RSTRING_LEN(indent); if (RSTRING_LEN(indent)) {
if (len == 0) { state->indent = RB_OBJ_FROZEN(indent) ? indent : rb_str_freeze(rb_str_dup(indent));
if (state->indent) {
ruby_xfree(state->indent);
state->indent = NULL;
state->indent_len = 0;
}
} else { } else {
if (state->indent) ruby_xfree(state->indent); state->indent = Qfalse;
state->indent = fstrndup(RSTRING_PTR(indent), len);
state->indent_len = len;
} }
return Qnil; return Qnil;
} }
@ -1078,7 +1075,7 @@ static VALUE cState_indent_set(VALUE self, VALUE indent)
static VALUE cState_space(VALUE self) static VALUE cState_space(VALUE self)
{ {
GET_STATE(self); GET_STATE(self);
return state->space ? rb_str_new(state->space, state->space_len) : rb_str_new2(""); return state->space ? state->space : rb_str_freeze(rb_utf8_str_new("", 0));
} }
/* /*
@ -1089,20 +1086,12 @@ static VALUE cState_space(VALUE self)
*/ */
static VALUE cState_space_set(VALUE self, VALUE space) static VALUE cState_space_set(VALUE self, VALUE space)
{ {
unsigned long len;
GET_STATE(self); GET_STATE(self);
Check_Type(space, T_STRING); Check_Type(space, T_STRING);
len = RSTRING_LEN(space); if (RSTRING_LEN(space)) {
if (len == 0) { state->space = RB_OBJ_FROZEN(space) ? space : rb_str_freeze(rb_str_dup(space));
if (state->space) {
ruby_xfree(state->space);
state->space = NULL;
state->space_len = 0;
}
} else { } else {
if (state->space) ruby_xfree(state->space); state->space = Qfalse;
state->space = fstrndup(RSTRING_PTR(space), len);
state->space_len = len;
} }
return Qnil; return Qnil;
} }
@ -1115,7 +1104,7 @@ static VALUE cState_space_set(VALUE self, VALUE space)
static VALUE cState_space_before(VALUE self) static VALUE cState_space_before(VALUE self)
{ {
GET_STATE(self); GET_STATE(self);
return state->space_before ? rb_str_new(state->space_before, state->space_before_len) : rb_str_new2(""); return state->space_before ? state->space_before : rb_str_freeze(rb_utf8_str_new("", 0));
} }
/* /*
@ -1125,20 +1114,12 @@ static VALUE cState_space_before(VALUE self)
*/ */
static VALUE cState_space_before_set(VALUE self, VALUE space_before) static VALUE cState_space_before_set(VALUE self, VALUE space_before)
{ {
unsigned long len;
GET_STATE(self); GET_STATE(self);
Check_Type(space_before, T_STRING); Check_Type(space_before, T_STRING);
len = RSTRING_LEN(space_before); if (RSTRING_LEN(space_before)) {
if (len == 0) { state->space_before = RB_OBJ_FROZEN(space_before) ? space_before : rb_str_freeze(rb_str_dup(space_before));
if (state->space_before) {
ruby_xfree(state->space_before);
state->space_before = NULL;
state->space_before_len = 0;
}
} else { } else {
if (state->space_before) ruby_xfree(state->space_before); state->space_before = Qfalse;
state->space_before = fstrndup(RSTRING_PTR(space_before), len);
state->space_before_len = len;
} }
return Qnil; return Qnil;
} }
@ -1152,7 +1133,7 @@ static VALUE cState_space_before_set(VALUE self, VALUE space_before)
static VALUE cState_object_nl(VALUE self) static VALUE cState_object_nl(VALUE self)
{ {
GET_STATE(self); GET_STATE(self);
return state->object_nl ? rb_str_new(state->object_nl, state->object_nl_len) : rb_str_new2(""); return state->object_nl ? state->object_nl : rb_str_freeze(rb_utf8_str_new("", 0));
} }
/* /*
@ -1163,19 +1144,12 @@ static VALUE cState_object_nl(VALUE self)
*/ */
static VALUE cState_object_nl_set(VALUE self, VALUE object_nl) static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
{ {
unsigned long len;
GET_STATE(self); GET_STATE(self);
Check_Type(object_nl, T_STRING); Check_Type(object_nl, T_STRING);
len = RSTRING_LEN(object_nl); if (RSTRING_LEN(object_nl)) {
if (len == 0) { state->object_nl = RB_OBJ_FROZEN(object_nl) ? object_nl : rb_str_freeze(rb_str_dup(object_nl));
if (state->object_nl) {
ruby_xfree(state->object_nl);
state->object_nl = NULL;
}
} else { } else {
if (state->object_nl) ruby_xfree(state->object_nl); state->object_nl = Qfalse;
state->object_nl = fstrndup(RSTRING_PTR(object_nl), len);
state->object_nl_len = len;
} }
return Qnil; return Qnil;
} }
@ -1188,7 +1162,7 @@ static VALUE cState_object_nl_set(VALUE self, VALUE object_nl)
static VALUE cState_array_nl(VALUE self) static VALUE cState_array_nl(VALUE self)
{ {
GET_STATE(self); GET_STATE(self);
return state->array_nl ? rb_str_new(state->array_nl, state->array_nl_len) : rb_str_new2(""); return state->array_nl ? state->array_nl : rb_str_freeze(rb_utf8_str_new("", 0));
} }
/* /*
@ -1198,19 +1172,12 @@ static VALUE cState_array_nl(VALUE self)
*/ */
static VALUE cState_array_nl_set(VALUE self, VALUE array_nl) static VALUE cState_array_nl_set(VALUE self, VALUE array_nl)
{ {
unsigned long len;
GET_STATE(self); GET_STATE(self);
Check_Type(array_nl, T_STRING); Check_Type(array_nl, T_STRING);
len = RSTRING_LEN(array_nl); if (RSTRING_LEN(array_nl)) {
if (len == 0) { state->array_nl = RB_OBJ_FROZEN(array_nl) ? array_nl : rb_str_freeze(rb_str_dup(array_nl));
if (state->array_nl) {
ruby_xfree(state->array_nl);
state->array_nl = NULL;
}
} else { } else {
if (state->array_nl) ruby_xfree(state->array_nl); state->array_nl = Qfalse;
state->array_nl = fstrndup(RSTRING_PTR(array_nl), len);
state->array_nl_len = len;
} }
return Qnil; return Qnil;
} }

View File

@ -23,28 +23,23 @@ typedef unsigned char _Bool;
#endif #endif
#endif #endif
static char *fstrndup(const char *ptr, unsigned long len);
/* ruby api and some helpers */ /* ruby api and some helpers */
typedef struct JSON_Generator_StateStruct { typedef struct JSON_Generator_StateStruct {
char *indent; VALUE indent;
long indent_len; VALUE space;
char *space; VALUE space_before;
long space_len; VALUE object_nl;
char *space_before; VALUE array_nl;
long space_before_len;
char *object_nl;
long object_nl_len;
char *array_nl;
long array_nl_len;
long max_nesting; long max_nesting;
char allow_nan;
char ascii_only;
char script_safe;
char strict;
long depth; long depth;
long buffer_initial_length; long buffer_initial_length;
bool allow_nan;
bool ascii_only;
bool script_safe;
bool strict;
} JSON_Generator_State; } JSON_Generator_State;
#define GET_STATE_TO(self, state) \ #define GET_STATE_TO(self, state) \