Skip to content

Commit e4b54b0

Browse files
byroothsbt
authored andcommitted
[ruby/json] Replace fbuffer by stack buffers or RB_ALLOCV in parser.c
We only use that buffer for parsing integer and floats, these are unlikely to be very big, and if so we can just use RB_ALLOCV as it will almost always end in a small `alloca`. This allow to no longer need `rb_protect` around the parser. ruby/json@994859916a
1 parent 99e9eb5 commit e4b54b0

File tree

2 files changed

+67
-42
lines changed

2 files changed

+67
-42
lines changed

ext/json/fbuffer/fbuffer.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,11 @@ typedef struct FBufferStruct {
5959
#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
6060

6161
static void fbuffer_free(FBuffer *fb);
62-
#ifndef JSON_GENERATOR
6362
static void fbuffer_clear(FBuffer *fb);
64-
#endif
6563
static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len);
66-
#ifdef JSON_GENERATOR
6764
static void fbuffer_append_long(FBuffer *fb, long number);
68-
#endif
6965
static inline void fbuffer_append_char(FBuffer *fb, char newchr);
70-
#ifdef JSON_GENERATOR
7166
static VALUE fbuffer_finalize(FBuffer *fb);
72-
#endif
7367

7468
static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
7569
{
@@ -156,7 +150,6 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
156150
}
157151
}
158152

159-
#ifdef JSON_GENERATOR
160153
static void fbuffer_append_str(FBuffer *fb, VALUE str)
161154
{
162155
const char *newstr = StringValuePtr(str);
@@ -166,7 +159,6 @@ static void fbuffer_append_str(FBuffer *fb, VALUE str)
166159

167160
fbuffer_append(fb, newstr, len);
168161
}
169-
#endif
170162

171163
static inline void fbuffer_append_char(FBuffer *fb, char newchr)
172164
{
@@ -175,7 +167,6 @@ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
175167
fb->len++;
176168
}
177169

178-
#ifdef JSON_GENERATOR
179170
static long fltoa(long number, char *buf)
180171
{
181172
static const char digits[] = "0123456789";
@@ -210,5 +201,5 @@ static VALUE fbuffer_finalize(FBuffer *fb)
210201
return result;
211202
}
212203
}
213-
#endif
204+
214205
#endif

ext/json/parser/parser.c

Lines changed: 66 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,32 @@
11
#include "ruby.h"
2-
#include "../fbuffer/fbuffer.h"
2+
#include "ruby/encoding.h"
3+
4+
/* shims */
5+
/* This is the fallback definition from Ruby 3.4 */
6+
7+
#ifndef RBIMPL_STDBOOL_H
8+
#if defined(__cplusplus)
9+
# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
10+
# include <cstdbool>
11+
# endif
12+
#elif defined(HAVE_STDBOOL_H)
13+
# include <stdbool.h>
14+
#elif !defined(HAVE__BOOL)
15+
typedef unsigned char _Bool;
16+
# define bool _Bool
17+
# define true ((_Bool)+1)
18+
# define false ((_Bool)+0)
19+
# define __bool_true_false_are_defined
20+
#endif
21+
#endif
22+
23+
#ifndef RB_UNLIKELY
24+
#define RB_UNLIKELY(expr) expr
25+
#endif
26+
27+
#ifndef RB_LIKELY
28+
#define RB_LIKELY(expr) expr
29+
#endif
330

431
static VALUE mJSON, eNestingError, Encoding_UTF_8;
532
static VALUE CNaN, CInfinity, CMinusInfinity;
@@ -401,7 +428,6 @@ typedef struct JSON_ParserStateStruct {
401428
VALUE stack_handle;
402429
const char *cursor;
403430
const char *end;
404-
FBuffer fbuffer;
405431
rvalue_stack *stack;
406432
rvalue_cache name_cache;
407433
int in_array;
@@ -690,26 +716,44 @@ static inline VALUE fast_decode_integer(const char *p, const char *pe)
690716
return LL2NUM(memo);
691717
}
692718

693-
static VALUE
719+
static VALUE json_decode_large_integer(const char *start, long len)
720+
{
721+
VALUE buffer_v;
722+
char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
723+
MEMCPY(buffer, start, char, len);
724+
buffer[len] = '\0';
725+
VALUE number = rb_cstr2inum(buffer, 10);
726+
RB_ALLOCV_END(buffer_v);
727+
return number;
728+
}
729+
730+
static inline VALUE
694731
json_decode_integer(JSON_ParserState *state, const char *start, const char *end)
695732
{
696733
long len = end - start;
697734
if (RB_LIKELY(len < MAX_FAST_INTEGER_SIZE)) {
698735
return fast_decode_integer(start, end);
699736
}
700-
701-
fbuffer_clear(&state->fbuffer);
702-
fbuffer_append(&state->fbuffer, start, len);
703-
fbuffer_append_char(&state->fbuffer, '\0');
704-
return rb_cstr2inum(FBUFFER_PTR(&state->fbuffer), 10);
737+
return json_decode_large_integer(start, len);
705738
}
706739

740+
static VALUE json_decode_large_float(const char *start, long len)
741+
{
742+
VALUE buffer_v;
743+
char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
744+
MEMCPY(buffer, start, char, len);
745+
buffer[len] = '\0';
746+
VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1));
747+
RB_ALLOCV_END(buffer_v);
748+
return number;
749+
}
750+
707751
static VALUE json_decode_float(JSON_ParserState *state, const char *start, const char *end)
708752
{
709753
VALUE mod = Qnil;
710754
ID method_id = 0;
711755
JSON_ParserConfig *config = state->config;
712-
if (config->decimal_class) {
756+
if (RB_UNLIKELY(config->decimal_class)) {
713757
// TODO: we should move this to the constructor
714758
if (rb_respond_to(config->decimal_class, i_try_convert)) {
715759
mod = config->decimal_class;
@@ -739,15 +783,17 @@ static VALUE json_decode_float(JSON_ParserState *state, const char *start, const
739783
}
740784

741785
long len = end - start;
742-
fbuffer_clear(&state->fbuffer);
743-
fbuffer_append(&state->fbuffer, start, len);
744-
fbuffer_append_char(&state->fbuffer, '\0');
745786

746-
if (method_id) {
747-
VALUE text = rb_str_new2(FBUFFER_PTR(&state->fbuffer));
787+
if (RB_UNLIKELY(method_id)) {
788+
VALUE text = rb_str_new(start, len);
748789
return rb_funcallv(mod, method_id, 1, &text);
790+
} else if (RB_LIKELY(len < 64)) {
791+
char buffer[64];
792+
MEMCPY(buffer, start, char, len);
793+
buffer[len] = '\0';
794+
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
749795
} else {
750-
return DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(&state->fbuffer), 1));
796+
return json_decode_large_float(start, len);
751797
}
752798
}
753799

@@ -1283,14 +1329,6 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
12831329
return self;
12841330
}
12851331

1286-
static VALUE cParser_parse_safe(VALUE vstate)
1287-
{
1288-
JSON_ParserState *state = (JSON_ParserState *)vstate;
1289-
VALUE result = json_parse_any(state);
1290-
json_ensure_eof(state);
1291-
return result;
1292-
}
1293-
12941332
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
12951333
{
12961334
Vsource = convert_encoding(StringValue(Vsource));
@@ -1311,17 +1349,13 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
13111349
};
13121350
JSON_ParserState *state = &_state;
13131351

1314-
char stack_buffer[FBUFFER_STACK_SIZE];
1315-
fbuffer_stack_init(&state->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE);
1316-
1317-
int interupted;
1318-
VALUE result = rb_protect(cParser_parse_safe, (VALUE)state, &interupted);
1352+
VALUE result = json_parse_any(state);
13191353

1354+
// This may be skipped in case of exception, but
1355+
// it won't cause a leak.
13201356
rvalue_stack_eagerly_release(state->stack_handle);
1321-
fbuffer_free(&state->fbuffer);
1322-
if (interupted) {
1323-
rb_jump_tag(interupted);
1324-
}
1357+
1358+
json_ensure_eof(state);
13251359

13261360
return result;
13271361
}

0 commit comments

Comments
 (0)