@@ -106,17 +106,53 @@ static void rvalue_cache_insert_at(rvalue_cache *cache, int index, VALUE rstring
106106 cache -> entries [index ] = rstring ;
107107}
108108
109- static inline int rstring_cache_cmp (const char * str , const long length , VALUE rstring )
109+ static inline FORCE_INLINE int rstring_cache_cmp (const char * str , const long length , VALUE rstring )
110110{
111+
112+
113+ #if defined(__BYTE_ORDER__ ) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ) && defined(__has_builtin ) && __has_builtin (__builtin_bswap64 )
114+ const char * rptr ;
115+ long rstring_length ;
116+
117+ RSTRING_GETMEM (rstring , rptr , rstring_length );
118+
119+ if (length != rstring_length ) {
120+ return (int )(length - rstring_length );
121+ }
122+
123+ long i = 0 ;
124+
125+ for (; i + 8 <= length ; i += 8 ) {
126+ uint64_t a , b ;
127+ memcpy (& a , str + i , 8 );
128+ memcpy (& b , rptr + i , 8 );
129+ if (a != b ) {
130+ a = __builtin_bswap64 (a );
131+ b = __builtin_bswap64 (b );
132+ return (a < b ) ? -1 : 1 ;
133+ }
134+ }
135+
136+ for (; i < length ; i ++ ) {
137+ unsigned char ca = (unsigned char )str [i ];
138+ unsigned char cb = (unsigned char )rptr [i ];
139+ if (ca != cb ) {
140+ return (ca < cb ) ? -1 : 1 ;
141+ }
142+ }
143+
144+ return 0 ;
145+ #else
111146 long rstring_length = RSTRING_LEN (rstring );
112147 if (length == rstring_length ) {
113148 return memcmp (str , RSTRING_PTR (rstring ), length );
114149 } else {
115150 return (int )(length - rstring_length );
116151 }
152+ #endif
117153}
118154
119- static VALUE rstring_cache_fetch (rvalue_cache * cache , const char * str , const long length )
155+ static inline VALUE rstring_cache_fetch (rvalue_cache * cache , const char * str , const long length )
120156{
121157 if (RB_UNLIKELY (length > JSON_RVALUE_CACHE_MAX_ENTRY_LENGTH )) {
122158 // Common names aren't likely to be very long. So we just don't
@@ -136,24 +172,50 @@ static VALUE rstring_cache_fetch(rvalue_cache *cache, const char *str, const lon
136172 int mid = 0 ;
137173 int last_cmp = 0 ;
138174
175+ // while (low <= high) {
176+ // mid = (high + low) >> 1;
177+ // VALUE entry = cache->entries[mid];
178+ // last_cmp = rstring_cache_cmp(str, length, entry);
179+
180+ // if (last_cmp == 0) {
181+ // return entry;
182+ // } else if (last_cmp > 0) {
183+ // low = mid + 1;
184+ // } else {
185+ // high = mid - 1;
186+ // }
187+ // }
188+
189+ // while (low <= high) {
190+ // mid = (high + low) >> 1;
191+ // VALUE entry = cache->entries[mid];
192+ // last_cmp = rstring_cache_cmp(str, length, entry);
193+
194+ // // Branchless update of low and high
195+ // int is_greater = last_cmp > 0;
196+ // int is_less = last_cmp < 0;
197+
198+ // low = is_greater ? (mid + 1) : low;
199+ // high = is_less ? (mid - 1) : high;
200+
201+ // if (last_cmp == 0) {
202+ // return entry;
203+ // }
204+ // }
205+
139206 while (low <= high ) {
140207 mid = (high + low ) >> 1 ;
141208 VALUE entry = cache -> entries [mid ];
142209 last_cmp = rstring_cache_cmp (str , length , entry );
143-
144- if (last_cmp == 0 ) {
145- return entry ;
146- } else if (last_cmp > 0 ) {
147- low = mid + 1 ;
148- } else {
149- high = mid - 1 ;
150- }
151- }
152-
153- if (RB_UNLIKELY (memchr (str , '\\' , length ))) {
154- // We assume the overwhelming majority of names don't need to be escaped.
155- // But if they do, we have to fallback to the slow path.
156- return Qfalse ;
210+
211+ if (last_cmp == 0 ) return entry ;
212+
213+ // Branchless: uses arithmetic/masking instead of branches
214+ int is_greater = (last_cmp > 0 ); // 1 if true, 0 if false
215+ int is_less = (last_cmp < 0 ); // 1 if true, 0 if false
216+
217+ low = low + is_greater * (mid + 1 - low );
218+ high = high + is_less * (mid - 1 - high );
157219 }
158220
159221 VALUE rstring = build_interned_string (str , length );
@@ -202,12 +264,6 @@ static VALUE rsymbol_cache_fetch(rvalue_cache *cache, const char *str, const lon
202264 }
203265 }
204266
205- if (RB_UNLIKELY (memchr (str , '\\' , length ))) {
206- // We assume the overwhelming majority of names don't need to be escaped.
207- // But if they do, we have to fallback to the slow path.
208- return Qfalse ;
209- }
210-
211267 VALUE rsymbol = build_symbol (str , length );
212268
213269 if (cache -> length < JSON_RVALUE_CACHE_CAPA ) {
@@ -635,7 +691,7 @@ static inline VALUE json_string_fastpath(JSON_ParserState *state, const char *st
635691 return build_string (string , stringEnd , intern , symbolize );
636692}
637693
638- static VALUE json_string_unescape (JSON_ParserState * state , const char * string , const char * stringEnd , bool is_name , bool intern , bool symbolize )
694+ static inline VALUE json_string_unescape (JSON_ParserState * state , const char * string , const char * stringEnd , bool is_name , bool intern , bool symbolize )
639695{
640696 size_t bufferSize = stringEnd - string ;
641697 const char * p = string , * pe = string , * unescape , * bufferStart ;
@@ -908,7 +964,7 @@ static inline VALUE json_decode_object(JSON_ParserState *state, JSON_ParserConfi
908964 return object ;
909965}
910966
911- static inline VALUE json_decode_string (JSON_ParserState * state , JSON_ParserConfig * config , const char * start , const char * end , bool escaped , bool is_name )
967+ static inline FORCE_INLINE VALUE json_decode_string (JSON_ParserState * state , JSON_ParserConfig * config , const char * start , const char * end , bool escaped , bool is_name )
912968{
913969 VALUE string ;
914970 bool intern = is_name || config -> freeze ;
0 commit comments