From 83c2e3b92e25b766615600913cdca8174128f262 Mon Sep 17 00:00:00 2001 From: Sorah Fukumori Date: Tue, 4 Nov 2025 15:52:23 +0900 Subject: [PATCH 1/6] [ruby/uri] v1.1.1 https://github.com/ruby/uri/commit/f1b05c89ab --- lib/uri/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/uri/version.rb b/lib/uri/version.rb index 03d6080fd33b0f..1f810602eb3faf 100644 --- a/lib/uri/version.rb +++ b/lib/uri/version.rb @@ -1,6 +1,6 @@ module URI # :stopdoc: - VERSION = '1.1.0'.freeze + VERSION = '1.1.1'.freeze VERSION_CODE = VERSION.split('.').map{|s| s.rjust(2, '0')}.join.freeze # :startdoc: end From 55257b50383bba389dbcfbc7379541e8093da0c7 Mon Sep 17 00:00:00 2001 From: git Date: Tue, 4 Nov 2025 06:54:54 +0000 Subject: [PATCH 2/6] Update default gems list at 83c2e3b92e25b766615600913cdca8 [ci skip] --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index ab9c0c32b1f958..8917a7d753ac56 100644 --- a/NEWS.md +++ b/NEWS.md @@ -203,7 +203,7 @@ The following default gems are updated. * stringio 3.1.8.dev * strscan 3.1.6.dev * timeout 0.4.4 -* uri 1.1.0 +* uri 1.1.1 * weakref 0.1.4 * zlib 3.2.2 From 157ae44b1e5f44d04e3dcb5cad4653e0f56e889d Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 4 Nov 2025 09:03:35 +0100 Subject: [PATCH 3/6] [ruby/json] Extract `JSON_CPU_LITTLE_ENDIAN_64BITS` definition Only apply these definitions on 64 bits archs, as it's unclear if they have performance benefits or compatibility issues on 32bit archs. https://github.com/ruby/json/commit/ddad00b746 --- ext/json/json.h | 7 +++++++ ext/json/parser/parser.c | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ext/json/json.h b/ext/json/json.h index 20e7101de7b0da..01fe0cd034e7e3 100644 --- a/ext/json/json.h +++ b/ext/json/json.h @@ -3,6 +3,7 @@ #include "ruby.h" #include "ruby/encoding.h" +#include #if defined(RUBY_DEBUG) && RUBY_DEBUG # define JSON_ASSERT RUBY_ASSERT @@ -82,4 +83,10 @@ typedef unsigned char _Bool; #endif #endif +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && INTPTR_MAX == INT64_MAX +#define JSON_CPU_LITTLE_ENDIAN_64BITS 1 +#else +#define JSON_CPU_LITTLE_ENDIAN_64BITS 0 +#endif + #endif // _JSON_H_ diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 20754249e2ca39..0f4d31097bf8bb 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -323,7 +323,7 @@ typedef struct JSON_ParserStateStruct { int current_nesting; } JSON_ParserState; -static inline ssize_t rest(JSON_ParserState *state) { +static inline size_t rest(JSON_ParserState *state) { return state->end - state->cursor; } @@ -525,7 +525,7 @@ json_eat_whitespace(JSON_ParserState *state) state->cursor++; // Heuristic: if we see a newline, there is likely consecutive spaces after it. -#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#if JSON_CPU_LITTLE_ENDIAN_64BITS while (rest(state) > 8) { uint64_t chunk; memcpy(&chunk, state->cursor, sizeof(uint64_t)); @@ -966,7 +966,7 @@ static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig return Qfalse; } -#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#if JSON_CPU_LITTLE_ENDIAN_64BITS // From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/ // Additional References: // https://johnnylee-sde.github.io/Fast-numeric-string-to-int/ @@ -995,8 +995,8 @@ static inline int json_parse_digits(JSON_ParserState *state, uint64_t *accumulat { const char *start = state->cursor; -#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - while (rest(state) >= 8) { +#if JSON_CPU_LITTLE_ENDIAN_64BITS + while (rest(state) >= sizeof(uint64_t)) { uint64_t next_8bytes; memcpy(&next_8bytes, state->cursor, sizeof(uint64_t)); From 7c924013630699321d51438f3754b291d8d4a37e Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 4 Nov 2025 09:08:05 +0100 Subject: [PATCH 4/6] [ruby/json] Micro-optimize `rstring_cache_fetch` Closes: https://github.com/ruby/json/pull/888 - Mark it as `inline`. - Use `RSTRING_GETMEM`, instead of `RSTRING_LEN` and `RSTRING_PTR`. - Use an inlinable version of `memcmp`. ``` == Parsing activitypub.json (58160 bytes) ruby 3.4.6 (2025-09-16 revision https://github.com/ruby/json/commit/dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 11766.6 i/s after: 12272.1 i/s - 1.04x faster == Parsing twitter.json (567916 bytes) ruby 3.4.6 (2025-09-16 revision https://github.com/ruby/json/commit/dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 1333.2 i/s after: 1422.0 i/s - 1.07x faster == Parsing citm_catalog.json (1727030 bytes) ruby 3.4.6 (2025-09-16 revision https://github.com/ruby/json/commit/dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 656.3 i/s after: 673.1 i/s - 1.03x faster == Parsing float parsing (2251051 bytes) ruby 3.4.6 (2025-09-16 revision https://github.com/ruby/json/commit/dbd83256b1) +YJIT +PRISM [arm64-darwin24] Comparison: before: 276.8 i/s after: 276.4 i/s - same-ish: difference falls within error ``` https://github.com/ruby/json/commit/a67d1a1af4 Co-Authored-By: Scott Myron --- ext/json/parser/parser.c | 43 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 0f4d31097bf8bb..3d88a399e746af 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -84,17 +84,52 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring cache->entries[index] = rstring; } -static inline int rstring_cache_cmp(const char *str, const long length, VALUE rstring) +#if JSON_CPU_LITTLE_ENDIAN_64BITS && defined(__has_builtin) && __has_builtin(__builtin_bswap64) +static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rptr, const long length) { - long rstring_length = RSTRING_LEN(rstring); + // The libc memcmp has numerous complex optimizations, but in this particular case, + // we know the string is small (JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH), so being able to + // inline a simpler memcmp outperforms calling the libc version. + long i = 0; + + for (; i + 8 <= length; i += 8) { + uint64_t a, b; + memcpy(&a, str + i, 8); + memcpy(&b, rptr + i, 8); + if (a != b) { + a = __builtin_bswap64(a); + b = __builtin_bswap64(b); + return (a < b) ? -1 : 1; + } + } + + for (; i < length; i++) { + if (str[i] != rptr[i]) { + return (str[i] < rptr[i]) ? -1 : 1; + } + } + + return 0; +} +#else +#define rstring_cache_memcmp memcmp +#endif + +static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length, VALUE rstring) +{ + const char *rstring_ptr; + long rstring_length; + + RSTRING_GETMEM(rstring, rstring_ptr, rstring_length); + if (length == rstring_length) { - return memcmp(str, RSTRING_PTR(rstring), length); + return rstring_cache_memcmp(str, rstring_ptr, length); } else { return (int)(length - rstring_length); } } -static VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) +static ALWAYS_INLINE() VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const long length) { int low = 0; int high = cache->length - 1; From f1776e8f176f50bd9ab16ca399d1a9c8b7d6eeb7 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Tue, 4 Nov 2025 12:06:46 +0100 Subject: [PATCH 5/6] [ruby/json] Tentative fix for RHEL8 compiler ``` parser.c:87:77: error: missing binary operator before token "(" #if JSON_CPU_LITTLE_ENDIAN_64BITS && defined(__has_builtin) && __has_builtin(__builtin_bswap64) ``` https://github.com/ruby/json/commit/fce1c7e84a --- ext/json/parser/parser.c | 9 ++++++--- ext/json/simd/simd.h | 4 ++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/ext/json/parser/parser.c b/ext/json/parser/parser.c index 3d88a399e746af..9df04ce0079fad 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -84,7 +84,11 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring cache->entries[index] = rstring; } -#if JSON_CPU_LITTLE_ENDIAN_64BITS && defined(__has_builtin) && __has_builtin(__builtin_bswap64) +#define rstring_cache_memcmp memcmp + +#if JSON_CPU_LITTLE_ENDIAN_64BITS +#if __has_builtin(__builtin_bswap64) +#undef rstring_cache_memcmp static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rptr, const long length) { // The libc memcmp has numerous complex optimizations, but in this particular case, @@ -111,8 +115,7 @@ static ALWAYS_INLINE() int rstring_cache_memcmp(const char *str, const char *rpt return 0; } -#else -#define rstring_cache_memcmp memcmp +#endif #endif static ALWAYS_INLINE() int rstring_cache_cmp(const char *str, const long length, VALUE rstring) diff --git a/ext/json/simd/simd.h b/ext/json/simd/simd.h index a4f917fd0ae518..c9e3b3ec7c9f28 100644 --- a/ext/json/simd/simd.h +++ b/ext/json/simd/simd.h @@ -6,6 +6,10 @@ typedef enum { SIMD_SSE2 } SIMD_Implementation; +#ifndef __has_builtin // Optional of course. + #define __has_builtin(x) 0 // Compatibility with non-clang compilers. +#endif + #ifdef __clang__ # if __has_builtin(__builtin_ctzll) # define HAVE_BUILTIN_CTZLL 1 From cdcb490d2bceb4ff8d6fce349047a2b722e6ae87 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Tue, 4 Nov 2025 19:18:07 +0900 Subject: [PATCH 6/6] Tweak ENC_TRANS_SO_D It corresponds to TRANSSODIR, that contains `$(arch)`, so should contain it as well. --- enc/Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enc/Makefile.in b/enc/Makefile.in index ce93fdd22d2350..17888f8f9b5e10 100644 --- a/enc/Makefile.in +++ b/enc/Makefile.in @@ -24,8 +24,8 @@ OBJEXT = @OBJEXT@ LIBEXT = @LIBEXT@ EXEEXT = @EXEEXT@ TIMESTAMPDIR = $(EXTOUT)/.timestamp -ENC_TRANS_D = $(TIMESTAMPDIR)/.enc-trans.time -ENC_TRANS_SO_D = $(TIMESTAMPDIR)/.enc-trans.so.time +ENC_TRANS_D = $(TIMESTAMPDIR)/enc-trans.time +ENC_TRANS_SO_D = $(TIMESTAMPDIR)/enc-trans-$(arch).time BUILTIN_ENCS = enc/ascii.c enc/us_ascii.c\ enc/unicode.c enc/utf_8.c