[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. https://github.com/ruby/json/commit/994859916a
This commit is contained in:
parent
99e9eb5380
commit
e4b54b0a36
Notes:
git
2025-01-20 07:09:22 +00:00
@ -59,17 +59,11 @@ typedef struct FBufferStruct {
|
||||
#define FBUFFER_PAIR(fb) FBUFFER_PTR(fb), FBUFFER_LEN(fb)
|
||||
|
||||
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 inline void fbuffer_append_char(FBuffer *fb, char newchr);
|
||||
#ifdef JSON_GENERATOR
|
||||
static VALUE fbuffer_finalize(FBuffer *fb);
|
||||
#endif
|
||||
|
||||
static void fbuffer_stack_init(FBuffer *fb, unsigned long initial_length, char *stack_buffer, long stack_buffer_size)
|
||||
{
|
||||
@ -156,7 +150,6 @@ static void fbuffer_append(FBuffer *fb, const char *newstr, unsigned long len)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JSON_GENERATOR
|
||||
static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
||||
{
|
||||
const char *newstr = StringValuePtr(str);
|
||||
@ -166,7 +159,6 @@ static void fbuffer_append_str(FBuffer *fb, VALUE str)
|
||||
|
||||
fbuffer_append(fb, newstr, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
||||
{
|
||||
@ -175,7 +167,6 @@ static inline void fbuffer_append_char(FBuffer *fb, char newchr)
|
||||
fb->len++;
|
||||
}
|
||||
|
||||
#ifdef JSON_GENERATOR
|
||||
static long fltoa(long number, char *buf)
|
||||
{
|
||||
static const char digits[] = "0123456789";
|
||||
@ -210,5 +201,5 @@ static VALUE fbuffer_finalize(FBuffer *fb)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,32 @@
|
||||
#include "ruby.h"
|
||||
#include "../fbuffer/fbuffer.h"
|
||||
#include "ruby/encoding.h"
|
||||
|
||||
/* shims */
|
||||
/* This is the fallback definition from Ruby 3.4 */
|
||||
|
||||
#ifndef RBIMPL_STDBOOL_H
|
||||
#if defined(__cplusplus)
|
||||
# if defined(HAVE_STDBOOL_H) && (__cplusplus >= 201103L)
|
||||
# include <cstdbool>
|
||||
# endif
|
||||
#elif defined(HAVE_STDBOOL_H)
|
||||
# include <stdbool.h>
|
||||
#elif !defined(HAVE__BOOL)
|
||||
typedef unsigned char _Bool;
|
||||
# define bool _Bool
|
||||
# define true ((_Bool)+1)
|
||||
# define false ((_Bool)+0)
|
||||
# define __bool_true_false_are_defined
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef RB_UNLIKELY
|
||||
#define RB_UNLIKELY(expr) expr
|
||||
#endif
|
||||
|
||||
#ifndef RB_LIKELY
|
||||
#define RB_LIKELY(expr) expr
|
||||
#endif
|
||||
|
||||
static VALUE mJSON, eNestingError, Encoding_UTF_8;
|
||||
static VALUE CNaN, CInfinity, CMinusInfinity;
|
||||
@ -401,7 +428,6 @@ typedef struct JSON_ParserStateStruct {
|
||||
VALUE stack_handle;
|
||||
const char *cursor;
|
||||
const char *end;
|
||||
FBuffer fbuffer;
|
||||
rvalue_stack *stack;
|
||||
rvalue_cache name_cache;
|
||||
int in_array;
|
||||
@ -690,26 +716,44 @@ static inline VALUE fast_decode_integer(const char *p, const char *pe)
|
||||
return LL2NUM(memo);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
static VALUE json_decode_large_integer(const char *start, long len)
|
||||
{
|
||||
VALUE buffer_v;
|
||||
char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
|
||||
MEMCPY(buffer, start, char, len);
|
||||
buffer[len] = '\0';
|
||||
VALUE number = rb_cstr2inum(buffer, 10);
|
||||
RB_ALLOCV_END(buffer_v);
|
||||
return number;
|
||||
}
|
||||
|
||||
static inline VALUE
|
||||
json_decode_integer(JSON_ParserState *state, const char *start, const char *end)
|
||||
{
|
||||
long len = end - start;
|
||||
if (RB_LIKELY(len < MAX_FAST_INTEGER_SIZE)) {
|
||||
return fast_decode_integer(start, end);
|
||||
}
|
||||
|
||||
fbuffer_clear(&state->fbuffer);
|
||||
fbuffer_append(&state->fbuffer, start, len);
|
||||
fbuffer_append_char(&state->fbuffer, '\0');
|
||||
return rb_cstr2inum(FBUFFER_PTR(&state->fbuffer), 10);
|
||||
return json_decode_large_integer(start, len);
|
||||
}
|
||||
|
||||
static VALUE json_decode_large_float(const char *start, long len)
|
||||
{
|
||||
VALUE buffer_v;
|
||||
char *buffer = RB_ALLOCV_N(char, buffer_v, len + 1);
|
||||
MEMCPY(buffer, start, char, len);
|
||||
buffer[len] = '\0';
|
||||
VALUE number = DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
||||
RB_ALLOCV_END(buffer_v);
|
||||
return number;
|
||||
}
|
||||
|
||||
static VALUE json_decode_float(JSON_ParserState *state, const char *start, const char *end)
|
||||
{
|
||||
VALUE mod = Qnil;
|
||||
ID method_id = 0;
|
||||
JSON_ParserConfig *config = state->config;
|
||||
if (config->decimal_class) {
|
||||
if (RB_UNLIKELY(config->decimal_class)) {
|
||||
// TODO: we should move this to the constructor
|
||||
if (rb_respond_to(config->decimal_class, i_try_convert)) {
|
||||
mod = config->decimal_class;
|
||||
@ -739,15 +783,17 @@ static VALUE json_decode_float(JSON_ParserState *state, const char *start, const
|
||||
}
|
||||
|
||||
long len = end - start;
|
||||
fbuffer_clear(&state->fbuffer);
|
||||
fbuffer_append(&state->fbuffer, start, len);
|
||||
fbuffer_append_char(&state->fbuffer, '\0');
|
||||
|
||||
if (method_id) {
|
||||
VALUE text = rb_str_new2(FBUFFER_PTR(&state->fbuffer));
|
||||
if (RB_UNLIKELY(method_id)) {
|
||||
VALUE text = rb_str_new(start, len);
|
||||
return rb_funcallv(mod, method_id, 1, &text);
|
||||
} else if (RB_LIKELY(len < 64)) {
|
||||
char buffer[64];
|
||||
MEMCPY(buffer, start, char, len);
|
||||
buffer[len] = '\0';
|
||||
return DBL2NUM(rb_cstr_to_dbl(buffer, 1));
|
||||
} else {
|
||||
return DBL2NUM(rb_cstr_to_dbl(FBUFFER_PTR(&state->fbuffer), 1));
|
||||
return json_decode_large_float(start, len);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1283,14 +1329,6 @@ static VALUE cParserConfig_initialize(VALUE self, VALUE opts)
|
||||
return self;
|
||||
}
|
||||
|
||||
static VALUE cParser_parse_safe(VALUE vstate)
|
||||
{
|
||||
JSON_ParserState *state = (JSON_ParserState *)vstate;
|
||||
VALUE result = json_parse_any(state);
|
||||
json_ensure_eof(state);
|
||||
return result;
|
||||
}
|
||||
|
||||
static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
||||
{
|
||||
Vsource = convert_encoding(StringValue(Vsource));
|
||||
@ -1311,17 +1349,13 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE Vsource)
|
||||
};
|
||||
JSON_ParserState *state = &_state;
|
||||
|
||||
char stack_buffer[FBUFFER_STACK_SIZE];
|
||||
fbuffer_stack_init(&state->fbuffer, FBUFFER_INITIAL_LENGTH_DEFAULT, stack_buffer, FBUFFER_STACK_SIZE);
|
||||
|
||||
int interupted;
|
||||
VALUE result = rb_protect(cParser_parse_safe, (VALUE)state, &interupted);
|
||||
VALUE result = json_parse_any(state);
|
||||
|
||||
// This may be skipped in case of exception, but
|
||||
// it won't cause a leak.
|
||||
rvalue_stack_eagerly_release(state->stack_handle);
|
||||
fbuffer_free(&state->fbuffer);
|
||||
if (interupted) {
|
||||
rb_jump_tag(interupted);
|
||||
}
|
||||
|
||||
json_ensure_eof(state);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user