Skip to content

Commit 6f1d3c3

Browse files
authored
Merge pull request #885 from byroot/parse-digits-swar
Use SWAR for parsing integers on little endian machines
2 parents 11f4e7b + 6348ff0 commit 6f1d3c3

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

ext/json/ext/parser/parser.c

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,11 +1039,61 @@ static inline VALUE json_parse_string(JSON_ParserState *state, JSON_ParserConfig
10391039
return Qfalse;
10401040
}
10411041

1042+
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1043+
// From: https://lemire.me/blog/2022/01/21/swar-explained-parsing-eight-digits/
1044+
// Additional References:
1045+
// https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
1046+
// http://0x80.pl/notesen/2014-10-12-parsing-decimal-numbers-part-1-swar.html
1047+
static inline uint64_t decode_8digits_unrolled(uint64_t val) {
1048+
const uint64_t mask = 0x000000FF000000FF;
1049+
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
1050+
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
1051+
val -= 0x3030303030303030;
1052+
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
1053+
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
1054+
return val;
1055+
}
1056+
1057+
static inline uint64_t decode_4digits_unrolled(uint32_t val) {
1058+
const uint32_t mask = 0x000000FF;
1059+
const uint32_t mul1 = 100;
1060+
val -= 0x30303030;
1061+
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
1062+
val = ((val & mask) * mul1) + (((val >> 16) & mask));
1063+
return val;
1064+
}
1065+
#endif
1066+
10421067
static inline int json_parse_digits(JSON_ParserState *state, uint64_t *accumulator)
10431068
{
10441069
const char *start = state->cursor;
1045-
char next_char;
10461070

1071+
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
1072+
while (rest(state) >= 8) {
1073+
uint64_t next_8bytes;
1074+
memcpy(&next_8bytes, state->cursor, sizeof(uint64_t));
1075+
1076+
// From: https://github.com/simdjson/simdjson/blob/32b301893c13d058095a07d9868edaaa42ee07aa/include/simdjson/generic/numberparsing.h#L333
1077+
// Branchless version of: http://0x80.pl/articles/swar-digits-validate.html
1078+
uint64_t match = (next_8bytes & 0xF0F0F0F0F0F0F0F0) | (((next_8bytes + 0x0606060606060606) & 0xF0F0F0F0F0F0F0F0) >> 4);
1079+
1080+
if (match == 0x3333333333333333) { // 8 consecutive digits
1081+
*accumulator = (*accumulator * 100000000) + decode_8digits_unrolled(next_8bytes);
1082+
state->cursor += 8;
1083+
continue;
1084+
}
1085+
1086+
if ((match & 0xFFFFFFFF) == 0x33333333) { // 4 consecutive digits
1087+
*accumulator = (*accumulator * 10000) + decode_4digits_unrolled((uint32_t)next_8bytes);
1088+
state->cursor += 4;
1089+
break;
1090+
}
1091+
1092+
break;
1093+
}
1094+
#endif
1095+
1096+
char next_char;
10471097
while (rb_isdigit(next_char = peek(state))) {
10481098
*accumulator = *accumulator * 10 + (next_char - '0');
10491099
state->cursor++;

0 commit comments

Comments
 (0)