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 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 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..9df04ce0079fad 100644 --- a/ext/json/parser/parser.c +++ b/ext/json/parser/parser.c @@ -84,17 +84,55 @@ 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) +#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) { - 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; +} +#endif +#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; @@ -323,7 +361,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 +563,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 +1004,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 +1033,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)); 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 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