@@ -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,72 @@ 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
30
+ // sometimes we have keys that differ in higher bits, so smudge the bits down a little
31
+ // to reduce the possibility of collisions
32
+ key ^= (uint)key >> 15 ;
33
+ key ^= (uint)key >> 7 ;
34
+ return key & (PowerOf2_BUCKETS - 1 );
25
35
}
26
36
27
37
void Add (PropertyId key, byte value)
28
38
{
39
+ Assert (value < 128 );
40
+
29
41
byte* buckets = reinterpret_cast <byte*>(bucketsData);
30
- uint32 bucketIndex = key & (PowerOf2_BUCKETS - 1 );
42
+ uint32 bucketIndex = ReduceKeyToIndex (key );
31
43
32
44
byte i = buckets[bucketIndex];
33
- buckets[bucketIndex] = value;
34
- next[value] = i;
45
+ if (i == NIL)
46
+ {
47
+ // set the highest bit to mark the value as the last in the chain
48
+ buckets[bucketIndex] = value | 128 ;
49
+ }
50
+ else
51
+ {
52
+ buckets[bucketIndex] = value;
53
+ next[value & 127 ] = i;
54
+ }
35
55
}
36
56
37
57
// Template shared with diagnostics
38
58
template <class Data >
39
59
inline bool TryGetValue (PropertyId key, PropertyIndex* index, const Data& data)
40
60
{
41
61
byte* buckets = reinterpret_cast <byte*>(bucketsData);
42
- uint32 bucketIndex = key & (PowerOf2_BUCKETS - 1 );
62
+ uint32 bucketIndex = ReduceKeyToIndex (key );
43
63
44
- for (byte i = buckets[bucketIndex] ; i != NIL ; i = next[i])
64
+ byte i = buckets[bucketIndex];
65
+ if (i != NIL)
45
66
{
46
- if (data[i]->GetPropertyId ()== key)
47
- {
48
- *index = i;
49
- return true ;
67
+ for (;;)
68
+ {
69
+ byte idx = i & 127 ; // strip the sentinel bit
70
+
71
+ if (data[idx]->GetPropertyId () == key)
72
+ {
73
+ *index = idx;
74
+ return true ;
75
+ }
76
+
77
+ if (i & 128 )
78
+ {
79
+ // this was the last value in the chain
80
+ break ;
81
+ }
82
+
83
+ Assert (idx != (next[idx] & 127 ));
84
+ i = next[idx];
50
85
}
51
- Assert (i != next[i]);
52
86
}
87
+
53
88
return false ;
54
89
}
55
90
};
@@ -75,7 +110,10 @@ namespace Js
75
110
#define TYPE_PATH_ALLOC_GRANULARITY_GAP 3
76
111
#endif
77
112
#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
113
+ // Although we can allocate 2 more,
114
+ // TinyDictionary can hold only up to 128 items (see TinyDictionary::Add),
115
+ // Besides 128+ would put struct Data into another bucket.
116
+ // Just waste some slot in that case for 32-bit
79
117
static const uint MaxPathTypeHandlerLength = 128 ;
80
118
static const uint InitialTypePathSize = 16 + TYPE_PATH_ALLOC_GRANULARITY_GAP;
81
119
0 commit comments