Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 4 additions & 11 deletions ext/json/ext/fbuffer/fbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,15 @@ typedef struct FBufferStruct {

static FBuffer *fbuffer_alloc(unsigned long initial_length);
static void fbuffer_free(FBuffer *fb);
#ifndef JSON_GENERATOR
static void fbuffer_clear(FBuffer *fb);
#endif
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
#ifdef JSON_GENERATOR
static void fbuffer_append_long(FBuffer *fb, long number);
#endif
static void fbuffer_append_char(FBuffer *fb, char newchr);
#ifdef JSON_GENERATOR
static FBuffer *fbuffer_dup(FBuffer *fb);
static VALUE fbuffer_to_s(FBuffer *fb);
#endif

Expand All @@ -86,10 +87,12 @@ static void fbuffer_free(FBuffer *fb)
ruby_xfree(fb);
}

#ifndef JSON_GENERATOR
static void fbuffer_clear(FBuffer *fb)
{
fb->len = 0;
}
#endif

static inline void fbuffer_inc_capa(FBuffer *fb, unsigned long requested)
{
Expand Down Expand Up @@ -168,16 +171,6 @@ static void fbuffer_append_long(FBuffer *fb, long number)
fbuffer_append(fb, buf, len);
}

static FBuffer *fbuffer_dup(FBuffer *fb)
{
unsigned long len = fb->len;
FBuffer *result;

result = fbuffer_alloc(len);
fbuffer_append(result, FBUFFER_PAIR(fb));
return result;
}

static VALUE fbuffer_to_s(FBuffer *fb)
{
VALUE result = rb_str_new(FBUFFER_PTR(fb), FBUFFER_LEN(fb));
Expand Down
178 changes: 82 additions & 96 deletions ext/json/ext/generator/generator.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include "../fbuffer/fbuffer.h"
#include "generator.h"

#ifndef RB_UNLIKELY
#define RB_UNLIKELY(cond) (cond)
#endif

static VALUE mJSON, mExt, mGenerator, cState, mGeneratorMethods, mObject,
mHash, mArray,
#ifdef RUBY_INTEGER_UNIFICATION
Expand Down Expand Up @@ -417,9 +421,6 @@ static void State_free(void *ptr)
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);
if (state->array_delim) fbuffer_free(state->array_delim);
if (state->object_delim) fbuffer_free(state->object_delim);
if (state->object_delim2) fbuffer_free(state->object_delim2);
ruby_xfree(state);
}

Expand All @@ -432,9 +433,6 @@ static size_t State_memsize(const void *ptr)
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;
if (state->array_delim) size += FBUFFER_CAPA(state->array_delim);
if (state->object_delim) size += FBUFFER_CAPA(state->object_delim);
if (state->object_delim2) size += FBUFFER_CAPA(state->object_delim2);
return size;
}

Expand Down Expand Up @@ -644,24 +642,16 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
JSON_Generator_State *state = arg->state;
VALUE Vstate = arg->Vstate;

char *object_nl = state->object_nl;
long object_nl_len = state->object_nl_len;
char *indent = state->indent;
long indent_len = state->indent_len;
char *delim = FBUFFER_PTR(state->object_delim);
long delim_len = FBUFFER_LEN(state->object_delim);
char *delim2 = FBUFFER_PTR(state->object_delim2);
long delim2_len = FBUFFER_LEN(state->object_delim2);
long depth = state->depth;
int j;

if (arg->iter > 0) fbuffer_append(buffer, delim, delim_len);
if (object_nl) {
fbuffer_append(buffer, object_nl, object_nl_len);
if (arg->iter > 0) fbuffer_append_char(buffer, ',');
if (RB_UNLIKELY(state->object_nl)) {
fbuffer_append(buffer, state->object_nl, state->object_nl_len);
}
if (indent) {
if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) {
fbuffer_append(buffer, indent, indent_len);
fbuffer_append(buffer, state->indent, state->indent_len);
}
}

Expand All @@ -678,8 +668,10 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)
break;
}

generate_json(buffer, Vstate, state, key_to_s);
fbuffer_append(buffer, delim2, delim2_len);
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);
fbuffer_append_char(buffer, ':');
if (RB_UNLIKELY(state->space)) fbuffer_append(buffer, state->space, state->space_len);
generate_json(buffer, Vstate, state, val);

arg->iter++;
Expand All @@ -688,10 +680,6 @@ json_object_i(VALUE key, VALUE val, VALUE _arg)

static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
{
char *object_nl = state->object_nl;
long object_nl_len = state->object_nl_len;
char *indent = state->indent;
long indent_len = state->indent_len;
long max_nesting = state->max_nesting;
long depth = ++state->depth;
int j;
Expand All @@ -709,11 +697,11 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S
rb_hash_foreach(obj, json_object_i, (VALUE)&arg);

depth = --state->depth;
if (object_nl) {
fbuffer_append(buffer, object_nl, object_nl_len);
if (indent) {
if (RB_UNLIKELY(state->object_nl)) {
fbuffer_append(buffer, state->object_nl, state->object_nl_len);
if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) {
fbuffer_append(buffer, indent, indent_len);
fbuffer_append(buffer, state->indent, state->indent_len);
}
}
}
Expand All @@ -722,52 +710,51 @@ static void generate_json_object(FBuffer *buffer, VALUE Vstate, JSON_Generator_S

static void generate_json_array(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
{
char *array_nl = state->array_nl;
long array_nl_len = state->array_nl_len;
char *indent = state->indent;
long indent_len = state->indent_len;
long max_nesting = state->max_nesting;
char *delim = FBUFFER_PTR(state->array_delim);
long delim_len = FBUFFER_LEN(state->array_delim);
long depth = ++state->depth;
int i, j;
if (max_nesting != 0 && depth > max_nesting) {
rb_raise(eNestingError, "nesting of %ld is too deep", --state->depth);
}
fbuffer_append_char(buffer, '[');
if (array_nl) fbuffer_append(buffer, array_nl, array_nl_len);
if (RB_UNLIKELY(state->array_nl)) fbuffer_append(buffer, state->array_nl, state->array_nl_len);
for(i = 0; i < RARRAY_LEN(obj); i++) {
if (i > 0) fbuffer_append(buffer, delim, delim_len);
if (indent) {
if (i > 0) {
fbuffer_append_char(buffer, ',');
if (RB_UNLIKELY(state->array_nl)) fbuffer_append(buffer, state->array_nl, state->array_nl_len);
}
if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) {
fbuffer_append(buffer, indent, indent_len);
fbuffer_append(buffer, state->indent, state->indent_len);
}
}
generate_json(buffer, Vstate, state, rb_ary_entry(obj, i));
generate_json(buffer, Vstate, state, RARRAY_AREF(obj, i));
}
state->depth = --depth;
if (array_nl) {
fbuffer_append(buffer, array_nl, array_nl_len);
if (indent) {
if (RB_UNLIKELY(state->array_nl)) {
fbuffer_append(buffer, state->array_nl, state->array_nl_len);
if (RB_UNLIKELY(state->indent)) {
for (j = 0; j < depth; j++) {
fbuffer_append(buffer, indent, indent_len);
fbuffer_append(buffer, state->indent, state->indent_len);
}
}
}
fbuffer_append_char(buffer, ']');
}

static int enc_utf8_compatible_p(rb_encoding *enc)
static int usascii_encindex, utf8_encindex;

static int enc_utf8_compatible_p(int enc_idx)
{
if (enc == rb_usascii_encoding()) return 1;
if (enc == rb_utf8_encoding()) return 1;
if (enc_idx == usascii_encindex) return 1;
if (enc_idx == utf8_encindex) return 1;
return 0;
}

static void generate_json_string(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
{
fbuffer_append_char(buffer, '"');
if (!enc_utf8_compatible_p(rb_enc_get(obj))) {
if (!enc_utf8_compatible_p(RB_ENCODING_GET(obj))) {
obj = rb_str_export_to_enc(obj, rb_utf8_encoding());
}
convert_UTF8_to_JSON(buffer, obj, state->ascii_only, state->script_safe);
Expand Down Expand Up @@ -827,35 +814,56 @@ static void generate_json_float(FBuffer *buffer, VALUE Vstate, JSON_Generator_St
static void generate_json(FBuffer *buffer, VALUE Vstate, JSON_Generator_State *state, VALUE obj)
{
VALUE tmp;
VALUE klass = CLASS_OF(obj);
if (klass == rb_cHash) {
generate_json_object(buffer, Vstate, state, obj);
} else if (klass == rb_cArray) {
generate_json_array(buffer, Vstate, state, obj);
} else if (klass == rb_cString) {
generate_json_string(buffer, Vstate, state, obj);
} else if (obj == Qnil) {
if (obj == Qnil) {
generate_json_null(buffer, Vstate, state, obj);
} else if (obj == Qfalse) {
generate_json_false(buffer, Vstate, state, obj);
} else if (obj == Qtrue) {
generate_json_true(buffer, Vstate, state, obj);
} else if (FIXNUM_P(obj)) {
generate_json_fixnum(buffer, Vstate, state, obj);
} else if (RB_TYPE_P(obj, T_BIGNUM)) {
generate_json_bignum(buffer, Vstate, state, obj);
} else if (klass == rb_cFloat) {
generate_json_float(buffer, Vstate, state, obj);
} else if (state->strict) {
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(CLASS_OF(obj)));
} else if (rb_respond_to(obj, i_to_json)) {
tmp = rb_funcall(obj, i_to_json, 1, Vstate);
Check_Type(tmp, T_STRING);
fbuffer_append_str(buffer, tmp);
} else if (RB_SPECIAL_CONST_P(obj)) {
if (RB_FIXNUM_P(obj)) {
generate_json_fixnum(buffer, Vstate, state, obj);
} else if (RB_FLONUM_P(obj)) {
generate_json_float(buffer, Vstate, state, obj);
} else {
goto general;
}
} else {
tmp = rb_funcall(obj, i_to_s, 0);
Check_Type(tmp, T_STRING);
generate_json_string(buffer, Vstate, state, tmp);
VALUE klass = RBASIC_CLASS(obj);
switch (RB_BUILTIN_TYPE(obj)) {
case T_BIGNUM:
generate_json_bignum(buffer, Vstate, state, obj);
break;
case T_HASH:
if (klass != rb_cHash) goto general;
generate_json_object(buffer, Vstate, state, obj);
break;
case T_ARRAY:
if (klass != rb_cArray) goto general;
generate_json_array(buffer, Vstate, state, obj);
break;
case T_STRING:
if (klass != rb_cString) goto general;
generate_json_string(buffer, Vstate, state, obj);
break;
case T_FLOAT:
if (klass != rb_cFloat) goto general;
generate_json_float(buffer, Vstate, state, obj);
break;
default:
general:
if (state->strict) {
rb_raise(eGeneratorError, "%"PRIsVALUE" not allowed in JSON", RB_OBJ_STRING(CLASS_OF(obj)));
} else if (rb_respond_to(obj, i_to_json)) {
tmp = rb_funcall(obj, i_to_json, 1, Vstate);
Check_Type(tmp, T_STRING);
fbuffer_append_str(buffer, tmp);
} else {
tmp = rb_funcall(obj, i_to_s, 0);
Check_Type(tmp, T_STRING);
generate_json_string(buffer, Vstate, state, tmp);
}
}
}
}

Expand All @@ -865,28 +873,6 @@ static FBuffer *cState_prepare_buffer(VALUE self)
GET_STATE(self);
buffer = fbuffer_alloc(state->buffer_initial_length);

if (state->object_delim) {
fbuffer_clear(state->object_delim);
} else {
state->object_delim = fbuffer_alloc(16);
}
fbuffer_append_char(state->object_delim, ',');
if (state->object_delim2) {
fbuffer_clear(state->object_delim2);
} else {
state->object_delim2 = fbuffer_alloc(16);
}
if (state->space_before) fbuffer_append(state->object_delim2, state->space_before, state->space_before_len);
fbuffer_append_char(state->object_delim2, ':');
if (state->space) fbuffer_append(state->object_delim2, state->space, state->space_len);

if (state->array_delim) {
fbuffer_clear(state->array_delim);
} else {
state->array_delim = fbuffer_alloc(16);
}
fbuffer_append_char(state->array_delim, ',');
if (state->array_nl) fbuffer_append(state->array_delim, state->array_nl, state->array_nl_len);
return buffer;
}

Expand Down Expand Up @@ -999,9 +985,6 @@ static VALUE cState_init_copy(VALUE obj, VALUE orig)
objState->space_before = fstrndup(origState->space_before, origState->space_before_len);
objState->object_nl = fstrndup(origState->object_nl, origState->object_nl_len);
objState->array_nl = fstrndup(origState->array_nl, origState->array_nl_len);
if (origState->array_delim) objState->array_delim = fbuffer_dup(origState->array_delim);
if (origState->object_delim) objState->object_delim = fbuffer_dup(origState->object_delim);
if (origState->object_delim2) objState->object_delim2 = fbuffer_dup(origState->object_delim2);
return obj;
}

Expand Down Expand Up @@ -1498,4 +1481,7 @@ void Init_generator(void)
i_match = rb_intern("match");
i_keys = rb_intern("keys");
i_dup = rb_intern("dup");

usascii_encindex = rb_usascii_encindex();
utf8_encindex = rb_utf8_encindex();
}
3 changes: 0 additions & 3 deletions ext/json/ext/generator/generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,6 @@ typedef struct JSON_Generator_StateStruct {
long object_nl_len;
char *array_nl;
long array_nl_len;
FBuffer *array_delim;
FBuffer *object_delim;
FBuffer *object_delim2;
long max_nesting;
char allow_nan;
char ascii_only;
Expand Down