]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/PropertyMapHashTable.h
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / runtime / PropertyMapHashTable.h
CommitLineData
9dae56ea 1/*
ed1e77d3 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2014 Apple Inc. All rights reserved.
9dae56ea
A
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#ifndef PropertyMapHashTable_h
22#define PropertyMapHashTable_h
23
81345200 24#include "JSExportMacros.h"
93a37866
A
25#include "PropertyOffset.h"
26#include "Structure.h"
14957cd0 27#include "WriteBarrier.h"
81345200 28#include <wtf/CryptographicallyRandomNumber.h>
14957cd0 29#include <wtf/HashTable.h>
93a37866 30#include <wtf/MathExtras.h>
9dae56ea 31#include <wtf/Vector.h>
ed1e77d3 32#include <wtf/text/AtomicStringImpl.h>
9dae56ea 33
14957cd0 34
14957cd0 35#define DUMP_PROPERTYMAP_STATS 0
81345200 36#define DUMP_PROPERTYMAP_COLLISIONS 0
14957cd0 37
ed1e77d3 38#define PROPERTY_MAP_DELETED_ENTRY_KEY ((UniquedStringImpl*)1)
14957cd0 39
81345200 40namespace JSC {
14957cd0 41
81345200 42#if DUMP_PROPERTYMAP_STATS
14957cd0 43
81345200
A
44struct PropertyMapHashTableStats {
45 std::atomic<unsigned> numFinds;
46 std::atomic<unsigned> numCollisions;
47 std::atomic<unsigned> numLookups;
48 std::atomic<unsigned> numLookupProbing;
49 std::atomic<unsigned> numAdds;
50 std::atomic<unsigned> numRemoves;
51 std::atomic<unsigned> numRehashes;
52 std::atomic<unsigned> numReinserts;
53};
14957cd0 54
81345200
A
55JS_EXPORTDATA extern PropertyMapHashTableStats* propertyMapHashTableStats;
56
57#endif
9dae56ea 58
14957cd0
A
59inline bool isPowerOf2(unsigned v)
60{
93a37866 61 return hasOneBitSet(v);
14957cd0
A
62}
63
64inline unsigned nextPowerOf2(unsigned v)
65{
66 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
67 // Devised by Sean Anderson, Sepember 14, 2001
68
69 v--;
70 v |= v >> 1;
71 v |= v >> 2;
72 v |= v >> 4;
73 v |= v >> 8;
74 v |= v >> 16;
75 v++;
76
77 return v;
78}
79
ed1e77d3 80class PropertyTable final : public JSCell {
14957cd0
A
81
82 // This is the implementation for 'iterator' and 'const_iterator',
83 // used for iterating over the table in insertion order.
84 template<typename T>
85 class ordered_iterator {
86 public:
87 ordered_iterator<T>& operator++()
9dae56ea 88 {
14957cd0
A
89 m_valuePtr = skipDeletedEntries(m_valuePtr + 1);
90 return *this;
9dae56ea
A
91 }
92
14957cd0 93 bool operator==(const ordered_iterator<T>& other)
9dae56ea 94 {
14957cd0
A
95 return m_valuePtr == other.m_valuePtr;
96 }
97
98 bool operator!=(const ordered_iterator<T>& other)
99 {
100 return m_valuePtr != other.m_valuePtr;
101 }
102
103 T& operator*()
104 {
105 return *m_valuePtr;
9dae56ea 106 }
9dae56ea 107
14957cd0 108 T* operator->()
9dae56ea 109 {
14957cd0 110 return m_valuePtr;
9dae56ea
A
111 }
112
14957cd0
A
113 ordered_iterator(T* valuePtr)
114 : m_valuePtr(valuePtr)
9dae56ea 115 {
9dae56ea 116 }
14957cd0
A
117
118 private:
119 T* m_valuePtr;
9dae56ea
A
120 };
121
14957cd0 122public:
ed1e77d3
A
123 typedef JSCell Base;
124 static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
125
93a37866 126 static const bool needsDestruction = true;
93a37866
A
127 static void destroy(JSCell*);
128
81345200 129 DECLARE_EXPORT_INFO;
93a37866
A
130
131 static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
132 {
ed1e77d3 133 return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
93a37866
A
134 }
135
ed1e77d3 136 typedef UniquedStringImpl* KeyType;
14957cd0
A
137 typedef PropertyMapEntry ValueType;
138
139 // The in order iterator provides overloaded * and -> to access the Value at the current position.
140 typedef ordered_iterator<ValueType> iterator;
141 typedef ordered_iterator<const ValueType> const_iterator;
142
143 // The find_iterator is a pair of a pointer to a Value* an the entry in the index.
144 // If 'find' does not find an entry then iter.first will be 0, and iter.second will
145 // give the point in m_index where an entry should be inserted.
146 typedef std::pair<ValueType*, unsigned> find_iterator;
147
148 // Constructor is passed an initial capacity, a PropertyTable to copy, or both.
93a37866 149 static PropertyTable* create(VM&, unsigned initialCapacity);
81345200
A
150 static PropertyTable* clone(VM&, const PropertyTable&);
151 static PropertyTable* clone(VM&, unsigned initialCapacity, const PropertyTable&);
14957cd0
A
152 ~PropertyTable();
153
154 // Ordered iteration methods.
155 iterator begin();
156 iterator end();
157 const_iterator begin() const;
158 const_iterator end() const;
159
160 // Find a value in the table.
6fe7ccc8 161 find_iterator find(const KeyType&);
81345200 162 ValueType* get(const KeyType&);
14957cd0 163 // Add a value to the table
93a37866
A
164 enum EffectOnPropertyOffset { PropertyOffsetMayChange, PropertyOffsetMustNotChange };
165 std::pair<find_iterator, bool> add(const ValueType& entry, PropertyOffset&, EffectOnPropertyOffset);
14957cd0
A
166 // Remove a value from the table.
167 void remove(const find_iterator& iter);
168 void remove(const KeyType& key);
169
170 // Returns the number of values in the hashtable.
171 unsigned size() const;
172
173 // Checks if there are any values in the hashtable.
174 bool isEmpty() const;
175
176 // Number of slots in the property storage array in use, included deletedOffsets.
177 unsigned propertyStorageSize() const;
178
179 // Used to maintain a list of unused entries in the property storage.
180 void clearDeletedOffsets();
181 bool hasDeletedOffset();
93a37866
A
182 PropertyOffset getDeletedOffset();
183 void addDeletedOffset(PropertyOffset);
184
185 PropertyOffset nextOffset(PropertyOffset inlineCapacity);
14957cd0
A
186
187 // Copy this PropertyTable, ensuring the copy has at least the capacity provided.
81345200 188 PropertyTable* copy(VM&, unsigned newCapacity);
14957cd0
A
189
190#ifndef NDEBUG
191 size_t sizeInMemory();
192 void checkConsistency();
193#endif
194
195private:
93a37866 196 PropertyTable(VM&, unsigned initialCapacity);
81345200
A
197 PropertyTable(VM&, const PropertyTable&);
198 PropertyTable(VM&, unsigned initialCapacity, const PropertyTable&);
93a37866 199
14957cd0
A
200 PropertyTable(const PropertyTable&);
201 // Used to insert a value known not to be in the table, and where we know capacity to be available.
202 void reinsert(const ValueType& entry);
203
204 // Rehash the table. Used to grow, or to recover deleted slots.
205 void rehash(unsigned newCapacity);
206
207 // The capacity of the table of values is half of the size of the index.
208 unsigned tableCapacity() const;
209
210 // We keep an extra deleted slot after the array to make iteration work,
211 // and to use for deleted values. Index values into the array are 1-based,
212 // so this is tableCapacity() + 1.
213 // For example, if m_tableSize is 16, then tableCapacity() is 8 - but the
214 // values array is actually 9 long (the 9th used for the deleted value/
215 // iteration guard). The 8 valid entries are numbered 1..8, so the
216 // deleted index is 9 (0 being reserved for empty).
217 unsigned deletedEntryIndex() const;
218
219 // Used in iterator creation/progression.
220 template<typename T>
221 static T* skipDeletedEntries(T* valuePtr);
222
223 // The table of values lies after the hash index.
224 ValueType* table();
225 const ValueType* table() const;
226
227 // total number of used entries in the values array - by either valid entries, or deleted ones.
228 unsigned usedCount() const;
229
230 // The size in bytes of data needed for by the table.
231 size_t dataSize();
232
233 // Calculates the appropriate table size (rounds up to a power of two).
234 static unsigned sizeForCapacity(unsigned capacity);
235
236 // Check if capacity is available.
237 bool canInsert();
238
239 unsigned m_indexSize;
240 unsigned m_indexMask;
241 unsigned* m_index;
242 unsigned m_keyCount;
243 unsigned m_deletedCount;
ed1e77d3 244 std::unique_ptr<Vector<PropertyOffset>> m_deletedOffsets;
14957cd0 245
81345200 246 static const unsigned MinimumTableSize = 16;
14957cd0
A
247 static const unsigned EmptyEntryIndex = 0;
248};
249
14957cd0
A
250inline PropertyTable::iterator PropertyTable::begin()
251{
252 return iterator(skipDeletedEntries(table()));
253}
254
255inline PropertyTable::iterator PropertyTable::end()
256{
257 return iterator(table() + usedCount());
258}
259
260inline PropertyTable::const_iterator PropertyTable::begin() const
261{
262 return const_iterator(skipDeletedEntries(table()));
263}
264
265inline PropertyTable::const_iterator PropertyTable::end() const
266{
267 return const_iterator(table() + usedCount());
268}
269
270inline PropertyTable::find_iterator PropertyTable::find(const KeyType& key)
271{
272 ASSERT(key);
ed1e77d3
A
273 ASSERT(key->isAtomic() || key->isSymbol());
274 unsigned hash = IdentifierRepHash::hash(key);
14957cd0
A
275 unsigned step = 0;
276
277#if DUMP_PROPERTYMAP_STATS
81345200 278 ++propertyMapHashTableStats->numFinds;
14957cd0
A
279#endif
280
281 while (true) {
282 unsigned entryIndex = m_index[hash & m_indexMask];
283 if (entryIndex == EmptyEntryIndex)
284 return std::make_pair((ValueType*)0, hash & m_indexMask);
285 if (key == table()[entryIndex - 1].key)
286 return std::make_pair(&table()[entryIndex - 1], hash & m_indexMask);
287
81345200 288 if (!step)
ed1e77d3 289 step = WTF::doubleHash(IdentifierRepHash::hash(key)) | 1;
81345200 290
14957cd0 291#if DUMP_PROPERTYMAP_STATS
81345200
A
292 ++propertyMapHashTableStats->numCollisions;
293#endif
294
295#if DUMP_PROPERTYMAP_COLLISIONS
296 dataLog("PropertyTable collision for ", key, " (", hash, ") with step ", step, "\n");
ed1e77d3 297 dataLog("Collided with ", table()[entryIndex - 1].key, "(", IdentifierRepHash::hash(table()[entryIndex - 1].key), ")\n");
14957cd0
A
298#endif
299
6fe7ccc8 300 hash += step;
81345200
A
301 }
302}
303
304inline PropertyTable::ValueType* PropertyTable::get(const KeyType& key)
305{
306 ASSERT(key);
ed1e77d3 307 ASSERT(key->isAtomic() || key->isSymbol());
81345200
A
308
309 if (!m_keyCount)
310 return nullptr;
311
ed1e77d3 312 unsigned hash = IdentifierRepHash::hash(key);
81345200
A
313 unsigned step = 0;
314
315#if DUMP_PROPERTYMAP_STATS
316 ++propertyMapHashTableStats->numLookups;
317#endif
318
319 while (true) {
320 unsigned entryIndex = m_index[hash & m_indexMask];
321 if (entryIndex == EmptyEntryIndex)
322 return nullptr;
323 if (key == table()[entryIndex - 1].key)
324 return &table()[entryIndex - 1];
6fe7ccc8
A
325
326#if DUMP_PROPERTYMAP_STATS
81345200 327 ++propertyMapHashTableStats->numLookupProbing;
6fe7ccc8 328#endif
81345200
A
329
330 if (!step)
ed1e77d3 331 step = WTF::doubleHash(IdentifierRepHash::hash(key)) | 1;
14957cd0 332 hash += step;
14957cd0
A
333 }
334}
335
93a37866 336inline std::pair<PropertyTable::find_iterator, bool> PropertyTable::add(const ValueType& entry, PropertyOffset& offset, EffectOnPropertyOffset offsetEffect)
14957cd0
A
337{
338 // Look for a value with a matching key already in the array.
339 find_iterator iter = find(entry.key);
93a37866
A
340 if (iter.first) {
341 RELEASE_ASSERT(iter.first->offset <= offset);
14957cd0 342 return std::make_pair(iter, false);
93a37866 343 }
14957cd0 344
81345200
A
345#if DUMP_PROPERTYMAP_STATS
346 ++propertyMapHashTableStats->numAdds;
347#endif
348
14957cd0
A
349 // Ref the key
350 entry.key->ref();
351
352 // ensure capacity is available.
353 if (!canInsert()) {
354 rehash(m_keyCount + 1);
355 iter = find(entry.key);
356 ASSERT(!iter.first);
357 }
358
359 // Allocate a slot in the hashtable, and set the index to reference this.
360 unsigned entryIndex = usedCount() + 1;
361 m_index[iter.second] = entryIndex;
362 iter.first = &table()[entryIndex - 1];
363 *iter.first = entry;
364
365 ++m_keyCount;
93a37866
A
366
367 if (offsetEffect == PropertyOffsetMayChange)
368 offset = std::max(offset, entry.offset);
369 else
370 RELEASE_ASSERT(offset >= entry.offset);
371
14957cd0
A
372 return std::make_pair(iter, true);
373}
374
375inline void PropertyTable::remove(const find_iterator& iter)
376{
377 // Removing a key that doesn't exist does nothing!
378 if (!iter.first)
379 return;
380
381#if DUMP_PROPERTYMAP_STATS
81345200 382 ++propertyMapHashTableStats->numRemoves;
14957cd0
A
383#endif
384
385 // Replace this one element with the deleted sentinel. Also clear out
386 // the entry so we can iterate all the entries as needed.
387 m_index[iter.second] = deletedEntryIndex();
388 iter.first->key->deref();
389 iter.first->key = PROPERTY_MAP_DELETED_ENTRY_KEY;
390
391 ASSERT(m_keyCount >= 1);
392 --m_keyCount;
393 ++m_deletedCount;
394
395 if (m_deletedCount * 4 >= m_indexSize)
396 rehash(m_keyCount);
397}
398
399inline void PropertyTable::remove(const KeyType& key)
400{
401 remove(find(key));
402}
403
404// returns the number of values in the hashtable.
405inline unsigned PropertyTable::size() const
406{
407 return m_keyCount;
408}
409
410inline bool PropertyTable::isEmpty() const
411{
412 return !m_keyCount;
413}
414
415inline unsigned PropertyTable::propertyStorageSize() const
416{
417 return size() + (m_deletedOffsets ? m_deletedOffsets->size() : 0);
418}
419
420inline void PropertyTable::clearDeletedOffsets()
421{
ed1e77d3 422 m_deletedOffsets = nullptr;
14957cd0
A
423}
424
425inline bool PropertyTable::hasDeletedOffset()
426{
427 return m_deletedOffsets && !m_deletedOffsets->isEmpty();
428}
429
93a37866 430inline PropertyOffset PropertyTable::getDeletedOffset()
14957cd0 431{
93a37866 432 PropertyOffset offset = m_deletedOffsets->last();
14957cd0
A
433 m_deletedOffsets->removeLast();
434 return offset;
435}
436
93a37866 437inline void PropertyTable::addDeletedOffset(PropertyOffset offset)
14957cd0
A
438{
439 if (!m_deletedOffsets)
ed1e77d3 440 m_deletedOffsets = std::make_unique<Vector<PropertyOffset>>();
14957cd0
A
441 m_deletedOffsets->append(offset);
442}
443
93a37866
A
444inline PropertyOffset PropertyTable::nextOffset(PropertyOffset inlineCapacity)
445{
446 if (hasDeletedOffset())
447 return getDeletedOffset();
448
449 return offsetForPropertyNumber(size(), inlineCapacity);
450}
451
81345200 452inline PropertyTable* PropertyTable::copy(VM& vm, unsigned newCapacity)
14957cd0
A
453{
454 ASSERT(newCapacity >= m_keyCount);
455
456 // Fast case; if the new table will be the same m_indexSize as this one, we can memcpy it,
457 // save rehashing all keys.
458 if (sizeForCapacity(newCapacity) == m_indexSize)
81345200
A
459 return PropertyTable::clone(vm, *this);
460 return PropertyTable::clone(vm, newCapacity, *this);
14957cd0
A
461}
462
463#ifndef NDEBUG
464inline size_t PropertyTable::sizeInMemory()
465{
466 size_t result = sizeof(PropertyTable) + dataSize();
467 if (m_deletedOffsets)
93a37866 468 result += (m_deletedOffsets->capacity() * sizeof(PropertyOffset));
14957cd0
A
469 return result;
470}
471#endif
472
473inline void PropertyTable::reinsert(const ValueType& entry)
474{
81345200
A
475#if DUMP_PROPERTYMAP_STATS
476 ++propertyMapHashTableStats->numReinserts;
477#endif
478
14957cd0
A
479 // Used to insert a value known not to be in the table, and where
480 // we know capacity to be available.
481 ASSERT(canInsert());
482 find_iterator iter = find(entry.key);
483 ASSERT(!iter.first);
484
485 unsigned entryIndex = usedCount() + 1;
486 m_index[iter.second] = entryIndex;
487 table()[entryIndex - 1] = entry;
488
489 ++m_keyCount;
490}
491
492inline void PropertyTable::rehash(unsigned newCapacity)
493{
81345200
A
494#if DUMP_PROPERTYMAP_STATS
495 ++propertyMapHashTableStats->numRehashes;
496#endif
497
14957cd0
A
498 unsigned* oldEntryIndices = m_index;
499 iterator iter = this->begin();
500 iterator end = this->end();
501
502 m_indexSize = sizeForCapacity(newCapacity);
503 m_indexMask = m_indexSize - 1;
504 m_keyCount = 0;
505 m_deletedCount = 0;
506 m_index = static_cast<unsigned*>(fastZeroedMalloc(dataSize()));
507
508 for (; iter != end; ++iter) {
509 ASSERT(canInsert());
510 reinsert(*iter);
511 }
512
513 fastFree(oldEntryIndices);
514}
515
516inline unsigned PropertyTable::tableCapacity() const { return m_indexSize >> 1; }
517
518inline unsigned PropertyTable::deletedEntryIndex() const { return tableCapacity() + 1; }
519
520template<typename T>
521inline T* PropertyTable::skipDeletedEntries(T* valuePtr)
522{
523 while (valuePtr->key == PROPERTY_MAP_DELETED_ENTRY_KEY)
524 ++valuePtr;
525 return valuePtr;
526}
527
528inline PropertyTable::ValueType* PropertyTable::table()
529{
530 // The table of values lies after the hash index.
531 return reinterpret_cast<ValueType*>(m_index + m_indexSize);
532}
533
534inline const PropertyTable::ValueType* PropertyTable::table() const
535{
536 // The table of values lies after the hash index.
537 return reinterpret_cast<const ValueType*>(m_index + m_indexSize);
538}
539
540inline unsigned PropertyTable::usedCount() const
541{
542 // Total number of used entries in the values array - by either valid entries, or deleted ones.
543 return m_keyCount + m_deletedCount;
544}
545
546inline size_t PropertyTable::dataSize()
547{
548 // The size in bytes of data needed for by the table.
549 return m_indexSize * sizeof(unsigned) + ((tableCapacity()) + 1) * sizeof(ValueType);
550}
551
552inline unsigned PropertyTable::sizeForCapacity(unsigned capacity)
553{
93a37866 554 if (capacity < MinimumTableSize / 2)
14957cd0
A
555 return MinimumTableSize;
556 return nextPowerOf2(capacity + 1) * 2;
557}
558
559inline bool PropertyTable::canInsert()
560{
561 return usedCount() < tableCapacity();
562}
563
9dae56ea
A
564} // namespace JSC
565
566#endif // PropertyMapHashTable_h