@@ -8,7 +8,7 @@ namespace Js
8
8
{
9
9
class TinyDictionary
10
10
{
11
- static const int PowerOf2_BUCKETS = 8 ;
11
+ static const int PowerOf2_BUCKETS = 16 ;
12
12
static const int BUCKETS_DWORDS = PowerOf2_BUCKETS / sizeof (DWORD);
13
13
static const byte NIL = 0xff ;
14
14
@@ -19,37 +19,73 @@ namespace Js
19
19
TinyDictionary ()
20
20
{
21
21
CompileAssert (BUCKETS_DWORDS * sizeof (DWORD) == PowerOf2_BUCKETS);
22
- CompileAssert (BUCKETS_DWORDS == 2 );
22
+ CompileAssert (BUCKETS_DWORDS == 4 );
23
23
DWORD* init = bucketsData;
24
- init[0 ] = init[1 ] = 0xffffffff ;
24
+ init[0 ] = init[1 ] = init[2 ] = init[3 ] =0xffffffff ;
25
+ }
26
+
27
+ uint32 ReduceKeyToIndex (PropertyId key)
28
+ {
29
+ // we use 4-bit bucket index, but we often have keys that are larger.
30
+ // use Fibonacci hash to reduce the possibility of collisions
31
+ #if TARGET_64
32
+ return (key * 11400714819323198485llu) >> 60 ;
33
+ #else
34
+ return (key * 2654435769ul ) >> 28 ;
35
+ #endif
25
36
}
26
37
27
38
void Add (PropertyId key, byte value)
28
39
{
40
+ Assert (value < 128 );
41
+
29
42
byte* buckets = reinterpret_cast <byte*>(bucketsData);
30
- uint32 bucketIndex = key & (PowerOf2_BUCKETS - 1 );
43
+ uint32 bucketIndex = ReduceKeyToIndex (key );
31
44
32
45
byte i = buckets[bucketIndex];
33
- buckets[bucketIndex] = value;
34
- next[value] = i;
46
+ if (i == NIL)
47
+ {
48
+ // set the highest bit to mark the value as the last in the chain
49
+ buckets[bucketIndex] = value | 128 ;
50
+ }
51
+ else
52
+ {
53
+ buckets[bucketIndex] = value;
54
+ next[value] = i;
55
+ }
35
56
}
36
57
37
58
// Template shared with diagnostics
38
59
template <class Data >
39
60
inline bool TryGetValue (PropertyId key, PropertyIndex* index, const Data& data)
40
61
{
41
62
byte* buckets = reinterpret_cast <byte*>(bucketsData);
42
- uint32 bucketIndex = key & (PowerOf2_BUCKETS - 1 );
63
+ uint32 bucketIndex = ReduceKeyToIndex (key );
43
64
44
- for (byte i = buckets[bucketIndex] ; i != NIL ; i = next[i])
65
+ byte i = buckets[bucketIndex];
66
+ if (i != NIL)
45
67
{
46
- if (data[i]->GetPropertyId ()== key)
47
- {
48
- *index = i;
49
- return true ;
68
+ for (;;)
69
+ {
70
+ byte idx = i & 127 ; // strip the sentinel bit
71
+
72
+ if (data[idx]->GetPropertyId () == key)
73
+ {
74
+ *index = idx;
75
+ return true ;
76
+ }
77
+
78
+ if (i & 128 )
79
+ {
80
+ // this was the last value in the chain
81
+ break ;
82
+ }
83
+
84
+ Assert (idx != (next[idx] & 127 ));
85
+ i = next[idx];
50
86
}
51
- Assert (i != next[i]);
52
87
}
88
+
53
89
return false ;
54
90
}
55
91
};
@@ -75,7 +111,10 @@ namespace Js
75
111
#define TYPE_PATH_ALLOC_GRANULARITY_GAP 3
76
112
#endif
77
113
#endif
78
- // Although we can allocate 2 more, this will put struct Data into another bucket. Just waste some slot in that case for 32-bit
114
+ // Although we can allocate 2 more,
115
+ // TinyDictionary can hold only up to 128 items (see TinyDictionary::Add),
116
+ // Besides 128+ would put struct Data into another bucket.
117
+ // Just waste some slot in that case for 32-bit
79
118
static const uint MaxPathTypeHandlerLength = 128 ;
80
119
static const uint InitialTypePathSize = 16 + TYPE_PATH_ALLOC_GRANULARITY_GAP;
81
120
0 commit comments