summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <[email protected]>2025-01-16 14:53:51 +0100
committerHiroshi SHIBATA <[email protected]>2025-01-20 16:09:00 +0900
commite4b54b0a3638ed89c6c0a9ca3f10b990b2bb008d (patch)
treef8573517c55d93d86224c7116e8628f97618684f
parent99e9eb5380a7a58880bef459c740e91369b8dab2 (diff)
[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
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/12598
-rw-r--r--ext/json/fbuffer/fbuffer.h11
-rw-r--r--ext/json/parser/parser.c98
2 files changed, 67 insertions, 42 deletions
diff --git a/ext/json/fbuffer/fbuffer.h b/ext/json/fbuffer/fbuffer.h
index 0774c7e464..4c42e14b6f 100644
--- a/ext/json/fbuffer/fbuffer.h
+++ b/ext/json/fbuffer/fbuffer.h
@@ -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
diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c
index e86d5c7b04..f777d7630b 100644
--- a/ext/json/parser/parser.c
+++ b/ext/json/parser/parser.c
@@ -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;
}