@@ -37,11 +37,10 @@ struct PLDHashTableOps;
37
37
// structure, for single static initialization per hash table sub-type.
38
38
//
39
39
// Each hash table sub-type should make its entry type a subclass of
40
- // PLDHashEntryHdr. The mKeyHash member contains the result of suitably
41
- // scrambling the hash code returned from the hashKey callback (see below),
42
- // then constraining the result to avoid the magic 0 and 1 values. The stored
43
- // mKeyHash value is table size invariant, and it is maintained automatically
44
- // -- users need never access it.
40
+ // PLDHashEntryHdr. PLDHashEntryHdr is merely a common superclass to present a
41
+ // uniform interface to PLDHashTable clients. The zero-sized base class
42
+ // optimization, employed by all of our supported C++ compilers, will ensure
43
+ // that this abstraction does not make objects needlessly larger.
45
44
struct PLDHashEntryHdr
46
45
{
47
46
PLDHashEntryHdr () = default ;
@@ -52,8 +51,6 @@ struct PLDHashEntryHdr
52
51
53
52
private:
54
53
friend class PLDHashTable ;
55
-
56
- PLDHashNumber mKeyHash ;
57
54
};
58
55
59
56
#ifdef DEBUG
@@ -223,57 +220,98 @@ class Checker
223
220
class PLDHashTable
224
221
{
225
222
private:
226
- // A slot represents a cached hash value and its associated entry stored
227
- // in the hash table. While they currently belong to the same object,
228
- // PLDHashEntryHdr, they do not necessarily need to be contiguous in memory,
229
- // and this abstraction helps enforce the separation between the two.
223
+ // A slot represents a cached hash value and its associated entry stored in
224
+ // the hash table. The hash value and the entry are not stored contiguously.
230
225
struct Slot
231
226
{
232
- Slot (PLDHashEntryHdr* aEntry)
227
+ Slot (PLDHashEntryHdr* aEntry, PLDHashNumber* aKeyHash )
233
228
: mEntry (aEntry)
229
+ , mKeyHash (aKeyHash)
234
230
{}
235
231
236
232
Slot (const Slot&) = default ;
237
- Slot (Slot&& aOther)
238
- : mEntry (aOther.mEntry )
239
- {}
233
+ Slot (Slot&& aOther) = default ;
240
234
241
235
Slot& operator =(Slot&& aOther) {
242
236
this ->~Slot ();
243
237
new (this ) Slot (std::move (aOther));
244
238
return *this ;
245
239
}
246
240
247
- bool operator ==(const Slot& aOther)
248
- {
249
- return mEntry == aOther.mEntry ;
250
- }
241
+ bool operator ==(const Slot& aOther) { return mEntry == aOther.mEntry ; }
251
242
252
- PLDHashNumber KeyHash () const { return mEntry -> mKeyHash ; }
253
- void SetKeyHash (PLDHashNumber aHash) { mEntry -> mKeyHash = aHash; }
243
+ PLDHashNumber KeyHash () const { return * HashPtr () ; }
244
+ void SetKeyHash (PLDHashNumber aHash) { * HashPtr () = aHash; }
254
245
255
246
PLDHashEntryHdr* ToEntry () const { return mEntry ; }
256
247
257
248
bool IsFree () const { return KeyHash () == 0 ; }
258
249
bool IsRemoved () const { return KeyHash () == 1 ; }
259
250
bool IsLive () const { return KeyHash () >= 2 ; }
260
251
261
- void MarkFree () { mEntry -> mKeyHash = 0 ; }
262
- void MarkRemoved () { mEntry -> mKeyHash = 1 ; }
263
- void MarkColliding () { mEntry -> mKeyHash |= kCollisionFlag ; }
252
+ void MarkFree () { * HashPtr () = 0 ; }
253
+ void MarkRemoved () { * HashPtr () = 1 ; }
254
+ void MarkColliding () { * HashPtr () |= kCollisionFlag ; }
264
255
265
256
void Next (uint32_t aEntrySize) {
266
257
char * p = reinterpret_cast <char *>(mEntry );
267
258
p += aEntrySize;
268
259
mEntry = reinterpret_cast <PLDHashEntryHdr*>(p);
260
+ mKeyHash ++;
269
261
}
270
262
private:
263
+ PLDHashNumber* HashPtr () const { return mKeyHash ; }
264
+
271
265
PLDHashEntryHdr* mEntry ;
266
+ PLDHashNumber* mKeyHash ;
272
267
};
273
268
274
269
// This class maintains the invariant that every time the entry store is
275
270
// changed, the generation is updated.
276
271
//
272
+ // The data layout separates the cached hashes of entries and the entries
273
+ // themselves to save space. We could store the entries thusly:
274
+ //
275
+ // +--------+--------+---------+
276
+ // | entry0 | entry1 | ... |
277
+ // +--------+--------+---------+
278
+ //
279
+ // where the entries themselves contain the cached hash stored as their
280
+ // first member. PLDHashTable did this for a long time, with entries looking
281
+ // like:
282
+ //
283
+ // class PLDHashEntryHdr
284
+ // {
285
+ // PLDHashNumber mKeyHash;
286
+ // };
287
+ //
288
+ // class MyEntry : public PLDHashEntryHdr
289
+ // {
290
+ // ...
291
+ // };
292
+ //
293
+ // The problem with this setup is that, depending on the layout of
294
+ // `MyEntry`, there may be platform ABI-mandated padding between `mKeyHash`
295
+ // and the first member of `MyEntry`. This ABI-mandated padding is wasted
296
+ // space, and was surprisingly common, e.g. when MyEntry contained a single
297
+ // pointer on 64-bit platforms.
298
+ //
299
+ // As previously alluded to, the current setup stores things thusly:
300
+ //
301
+ // +-------+-------+-------+-------+--------+--------+---------+
302
+ // | hash0 | hash1 | ..... | hashN | entry0 | entry1 | ... |
303
+ // +-------+-------+-------+-------+--------+--------+---------+
304
+ //
305
+ // which contains no wasted space between the hashes themselves, and no
306
+ // wasted space between the entries themselves. malloc is guaranteed to
307
+ // return blocks of memory with at least word alignment on all of our major
308
+ // platforms. PLDHashTable mandates that the size of the hash table is
309
+ // always a power of two, so the alignment of the memory containing the
310
+ // first entry is always at least the alignment of the entire entry store.
311
+ // That means the alignment of `entry0` should be its natural alignment.
312
+ // Entries may have problems if they contain over-aligned members such as
313
+ // SIMD vector types, but this has not been a problem in practice.
314
+ //
277
315
// Note: It would be natural to store the generation within this class, but
278
316
// we can't do that without bloating sizeof(PLDHashTable) on 64-bit machines.
279
317
// So instead we store it outside this class, and Set() takes a pointer to it
@@ -283,9 +321,16 @@ class PLDHashTable
283
321
private:
284
322
char * mEntryStore ;
285
323
286
- PLDHashEntryHdr* EntryAt (uint32_t aIndex, uint32_t aEntrySize) const {
287
- return reinterpret_cast <PLDHashEntryHdr*>(Get () + aIndex * aEntrySize);
324
+ static char * Entries (char * aStore, uint32_t aCapacity)
325
+ {
326
+ return aStore + aCapacity * sizeof (PLDHashNumber);
327
+ }
328
+
329
+ char * Entries (uint32_t aCapacity) const
330
+ {
331
+ return Entries (Get (), aCapacity);
288
332
}
333
+
289
334
public:
290
335
EntryStore () : mEntryStore (nullptr ) {}
291
336
@@ -296,8 +341,24 @@ class PLDHashTable
296
341
}
297
342
298
343
char * Get () const { return mEntryStore ; }
299
- Slot SlotForIndex (uint32_t aIndex, uint32_t aEntrySize) const {
300
- return Slot (EntryAt (aIndex, aEntrySize));
344
+
345
+ Slot SlotForIndex (uint32_t aIndex, uint32_t aEntrySize,
346
+ uint32_t aCapacity) const
347
+ {
348
+ char * entries = Entries (aCapacity);
349
+ auto entry = reinterpret_cast <PLDHashEntryHdr*>(entries + aIndex * aEntrySize);
350
+ auto hashes = reinterpret_cast <PLDHashNumber*>(Get ());
351
+ return Slot (entry, &hashes[aIndex]);
352
+ }
353
+
354
+ Slot SlotForPLDHashEntry (PLDHashEntryHdr* aEntry,
355
+ uint32_t aCapacity, uint32_t aEntrySize)
356
+ {
357
+ char * entries = Entries (aCapacity);
358
+ char * entry = reinterpret_cast <char *>(aEntry);
359
+ uint32_t entryOffset = entry - entries;
360
+ uint32_t slotIndex = entryOffset / aEntrySize;
361
+ return SlotForIndex (slotIndex, aEntrySize, aCapacity);
301
362
}
302
363
303
364
template <typename F>
@@ -308,7 +369,9 @@ class PLDHashTable
308
369
template <typename F>
309
370
static void ForEachSlot (char * aStore, uint32_t aCapacity, uint32_t aEntrySize,
310
371
F&& aFunc) {
311
- Slot slot (reinterpret_cast <PLDHashEntryHdr*>(aStore));
372
+ char * entries = Entries (aStore, aCapacity);
373
+ Slot slot (reinterpret_cast <PLDHashEntryHdr*>(entries),
374
+ reinterpret_cast <PLDHashNumber*>(aStore));
312
375
for (size_t i = 0 ; i < aCapacity; ++i) {
313
376
aFunc (slot);
314
377
slot.Next (aEntrySize);
@@ -417,11 +480,9 @@ class PLDHashTable
417
480
// If |entry| is null upon return, then the table is severely overloaded and
418
481
// memory can't be allocated for entry storage.
419
482
//
420
- // Otherwise, |aEntry->mKeyHash| has been set so that
421
- // PLDHashTable::EntryIsFree(entry) is false, and it is up to the caller to
422
- // initialize the key and value parts of the entry sub-type, if they have not
423
- // been set already (i.e. if entry was not already in the table, and if the
424
- // optional initEntry hook was not used).
483
+ // Otherwise, if the initEntry hook was provided, |entry| will be
484
+ // initialized. If the initEntry hook was not provided, the caller
485
+ // should initialize |entry| as appropriate.
425
486
PLDHashEntryHdr* Add (const void * aKey, const mozilla::fallible_t &);
426
487
427
488
// This is like the other Add() function, but infallible, and so never
@@ -583,37 +644,12 @@ class PLDHashTable
583
644
584
645
static const PLDHashNumber kCollisionFlag = 1 ;
585
646
586
- static bool EntryIsFree (const PLDHashEntryHdr* aEntry)
587
- {
588
- return aEntry->mKeyHash == 0 ;
589
- }
590
- static bool EntryIsRemoved (const PLDHashEntryHdr* aEntry)
591
- {
592
- return aEntry->mKeyHash == 1 ;
593
- }
594
- static bool EntryIsLive (const PLDHashEntryHdr* aEntry)
595
- {
596
- return aEntry->mKeyHash >= 2 ;
597
- }
598
-
599
- static void MarkEntryFree (PLDHashEntryHdr* aEntry)
600
- {
601
- aEntry->mKeyHash = 0 ;
602
- }
603
- static void MarkEntryRemoved (PLDHashEntryHdr* aEntry)
604
- {
605
- aEntry->mKeyHash = 1 ;
606
- }
607
-
608
647
PLDHashNumber Hash1 (PLDHashNumber aHash0) const ;
609
648
void Hash2 (PLDHashNumber aHash,
610
649
uint32_t & aHash2Out, uint32_t & aSizeMaskOut) const ;
611
650
612
651
static bool MatchSlotKeyhash (Slot& aSlot, const PLDHashNumber aHash);
613
- static bool MatchEntryKeyhash (const PLDHashEntryHdr* aEntry,
614
- const PLDHashNumber aHash);
615
652
Slot SlotForIndex (uint32_t aIndex) const ;
616
- PLDHashEntryHdr* AddressEntry (uint32_t aIndex) const ;
617
653
618
654
// We store mHashShift rather than sizeLog2 to optimize the collision-free
619
655
// case in SearchTable.
@@ -666,10 +702,8 @@ typedef void (*PLDHashMoveEntry)(PLDHashTable* aTable,
666
702
typedef void (*PLDHashClearEntry)(PLDHashTable* aTable,
667
703
PLDHashEntryHdr* aEntry);
668
704
669
- // Initialize a new entry, apart from mKeyHash. This function is called when
670
- // Add() finds no existing entry for the given key, and must add a new one. At
671
- // that point, |aEntry->mKeyHash| is not set yet, to avoid claiming the last
672
- // free entry in a severely overloaded table.
705
+ // Initialize a new entry. This function is called when
706
+ // Add() finds no existing entry for the given key, and must add a new one.
673
707
typedef void (*PLDHashInitEntry)(PLDHashEntryHdr* aEntry, const void * aKey);
674
708
675
709
// Finally, the "vtable" structure for PLDHashTable. The first four hooks
@@ -683,13 +717,12 @@ typedef void (*PLDHashInitEntry)(PLDHashEntryHdr* aEntry, const void* aKey);
683
717
// clearEntry Run dtor on entry.
684
718
//
685
719
// Note the reason why initEntry is optional: the default hooks (stubs) clear
686
- // entry storage: On successful Add(tbl, key), the returned entry pointer
687
- // addresses an entry struct whose mKeyHash member has been set non-zero, but
688
- // all other entry members are still clear (null). Add() callers can test such
689
- // members to see whether the entry was newly created by the Add() call that
690
- // just succeeded. If placement new or similar initialization is required,
691
- // define an |initEntry| hook. Of course, the |clearEntry| hook must zero or
692
- // null appropriately.
720
+ // entry storage. On a successful Add(tbl, key), the returned entry pointer
721
+ // addresses an entry struct whose entry members are still clear (null). Add()
722
+ // callers can test such members to see whether the entry was newly created by
723
+ // the Add() call that just succeeded. If placement new or similar
724
+ // initialization is required, define an |initEntry| hook. Of course, the
725
+ // |clearEntry| hook must zero or null appropriately.
693
726
//
694
727
// XXX assumes 0 is null for pointer types.
695
728
struct PLDHashTableOps
0 commit comments