@@ -175,7 +175,7 @@ The code in isValidCodePoint() is derived from the ICU code in
175
175
#define JK_CACHE_SLOTS (1UL << JK_CACHE_SLOTS_BITS)
176
176
// JK_CACHE_PROBES is the number of probe attempts.
177
177
#define JK_CACHE_PROBES (4UL )
178
- // JK_INIT_CACHE_AGE must be (1 << AGE) - 1
178
+ // JK_INIT_CACHE_AGE must be < (1 << AGE) - 1, where AGE is sizeof(typeof(AGE)) * 8.
179
179
#define JK_INIT_CACHE_AGE (0 )
180
180
181
181
// JK_TOKENBUFFER_SIZE is the default stack size for the temporary buffer used to hold "non-simple" strings (i.e., contains \ escapes)
@@ -609,7 +609,7 @@ - (void)releaseState;
609
609
610
610
JK_STATIC_INLINE size_t jk_min (size_t a, size_t b);
611
611
JK_STATIC_INLINE size_t jk_max (size_t a, size_t b);
612
- JK_STATIC_INLINE JKHash calculateHash (JKHash currentHash, unsigned char c);
612
+ JK_STATIC_INLINE JKHash jk_calculateHash (JKHash currentHash, unsigned char c);
613
613
614
614
// JSONKit v1.4 used both a JKArray : NSArray and JKMutableArray : NSMutableArray, and the same for the dictionary collection type.
615
615
// However, Louis Gerbarg (via cocoa-dev) pointed out that Cocoa / Core Foundation actually implements only a single class that inherits from the
@@ -1111,7 +1111,8 @@ - (id)mutableCopyWithZone:(NSZone *)zone
1111
1111
JK_STATIC_INLINE size_t jk_min (size_t a, size_t b) { return ((a < b) ? a : b); }
1112
1112
JK_STATIC_INLINE size_t jk_max (size_t a, size_t b) { return ((a > b) ? a : b); }
1113
1113
1114
- JK_STATIC_INLINE JKHash calculateHash (JKHash currentHash, unsigned char c) { return (((currentHash << 5 ) + currentHash) + c); }
1114
+ JK_STATIC_INLINE JKHash jk_calculateHash (JKHash currentHash, unsigned char c) { return ((((currentHash << 5 ) + currentHash) + (c - 29 )) ^ (currentHash >> 19 )); }
1115
+
1115
1116
1116
1117
static void jk_error (JKParseState *parseState, NSString *format, ...) {
1117
1118
NSCParameterAssert ((parseState != NULL ) && (format != NULL ));
@@ -1408,7 +1409,7 @@ JK_STATIC_INLINE int jk_string_add_unicodeCodePoint(JKParseState *parseState, ui
1408
1409
if ((result = ConvertUTF32toUTF8 (unicodeCodePoint, &u8s, (parseState->token .tokenBuffer .bytes .ptr + parseState->token .tokenBuffer .bytes .length ))) != conversionOK) { if (result == targetExhausted) { return (1 ); } }
1409
1410
size_t utf8len = u8s - &parseState->token .tokenBuffer .bytes .ptr [*tokenBufferIdx], nextIdx = (*tokenBufferIdx) + utf8len;
1410
1411
1411
- while (*tokenBufferIdx < nextIdx) { *stringHash = calculateHash (*stringHash, parseState->token .tokenBuffer .bytes .ptr [(*tokenBufferIdx)++]); }
1412
+ while (*tokenBufferIdx < nextIdx) { *stringHash = jk_calculateHash (*stringHash, parseState->token .tokenBuffer .bytes .ptr [(*tokenBufferIdx)++]); }
1412
1413
1413
1414
return (0 );
1414
1415
}
@@ -1442,8 +1443,8 @@ static int jk_parse_string(JKParseState *parseState) {
1442
1443
ConversionResult result;
1443
1444
1444
1445
if (JK_EXPECT_F ((result = ConvertSingleCodePointInUTF8 (atStringCharacter - 1 , endOfBuffer, (UTF8 const **)&nextValidCharacter, &u32ch)) != conversionOK)) { goto switchToSlowPath; }
1445
- stringHash = calculateHash (stringHash, currentChar);
1446
- while (atStringCharacter < nextValidCharacter) { NSCParameterAssert(JK_AT_STRING_PTR (parseState) <= JK_END_STRING_PTR (parseState)); stringHash = calculateHash (stringHash, *atStringCharacter++); }
1446
+ stringHash = jk_calculateHash (stringHash, currentChar);
1447
+ while (atStringCharacter < nextValidCharacter) { NSCParameterAssert(JK_AT_STRING_PTR (parseState) <= JK_END_STRING_PTR (parseState)); stringHash = jk_calculateHash (stringHash, *atStringCharacter++); }
1447
1448
continue ;
1448
1449
} else {
1449
1450
if (JK_EXPECT_F (currentChar == (unsigned long )' "' )) { stringState = JSONStringStateFinished; goto finishedParsing; }
@@ -1460,7 +1461,7 @@ static int jk_parse_string(JKParseState *parseState) {
1460
1461
1461
1462
if (JK_EXPECT_F (currentChar < 0x20UL )) { jk_error (parseState, @" Invalid character < 0x20 found in string: 0x%2.2x ." , currentChar); stringState = JSONStringStateError; goto finishedParsing; }
1462
1463
1463
- stringHash = calculateHash (stringHash, currentChar);
1464
+ stringHash = jk_calculateHash (stringHash, currentChar);
1464
1465
}
1465
1466
}
1466
1467
@@ -1478,7 +1479,7 @@ static int jk_parse_string(JKParseState *parseState) {
1478
1479
if (JK_EXPECT_T (currentChar < (unsigned long )0x80 )) { // Not a UTF8 sequence
1479
1480
if (JK_EXPECT_F (currentChar == (unsigned long )' "' )) { stringState = JSONStringStateFinished; atStringCharacter++; goto finishedParsing; }
1480
1481
if (JK_EXPECT_F (currentChar == (unsigned long )' \\ ' )) { stringState = JSONStringStateEscape; continue ; }
1481
- stringHash = calculateHash (stringHash, currentChar);
1482
+ stringHash = jk_calculateHash (stringHash, currentChar);
1482
1483
tokenBuffer[tokenBufferIdx++] = currentChar;
1483
1484
continue ;
1484
1485
} else { // UTF8 sequence
@@ -1493,7 +1494,7 @@ static int jk_parse_string(JKParseState *parseState) {
1493
1494
atStringCharacter = nextValidCharacter - 1 ;
1494
1495
continue ;
1495
1496
} else {
1496
- while (atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = calculateHash (stringHash, *atStringCharacter++); }
1497
+ while (atStringCharacter < nextValidCharacter) { tokenBuffer[tokenBufferIdx++] = *atStringCharacter; stringHash = jk_calculateHash (stringHash, *atStringCharacter++); }
1497
1498
atStringCharacter--;
1498
1499
continue ;
1499
1500
}
@@ -1521,7 +1522,7 @@ static int jk_parse_string(JKParseState *parseState) {
1521
1522
1522
1523
parsedEscapedChar:
1523
1524
stringState = JSONStringStateParsing;
1524
- stringHash = calculateHash (stringHash, escapedChar);
1525
+ stringHash = jk_calculateHash (stringHash, escapedChar);
1525
1526
tokenBuffer[tokenBufferIdx++] = escapedChar;
1526
1527
break ;
1527
1528
@@ -1709,7 +1710,7 @@ static int jk_parse_number(JKParseState *parseState) {
1709
1710
if (JK_EXPECT_F (endOfNumber != &numberTempBuf[parseState->token.tokenPtrRange.length]) && JK_EXPECT_F (numberState != JSONNumberStateError)) { numberState = JSONNumberStateError; jk_error (parseState, @" The conversion function did not consume all of the number tokens characters." ); }
1710
1711
1711
1712
size_t hashIndex = 0UL ;
1712
- for (hashIndex = 0UL ; hashIndex < parseState->token .value .ptrRange .length ; hashIndex++) { parseState->token .value .hash = calculateHash (parseState->token .value .hash , parseState->token .value .ptrRange .ptr [hashIndex]); }
1713
+ for (hashIndex = 0UL ; hashIndex < parseState->token .value .ptrRange .length ; hashIndex++) { parseState->token .value .hash = jk_calculateHash (parseState->token .value .hash , parseState->token .value .ptrRange .ptr [hashIndex]); }
1713
1714
}
1714
1715
1715
1716
if (JK_EXPECT_F (numberState != JSONNumberStateFinished)) { jk_error (parseState, @" Invalid number." ); }
@@ -1972,7 +1973,7 @@ static id json_parse_it(JKParseState *parseState) {
1972
1973
#pragma mark Object cache
1973
1974
1974
1975
// This uses a Galois Linear Feedback Shift Register (LFSR) PRNG to pick which item in the cache to age. It has a period of (2^32)-1.
1975
- // NOTE: A LFSR *MUST* be initialized to a non-zero value and must always have a non-zero value.
1976
+ // NOTE: A LFSR *MUST* be initialized to a non-zero value and must always have a non-zero value. The LFSR is initalized to 1 in -initWithParseOptions:
1976
1977
JK_STATIC_INLINE void jk_cache_age (JKParseState *parseState) {
1977
1978
NSCParameterAssert ((parseState != NULL ) && (parseState->cache.prng_lfsr != 0U ));
1978
1979
parseState->cache .prng_lfsr = (parseState->cache .prng_lfsr >> 1 ) ^ ((0U - (parseState->cache .prng_lfsr & 1U )) & 0x80200003U );
@@ -1983,9 +1984,8 @@ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
1983
1984
//
1984
1985
// The hash table is a linear C array of JKTokenCacheItem. The terms "item" and "bucket" are synonymous with the index in to the cache array, i.e. cache.items[bucket].
1985
1986
//
1986
- // Items in the cache have an age associated with them. The age is the number of rightmost 1 bits, i.e. 0000 = 0, 0001 = 1, 0011 = 2, 0111 = 3, 1111 = 4.
1987
- // This allows us to use left and right shifts to add or subtract from an items age. Add = (age << 1) | 1. Subtract = age >> 0. Subtract is synonymous with "age" (i.e., age an item).
1988
- // The reason for this is it allows us to perform saturated adds and subtractions and is branchless.
1987
+ // Items in the cache have an age associated with them. An items age is incremented using saturating unsigned arithmetic and decremeted using unsigned right shifts.
1988
+ // Thus, an items age is managed using an AIMD policy- additive increase, multiplicative decrease. All age calculations and manipulations are branchless.
1989
1989
// The primitive C type MUST be unsigned. It is currently a "char", which allows (at a minimum and in practice) 8 bits.
1990
1990
//
1991
1991
// A "useable bucket" is a bucket that is not in use (never populated), or has an age == 0.
@@ -2000,12 +2000,12 @@ JK_STATIC_INLINE void jk_cache_age(JKParseState *parseState) {
2000
2000
void *parsedAtom = NULL ;
2001
2001
2002
2002
if (JK_EXPECT_F (parseState->token .value .ptrRange .length == 0UL ) && JK_EXPECT_T (parseState->token .value .type == JKValueTypeString)) { return (@" " ); }
2003
-
2003
+
2004
2004
for (x = 0UL ; x < JK_CACHE_PROBES; x++) {
2005
2005
if (JK_EXPECT_F (parseState->cache .items [bucket].object == NULL )) { setBucket = 1UL ; useableBucket = bucket; break ; }
2006
2006
2007
2007
if ((JK_EXPECT_T (parseState->cache .items [bucket].hash == parseState->token .value .hash )) && (JK_EXPECT_T (parseState->cache .items [bucket].size == parseState->token .value .ptrRange .length )) && (JK_EXPECT_T (parseState->cache .items [bucket].type == parseState->token .value .type )) && (JK_EXPECT_T (parseState->cache .items [bucket].bytes != NULL )) && (JK_EXPECT_T (memcmp (parseState->cache .items [bucket].bytes , parseState->token .value .ptrRange .ptr , parseState->token .value .ptrRange .length ) == 0U ))) {
2008
- parseState->cache .age [bucket] = (parseState->cache .age [bucket] << 1 ) | 1U ;
2008
+ parseState->cache .age [bucket] = ((( uint32_t ) parseState->cache .age [bucket]) + 1U ) - ((((( uint32_t )parseState-> cache . age [bucket]) + 1U ) >> 31 ) ^ 1U ) ;
2009
2009
parseState->token .value .cacheItem = &parseState->cache .items [bucket];
2010
2010
NSCParameterAssert (parseState->cache.items[bucket].object != NULL );
2011
2011
return ((void *)CFRetain (parseState->cache .items [bucket].object ));
0 commit comments