From fecf9666bedc3d0673104843e925171bf4736484 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 03:54:59 +0900 Subject: [PATCH 1/5] Use rb_len_t for String lengths RSTRING_LEN returns rb_len_t on Ruby master, so the variables receiving it follow. Older Rubies get a long typedef and build as before. https://bugs.ruby-lang.org/issues/22400 Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/generator/generator.c | 2 +- ext/json/ext/json.h | 4 ++++ ext/json/ext/parser/parser.c | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 380622235..4eeae7102 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -898,7 +898,7 @@ static void raw_generate_json_string(FBuffer *buffer, struct generate_json_data } fbuffer_append_char(buffer, '"'); - long len; + rb_len_t len; search_state search; search.buffer = buffer; RSTRING_GETMEM(str, search.ptr, len); diff --git a/ext/json/ext/json.h b/ext/json/ext/json.h index be69bf589..f2be3e552 100644 --- a/ext/json/ext/json.h +++ b/ext/json/ext/json.h @@ -5,6 +5,10 @@ #include "ruby/encoding.h" #include +#ifndef HAVE_RB_LEN_T +typedef long rb_len_t; +#endif + #ifndef RBIMPL_ASSERT_OR_ASSUME # define RBIMPL_ASSERT_OR_ASSUME(x) #endif diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index c912a8eec..80bc12d20 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -138,7 +138,7 @@ ALWAYS_INLINE(static) int rstring_cache_memcmp(const char *str, const char *rptr ALWAYS_INLINE(static) int rstring_cache_cmp(const char *str, const long length, VALUE rstring) { const char *rstring_ptr; - long rstring_length; + rb_len_t rstring_length; RSTRING_GETMEM(rstring, rstring_ptr, rstring_length); @@ -2044,7 +2044,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data) const char *method_name_beg = last_colon + 1; long before_len = method_name_beg - name_cstr; - long len = RSTRING_LEN(name) - before_len; + rb_len_t len = RSTRING_LEN(name) - before_len; VALUE method_name = rb_str_substr(name, before_len, len); config->decimal_method_id = SYM2ID(rb_str_intern(method_name)); } else { @@ -2137,7 +2137,7 @@ static VALUE cParser_parse(JSON_ParserConfig *config, VALUE src) .head = 1, }; - long len; + rb_len_t len; const char *start; RSTRING_GETMEM(Vsource, start, len); @@ -2456,7 +2456,7 @@ static VALUE cResumableParser_feed(VALUE self, VALUE str) rb_str_append(parser->buffer, str); } - long len; + rb_len_t len; const char *start; RSTRING_GETMEM(parser->buffer, start, len); parser->state.start = start; @@ -2762,7 +2762,7 @@ static VALUE cResumableParser_rest(VALUE self) size_t offset = parser->state.cursor - parser->state.start; const char *ptr; - long len; + rb_len_t len; RSTRING_GETMEM(parser->buffer, ptr, len); return rb_utf8_str_new(ptr + offset, len - offset); } From 65f139e318bd828770fe7a6a08b445911eb92319 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 03:55:23 +0900 Subject: [PATCH 2/5] Use rb_len_t for the column of parse errors The column is a character offset counted by rb_enc_strlen(), which returns rb_len_t. The line number stays long. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/json.h | 2 ++ ext/json/ext/parser/parser.c | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ext/json/ext/json.h b/ext/json/ext/json.h index f2be3e552..a402182d0 100644 --- a/ext/json/ext/json.h +++ b/ext/json/ext/json.h @@ -7,6 +7,8 @@ #ifndef HAVE_RB_LEN_T typedef long rb_len_t; +# define LEN2NUM LONG2NUM +# define PRIdLEN "ld" #endif #ifndef RBIMPL_ASSERT_OR_ASSUME diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 80bc12d20..d35c14955 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -580,7 +580,7 @@ static inline char peek(JSON_ParserState *state) return *state->cursor; } -static void cursor_position(JSON_ParserState *state, long *line_out, long *column_out) +static void cursor_position(JSON_ParserState *state, long *line_out, rb_len_t *column_out) { JSON_ASSERT(!state->parser); JSON_ASSERT(state->cursor); @@ -597,7 +597,7 @@ static void cursor_position(JSON_ParserState *state, long *line_out, long *colum while (cursor > state->start && cursor[-1] != '\n') { cursor--; } - long column = rb_enc_strlen(cursor, state->cursor, enc_utf8) + 1; + rb_len_t column = rb_enc_strlen(cursor, state->cursor, enc_utf8) + 1; while (cursor > state->start) { if (*--cursor == '\n') { @@ -675,11 +675,11 @@ static VALUE json_path_new(JSON_ParserState *state, VALUE duplicate_key) return path; } -static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line, long column, bool eos) +static VALUE parse_error_new(JSON_ParserState *state, VALUE message, long line, rb_len_t column, bool eos) { VALUE exc = rb_exc_new_str(eParserError, message); rb_ivar_set(exc, i_at_line, LONG2NUM(line)); - rb_ivar_set(exc, i_at_column, LONG2NUM(column)); + rb_ivar_set(exc, i_at_column, LEN2NUM(column)); rb_ivar_set(exc, i_at_json_path, json_path_new(state, Qundef)); return exc; } @@ -697,9 +697,10 @@ NORETURN(static) void raise_parse_error(const char *format, JSON_ParserState *st } } else { VALUE message = build_parse_error_message(format, state); - long line, column; + long line; + rb_len_t column; cursor_position(state, &line, &column); - rb_str_catf(message, " at line %ld column %ld", line, column); + rb_str_catf(message, " at line %ld column %"PRIdLEN, line, column); rb_exc_raise(parse_error_new(state, message, line, column, eos)); } } @@ -1231,9 +1232,10 @@ NORETURN(static) void raise_duplicate_key_error(JSON_ParserState *state, VALUE d if (state->parser) { // line and columns can't be accurate in resumable exc = parse_error_new(state, message, 0, 0, false); } else { - long line, column; + long line; + rb_len_t column; cursor_position(state, &line, &column); - rb_str_catf(message, " at line %ld column %ld", line, column); + rb_str_catf(message, " at line %ld column %"PRIdLEN, line, column); exc = parse_error_new(state, message, line, column, false); } rb_ivar_set(exc, i_at_json_path, json_path_new(state, duplicate_key)); From 23a3e0ad5e464f56787535d99c5a3688c9c408aa Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 04:03:49 +0900 Subject: [PATCH 3/5] Use rb_len_t for lengths given to the String API These lengths come from pointer differences and go to functions that take rb_len_t, so they no longer pass through long on the way. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/parser/parser.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index d35c14955..696a878a9 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -79,7 +79,7 @@ static rb_encoding *enc_utf8; #define JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH 55 -static inline VALUE build_interned_string(const char *str, const long length) +static inline VALUE build_interned_string(const char *str, const rb_len_t length) { # ifdef HAVE_RB_ENC_INTERNED_STR return rb_enc_interned_str(str, length, enc_utf8); @@ -89,7 +89,7 @@ static inline VALUE build_interned_string(const char *str, const long length) # endif } -static inline VALUE build_symbol(const char *str, const long length) +static inline VALUE build_symbol(const char *str, const rb_len_t length) { return rb_str_intern(build_interned_string(str, length)); } @@ -890,12 +890,12 @@ static inline VALUE build_string(const char *start, const char *end, bool intern VALUE result; # ifdef HAVE_RB_ENC_INTERNED_STR if (intern) { - result = rb_enc_interned_str(start, (long)(end - start), enc_utf8); + result = rb_enc_interned_str(start, (rb_len_t)(end - start), enc_utf8); } else { - result = rb_utf8_str_new(start, (long)(end - start)); + result = rb_utf8_str_new(start, (rb_len_t)(end - start)); } # else - result = rb_utf8_str_new(start, (long)(end - start)); + result = rb_utf8_str_new(start, (rb_len_t)(end - start)); if (intern) { result = rb_funcall(rb_str_freeze(result), i_uminus, 0); } @@ -1089,7 +1089,7 @@ NOINLINE(static) VALUE json_string_unescape(JSON_ParserState *state, JSON_Parser typedef VALUE (*json_number_decode_func_t)(const char *ptr); -static inline VALUE json_decode_large_number(const char *start, long len, json_number_decode_func_t func) +static inline VALUE json_decode_large_number(const char *start, rb_len_t len, json_number_decode_func_t func) { if (RB_LIKELY(len < MAX_NUMBER_STACK_BUFFER)) { char buffer[MAX_NUMBER_STACK_BUFFER]; @@ -1112,7 +1112,7 @@ static VALUE json_decode_inum(const char *buffer) return rb_cstr2inum(buffer, 10); } -NOINLINE(static) VALUE json_decode_large_integer(const char *start, long len) +NOINLINE(static) VALUE json_decode_large_integer(const char *start, rb_len_t len) { return json_decode_large_number(start, len, json_decode_inum); } @@ -1153,7 +1153,7 @@ static VALUE json_decode_dnum(const char *buffer) return DBL2NUM(rb_cstr_to_dbl(buffer, 1)); } -NOINLINE(static) VALUE json_decode_large_float(const char *start, long len) +NOINLINE(static) VALUE json_decode_large_float(const char *start, rb_len_t len) { return json_decode_large_number(start, len, json_decode_dnum); } @@ -2045,7 +2045,7 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data) parser_config_wb_write(self, &config->decimal_class, rb_path_to_class(mod_path)); const char *method_name_beg = last_colon + 1; - long before_len = method_name_beg - name_cstr; + rb_len_t before_len = method_name_beg - name_cstr; rb_len_t len = RSTRING_LEN(name) - before_len; VALUE method_name = rb_str_substr(name, before_len, len); config->decimal_method_id = SYM2ID(rb_str_intern(method_name)); From c92c5e477f14ad3cb8850cb9315f383415a46493 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Sat, 10 Oct 2026 07:09:54 +0900 Subject: [PATCH 4/5] Match rb_hash_bulk_insert fallback to Ruby's declaration Ruby declares it with an rb_len_t count, and a mismatching definition fails builds where the fallback is compiled against those headers. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/parser/parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 696a878a9..70e34edb7 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -20,9 +20,9 @@ static int utf8_encindex; #ifndef HAVE_RB_HASH_BULK_INSERT // For TruffleRuby static void -rb_hash_bulk_insert(long count, const VALUE *pairs, VALUE hash) +rb_hash_bulk_insert(rb_len_t count, const VALUE *pairs, VALUE hash) { - long index = 0; + rb_len_t index = 0; while (index < count) { VALUE name = pairs[index++]; VALUE value = pairs[index++]; From 9703ff0093c13dfcc971805231261f1e06cf6da8 Mon Sep 17 00:00:00 2001 From: Hiroshi SHIBATA Date: Mon, 12 Oct 2026 10:34:31 +0900 Subject: [PATCH 5/5] Index the generated JSON array by rb_len_t An int index overflows on an Array longer than INT_MAX, which LP64 and now mswin can hold. Co-Authored-By: Claude Opus 5.5 --- ext/json/ext/generator/generator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/json/ext/generator/generator.c b/ext/json/ext/generator/generator.c index 4eeae7102..738f453cd 100644 --- a/ext/json/ext/generator/generator.c +++ b/ext/json/ext/generator/generator.c @@ -1097,7 +1097,7 @@ static void generate_json_array(FBuffer *buffer, struct generate_json_data *data fbuffer_append_char(buffer, '['); if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl); - for (int i = 0; i < RARRAY_LEN(obj); i++) { + for (rb_len_t i = 0; i < RARRAY_LEN(obj); i++) { if (i > 0) { fbuffer_append_char(buffer, ','); if (RB_UNLIKELY(data->state->array_nl)) fbuffer_append_str(buffer, data->state->array_nl);