Skip to content

Commit 4dcbcb8

Browse files
committed
Stop using RB_ALLOCV
While it's a very nice API, if the buffer is too big for the stack (> 1024B) the buffer object will be mark like a stack region which is slow and overkill for a char buffer.
1 parent 7ddf349 commit 4dcbcb8

2 files changed

Lines changed: 37 additions & 21 deletions

File tree

ext/json/ext/parser/parser.c

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -770,16 +770,36 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser
770770
}
771771

772772
#define MAX_FAST_INTEGER_SIZE 18
773+
#define MAX_NUMBER_STACK_BUFFER 128
774+
775+
typedef VALUE (*json_number_decode_func_t)(const char *ptr);
776+
777+
static inline VALUE json_decode_large_number(const char *start, long len, json_number_decode_func_t func)
778+
{
779+
if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) {
780+
char buffer[MAX_NUMBER_STACK_BUFFER];
781+
MEMCPY(buffer, start, char, len);
782+
buffer[len] = '\0';
783+
return func(buffer);
784+
} else {
785+
VALUE buffer_v = rb_str_tmp_new(len);
786+
char *buffer = RSTRING_PTR(buffer_v);
787+
MEMCPY(buffer, start, char, len);
788+
buffer[len] = '\0';
789+
VALUE number = func(buffer);
790+
RB_GC_GUARD(buffer_v);
791+
return number;
792+
}
793+
}
794+
795+
static VALUE json_decode_inum(const char *buffer)
796+
{
797+
return rb_cstr2inum(buffer, 10);
798+
}
773799

774800
NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len)
775801
{
776-
VALUE buffer_v;
777-
char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
778-
MEMCPY(buffer, start, char, len);
779-
buffer[len] = '\0';
780-
VALUE number = rb_cstr2inum(buffer, 10);
781-
RB_ALLOCV_END(buffer_v);
782-
return number;
802+
return json_decode_large_number(start, len, json_decode_inum);
783803
}
784804

785805
static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits, bool negative, const char *start, const char *end)
@@ -794,22 +814,14 @@ static inline VALUE json_decode_integer(uint64_t mantissa, int mantissa_digits,
794814
return json_decode_large_integer(start, end - start);
795815
}
796816

797-
NOINLINE(static) VALUE json_decode_large_float(const char *start, long len)
817+
static VALUE json_decode_dnum(const char *buffer)
798818
{
799-
if (RB_LIKELY(len < 64)) {
800-
char buffer[64];
801-
MEMCPY(buffer, start, char, len);
802-
buffer[len] = '\0';
803-
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
804-
}
819+
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
820+
}
805821

806-
VALUE buffer_v;
807-
char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
808-
MEMCPY(buffer, start, char, len);
809-
buffer[len] = '\0';
810-
VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1));
811-
RB_ALLOCV_END(buffer_v);
812-
return number;
822+
NOINLINE(static) VALUE json_decode_large_float(const char *start, long len)
823+
{
824+
return json_decode_large_number(start, len, json_decode_dnum);
813825
}
814826

815827
/* Ruby JSON optimized float decoder using vendored Ryu algorithm

test/json/json_parser_test.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ def test_parse_bignum
135135
bignum = Integer('1234567890' * 10)
136136
assert_equal(bignum, JSON.parse(bignum.to_s))
137137
assert_equal(bignum.to_f, JSON.parse(bignum.to_s + ".0"))
138+
139+
bignum = Integer('1234567890' * 50)
140+
assert_equal(bignum, JSON.parse(bignum.to_s))
141+
assert_equal(bignum.to_f, JSON.parse(bignum.to_s + ".0"))
138142
end
139143

140144
def test_parse_bigdecimals

0 commit comments

Comments
 (0)