2 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
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.
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.
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.
21 #ifndef PropertyMapHashTable_h
22 #define PropertyMapHashTable_h
24 #include "PropertyOffset.h"
25 #include "Structure.h"
26 #include "WriteBarrier.h"
27 #include <wtf/HashTable.h>
28 #include <wtf/MathExtras.h>
29 #include <wtf/PassOwnPtr.h>
30 #include <wtf/Vector.h>
31 #include <wtf/text/StringImpl.h>
35 #define DUMP_PROPERTYMAP_STATS 0
37 #define DUMP_PROPERTYMAP_STATS 0
40 #if DUMP_PROPERTYMAP_STATS
43 extern int numCollisions
;
44 extern int numRehashes
;
45 extern int numRemoves
;
49 #define PROPERTY_MAP_DELETED_ENTRY_KEY ((StringImpl*)1)
53 inline bool isPowerOf2(unsigned v
)
55 return hasOneBitSet(v
);
58 inline unsigned nextPowerOf2(unsigned v
)
60 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
61 // Devised by Sean Anderson, Sepember 14, 2001
74 struct PropertyMapEntry
{
76 PropertyOffset offset
;
78 WriteBarrier
<JSCell
> specificValue
;
80 PropertyMapEntry(VM
& vm
, JSCell
* owner
, StringImpl
* key
, PropertyOffset offset
, unsigned attributes
, JSCell
* specificValue
)
83 , attributes(attributes
)
84 , specificValue(vm
, owner
, specificValue
, WriteBarrier
<JSCell
>::MayBeNull
)
89 class PropertyTable
: public JSCell
{
91 // This is the implementation for 'iterator' and 'const_iterator',
92 // used for iterating over the table in insertion order.
94 class ordered_iterator
{
96 ordered_iterator
<T
>& operator++()
98 m_valuePtr
= skipDeletedEntries(m_valuePtr
+ 1);
102 bool operator==(const ordered_iterator
<T
>& other
)
104 return m_valuePtr
== other
.m_valuePtr
;
107 bool operator!=(const ordered_iterator
<T
>& other
)
109 return m_valuePtr
!= other
.m_valuePtr
;
122 ordered_iterator(T
* valuePtr
)
123 : m_valuePtr(valuePtr
)
132 static const bool needsDestruction
= true;
133 static const bool hasImmortalStructure
= true;
134 static void destroy(JSCell
*);
136 static JS_EXPORTDATA
const ClassInfo s_info
;
138 static Structure
* createStructure(VM
& vm
, JSGlobalObject
* globalObject
, JSValue prototype
)
140 return Structure::create(vm
, globalObject
, prototype
, TypeInfo(CompoundType
, OverridesVisitChildren
), &s_info
);
143 static void visitChildren(JSCell
*, SlotVisitor
&);
145 typedef StringImpl
* KeyType
;
146 typedef PropertyMapEntry ValueType
;
148 // The in order iterator provides overloaded * and -> to access the Value at the current position.
149 typedef ordered_iterator
<ValueType
> iterator
;
150 typedef ordered_iterator
<const ValueType
> const_iterator
;
152 // The find_iterator is a pair of a pointer to a Value* an the entry in the index.
153 // If 'find' does not find an entry then iter.first will be 0, and iter.second will
154 // give the point in m_index where an entry should be inserted.
155 typedef std::pair
<ValueType
*, unsigned> find_iterator
;
157 // Constructor is passed an initial capacity, a PropertyTable to copy, or both.
158 static PropertyTable
* create(VM
&, unsigned initialCapacity
);
159 static PropertyTable
* clone(VM
&, JSCell
* owner
, const PropertyTable
&);
160 static PropertyTable
* clone(VM
&, JSCell
* owner
, unsigned initialCapacity
, const PropertyTable
&);
163 // Ordered iteration methods.
166 const_iterator
begin() const;
167 const_iterator
end() const;
169 // Find a value in the table.
170 find_iterator
find(const KeyType
&);
171 find_iterator
findWithString(const KeyType
&);
172 // Add a value to the table
173 enum EffectOnPropertyOffset
{ PropertyOffsetMayChange
, PropertyOffsetMustNotChange
};
174 std::pair
<find_iterator
, bool> add(const ValueType
& entry
, PropertyOffset
&, EffectOnPropertyOffset
);
175 // Remove a value from the table.
176 void remove(const find_iterator
& iter
);
177 void remove(const KeyType
& key
);
179 // Returns the number of values in the hashtable.
180 unsigned size() const;
182 // Checks if there are any values in the hashtable.
183 bool isEmpty() const;
185 // Number of slots in the property storage array in use, included deletedOffsets.
186 unsigned propertyStorageSize() const;
188 // Used to maintain a list of unused entries in the property storage.
189 void clearDeletedOffsets();
190 bool hasDeletedOffset();
191 PropertyOffset
getDeletedOffset();
192 void addDeletedOffset(PropertyOffset
);
194 PropertyOffset
nextOffset(PropertyOffset inlineCapacity
);
196 // Copy this PropertyTable, ensuring the copy has at least the capacity provided.
197 PropertyTable
* copy(VM
&, JSCell
* owner
, unsigned newCapacity
);
200 size_t sizeInMemory();
201 void checkConsistency();
205 PropertyTable(VM
&, unsigned initialCapacity
);
206 PropertyTable(VM
&, JSCell
*, const PropertyTable
&);
207 PropertyTable(VM
&, JSCell
*, unsigned initialCapacity
, const PropertyTable
&);
209 PropertyTable(const PropertyTable
&);
210 // Used to insert a value known not to be in the table, and where we know capacity to be available.
211 void reinsert(const ValueType
& entry
);
213 // Rehash the table. Used to grow, or to recover deleted slots.
214 void rehash(unsigned newCapacity
);
216 // The capacity of the table of values is half of the size of the index.
217 unsigned tableCapacity() const;
219 // We keep an extra deleted slot after the array to make iteration work,
220 // and to use for deleted values. Index values into the array are 1-based,
221 // so this is tableCapacity() + 1.
222 // For example, if m_tableSize is 16, then tableCapacity() is 8 - but the
223 // values array is actually 9 long (the 9th used for the deleted value/
224 // iteration guard). The 8 valid entries are numbered 1..8, so the
225 // deleted index is 9 (0 being reserved for empty).
226 unsigned deletedEntryIndex() const;
228 // Used in iterator creation/progression.
230 static T
* skipDeletedEntries(T
* valuePtr
);
232 // The table of values lies after the hash index.
234 const ValueType
* table() const;
236 // total number of used entries in the values array - by either valid entries, or deleted ones.
237 unsigned usedCount() const;
239 // The size in bytes of data needed for by the table.
242 // Calculates the appropriate table size (rounds up to a power of two).
243 static unsigned sizeForCapacity(unsigned capacity
);
245 // Check if capacity is available.
248 unsigned m_indexSize
;
249 unsigned m_indexMask
;
252 unsigned m_deletedCount
;
253 OwnPtr
< Vector
<PropertyOffset
> > m_deletedOffsets
;
255 static const unsigned MinimumTableSize
= 8;
256 static const unsigned EmptyEntryIndex
= 0;
259 inline PropertyTable::iterator
PropertyTable::begin()
261 return iterator(skipDeletedEntries(table()));
264 inline PropertyTable::iterator
PropertyTable::end()
266 return iterator(table() + usedCount());
269 inline PropertyTable::const_iterator
PropertyTable::begin() const
271 return const_iterator(skipDeletedEntries(table()));
274 inline PropertyTable::const_iterator
PropertyTable::end() const
276 return const_iterator(table() + usedCount());
279 inline PropertyTable::find_iterator
PropertyTable::find(const KeyType
& key
)
282 ASSERT(key
->isIdentifier() || key
->isEmptyUnique());
283 unsigned hash
= key
->existingHash();
286 #if DUMP_PROPERTYMAP_STATS
291 unsigned entryIndex
= m_index
[hash
& m_indexMask
];
292 if (entryIndex
== EmptyEntryIndex
)
293 return std::make_pair((ValueType
*)0, hash
& m_indexMask
);
294 if (key
== table()[entryIndex
- 1].key
)
295 return std::make_pair(&table()[entryIndex
- 1], hash
& m_indexMask
);
297 #if DUMP_PROPERTYMAP_STATS
302 step
= WTF::doubleHash(key
->existingHash()) | 1;
305 #if DUMP_PROPERTYMAP_STATS
311 inline PropertyTable::find_iterator
PropertyTable::findWithString(const KeyType
& key
)
314 ASSERT(!key
->isIdentifier() && !key
->hasHash());
315 unsigned hash
= key
->hash();
318 #if DUMP_PROPERTYMAP_STATS
323 unsigned entryIndex
= m_index
[hash
& m_indexMask
];
324 if (entryIndex
== EmptyEntryIndex
)
325 return std::make_pair((ValueType
*)0, hash
& m_indexMask
);
326 const KeyType
& keyInMap
= table()[entryIndex
- 1].key
;
327 if (equal(key
, keyInMap
) && keyInMap
->isIdentifier())
328 return std::make_pair(&table()[entryIndex
- 1], hash
& m_indexMask
);
330 #if DUMP_PROPERTYMAP_STATS
335 step
= WTF::doubleHash(key
->existingHash()) | 1;
338 #if DUMP_PROPERTYMAP_STATS
344 inline std::pair
<PropertyTable::find_iterator
, bool> PropertyTable::add(const ValueType
& entry
, PropertyOffset
& offset
, EffectOnPropertyOffset offsetEffect
)
346 // Look for a value with a matching key already in the array.
347 find_iterator iter
= find(entry
.key
);
349 RELEASE_ASSERT(iter
.first
->offset
<= offset
);
350 return std::make_pair(iter
, false);
356 // ensure capacity is available.
358 rehash(m_keyCount
+ 1);
359 iter
= find(entry
.key
);
363 // Allocate a slot in the hashtable, and set the index to reference this.
364 unsigned entryIndex
= usedCount() + 1;
365 m_index
[iter
.second
] = entryIndex
;
366 iter
.first
= &table()[entryIndex
- 1];
371 if (offsetEffect
== PropertyOffsetMayChange
)
372 offset
= std::max(offset
, entry
.offset
);
374 RELEASE_ASSERT(offset
>= entry
.offset
);
376 return std::make_pair(iter
, true);
379 inline void PropertyTable::remove(const find_iterator
& iter
)
381 // Removing a key that doesn't exist does nothing!
385 #if DUMP_PROPERTYMAP_STATS
389 // Replace this one element with the deleted sentinel. Also clear out
390 // the entry so we can iterate all the entries as needed.
391 m_index
[iter
.second
] = deletedEntryIndex();
392 iter
.first
->key
->deref();
393 iter
.first
->key
= PROPERTY_MAP_DELETED_ENTRY_KEY
;
395 ASSERT(m_keyCount
>= 1);
399 if (m_deletedCount
* 4 >= m_indexSize
)
403 inline void PropertyTable::remove(const KeyType
& key
)
408 // returns the number of values in the hashtable.
409 inline unsigned PropertyTable::size() const
414 inline bool PropertyTable::isEmpty() const
419 inline unsigned PropertyTable::propertyStorageSize() const
421 return size() + (m_deletedOffsets
? m_deletedOffsets
->size() : 0);
424 inline void PropertyTable::clearDeletedOffsets()
426 m_deletedOffsets
.clear();
429 inline bool PropertyTable::hasDeletedOffset()
431 return m_deletedOffsets
&& !m_deletedOffsets
->isEmpty();
434 inline PropertyOffset
PropertyTable::getDeletedOffset()
436 PropertyOffset offset
= m_deletedOffsets
->last();
437 m_deletedOffsets
->removeLast();
441 inline void PropertyTable::addDeletedOffset(PropertyOffset offset
)
443 if (!m_deletedOffsets
)
444 m_deletedOffsets
= adoptPtr(new Vector
<PropertyOffset
>);
445 m_deletedOffsets
->append(offset
);
448 inline PropertyOffset
PropertyTable::nextOffset(PropertyOffset inlineCapacity
)
450 if (hasDeletedOffset())
451 return getDeletedOffset();
453 return offsetForPropertyNumber(size(), inlineCapacity
);
456 inline PropertyTable
* PropertyTable::copy(VM
& vm
, JSCell
* owner
, unsigned newCapacity
)
458 ASSERT(newCapacity
>= m_keyCount
);
460 // Fast case; if the new table will be the same m_indexSize as this one, we can memcpy it,
461 // save rehashing all keys.
462 if (sizeForCapacity(newCapacity
) == m_indexSize
)
463 return PropertyTable::clone(vm
, owner
, *this);
464 return PropertyTable::clone(vm
, owner
, newCapacity
, *this);
468 inline size_t PropertyTable::sizeInMemory()
470 size_t result
= sizeof(PropertyTable
) + dataSize();
471 if (m_deletedOffsets
)
472 result
+= (m_deletedOffsets
->capacity() * sizeof(PropertyOffset
));
477 inline void PropertyTable::reinsert(const ValueType
& entry
)
479 // Used to insert a value known not to be in the table, and where
480 // we know capacity to be available.
482 find_iterator iter
= find(entry
.key
);
485 unsigned entryIndex
= usedCount() + 1;
486 m_index
[iter
.second
] = entryIndex
;
487 table()[entryIndex
- 1] = entry
;
492 inline void PropertyTable::rehash(unsigned newCapacity
)
494 unsigned* oldEntryIndices
= m_index
;
495 iterator iter
= this->begin();
496 iterator end
= this->end();
498 m_indexSize
= sizeForCapacity(newCapacity
);
499 m_indexMask
= m_indexSize
- 1;
502 m_index
= static_cast<unsigned*>(fastZeroedMalloc(dataSize()));
504 for (; iter
!= end
; ++iter
) {
509 fastFree(oldEntryIndices
);
512 inline unsigned PropertyTable::tableCapacity() const { return m_indexSize
>> 1; }
514 inline unsigned PropertyTable::deletedEntryIndex() const { return tableCapacity() + 1; }
517 inline T
* PropertyTable::skipDeletedEntries(T
* valuePtr
)
519 while (valuePtr
->key
== PROPERTY_MAP_DELETED_ENTRY_KEY
)
524 inline PropertyTable::ValueType
* PropertyTable::table()
526 // The table of values lies after the hash index.
527 return reinterpret_cast<ValueType
*>(m_index
+ m_indexSize
);
530 inline const PropertyTable::ValueType
* PropertyTable::table() const
532 // The table of values lies after the hash index.
533 return reinterpret_cast<const ValueType
*>(m_index
+ m_indexSize
);
536 inline unsigned PropertyTable::usedCount() const
538 // Total number of used entries in the values array - by either valid entries, or deleted ones.
539 return m_keyCount
+ m_deletedCount
;
542 inline size_t PropertyTable::dataSize()
544 // The size in bytes of data needed for by the table.
545 return m_indexSize
* sizeof(unsigned) + ((tableCapacity()) + 1) * sizeof(ValueType
);
548 inline unsigned PropertyTable::sizeForCapacity(unsigned capacity
)
550 if (capacity
< MinimumTableSize
/ 2)
551 return MinimumTableSize
;
552 return nextPowerOf2(capacity
+ 1) * 2;
555 inline bool PropertyTable::canInsert()
557 return usedCount() < tableCapacity();
562 #endif // PropertyMapHashTable_h