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