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
25 #include "WriteBarrier.h"
26 #include <wtf/HashTable.h>
27 #include <wtf/PassOwnPtr.h>
28 #include <wtf/Vector.h>
32 #define DUMP_PROPERTYMAP_STATS 0
34 #define DUMP_PROPERTYMAP_STATS 0
37 #if DUMP_PROPERTYMAP_STATS
40 extern int numCollisions
;
41 extern int numRehashes
;
42 extern int numRemoves
;
46 #define PROPERTY_MAP_DELETED_ENTRY_KEY ((StringImpl*)1)
50 inline bool isPowerOf2(unsigned v
)
52 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
54 return !(v
& (v
- 1)) && v
;
57 inline unsigned nextPowerOf2(unsigned v
)
59 // Taken from http://www.cs.utk.edu/~vose/c-stuff/bithacks.html
60 // Devised by Sean Anderson, Sepember 14, 2001
73 struct PropertyMapEntry
{
77 WriteBarrier
<JSCell
> specificValue
;
79 PropertyMapEntry(JSGlobalData
& globalData
, JSCell
* owner
, StringImpl
* key
, unsigned offset
, unsigned attributes
, JSCell
* specificValue
)
82 , attributes(attributes
)
83 , specificValue(globalData
, owner
, specificValue
, WriteBarrier
<JSCell
>::MayBeNull
)
89 WTF_MAKE_FAST_ALLOCATED
;
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 typedef StringImpl
* KeyType
;
133 typedef PropertyMapEntry ValueType
;
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
;
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
;
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
&);
150 // Ordered iteration methods.
153 const_iterator
begin() const;
154 const_iterator
end() const;
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
);
164 // Returns the number of values in the hashtable.
165 unsigned size() const;
167 // Checks if there are any values in the hashtable.
168 bool isEmpty() const;
170 // Number of slots in the property storage array in use, included deletedOffsets.
171 unsigned propertyStorageSize() const;
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
);
179 // Copy this PropertyTable, ensuring the copy has at least the capacity provided.
180 PassOwnPtr
<PropertyTable
> copy(JSGlobalData
&, JSCell
* owner
, unsigned newCapacity
);
183 size_t sizeInMemory();
184 void checkConsistency();
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
);
192 // Rehash the table. Used to grow, or to recover deleted slots.
193 void rehash(unsigned newCapacity
);
195 // The capacity of the table of values is half of the size of the index.
196 unsigned tableCapacity() const;
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;
207 // Used in iterator creation/progression.
209 static T
* skipDeletedEntries(T
* valuePtr
);
211 // The table of values lies after the hash index.
213 const ValueType
* table() const;
215 // total number of used entries in the values array - by either valid entries, or deleted ones.
216 unsigned usedCount() const;
218 // The size in bytes of data needed for by the table.
221 // Calculates the appropriate table size (rounds up to a power of two).
222 static unsigned sizeForCapacity(unsigned capacity
);
224 // Check if capacity is available.
227 unsigned m_indexSize
;
228 unsigned m_indexMask
;
231 unsigned m_deletedCount
;
232 OwnPtr
< Vector
<unsigned> > m_deletedOffsets
;
234 static const unsigned MinimumTableSize
= 16;
235 static const unsigned EmptyEntryIndex
= 0;
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())))
245 ASSERT(isPowerOf2(m_indexSize
));
248 inline PropertyTable::PropertyTable(JSGlobalData
&, 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
)
255 ASSERT(isPowerOf2(m_indexSize
));
257 memcpy(m_index
, other
.m_index
, dataSize());
259 iterator end
= this->end();
260 for (iterator iter
= begin(); iter
!= end
; ++iter
) {
262 Heap::writeBarrier(owner
, iter
->specificValue
.get());
265 // Copy the m_deletedOffsets vector.
266 Vector
<unsigned>* otherDeletedOffsets
= other
.m_deletedOffsets
.get();
267 if (otherDeletedOffsets
)
268 m_deletedOffsets
= adoptPtr(new Vector
<unsigned>(*otherDeletedOffsets
));
271 inline PropertyTable::PropertyTable(JSGlobalData
&, 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())))
278 ASSERT(isPowerOf2(m_indexSize
));
279 ASSERT(initialCapacity
>= other
.m_keyCount
);
281 const_iterator end
= other
.end();
282 for (const_iterator iter
= other
.begin(); iter
!= end
; ++iter
) {
286 Heap::writeBarrier(owner
, iter
->specificValue
.get());
289 // Copy the m_deletedOffsets vector.
290 Vector
<unsigned>* otherDeletedOffsets
= other
.m_deletedOffsets
.get();
291 if (otherDeletedOffsets
)
292 m_deletedOffsets
= adoptPtr(new Vector
<unsigned>(*otherDeletedOffsets
));
295 inline PropertyTable::~PropertyTable()
297 iterator end
= this->end();
298 for (iterator iter
= begin(); iter
!= end
; ++iter
)
304 inline PropertyTable::iterator
PropertyTable::begin()
306 return iterator(skipDeletedEntries(table()));
309 inline PropertyTable::iterator
PropertyTable::end()
311 return iterator(table() + usedCount());
314 inline PropertyTable::const_iterator
PropertyTable::begin() const
316 return const_iterator(skipDeletedEntries(table()));
319 inline PropertyTable::const_iterator
PropertyTable::end() const
321 return const_iterator(table() + usedCount());
324 inline PropertyTable::find_iterator
PropertyTable::find(const KeyType
& key
)
327 unsigned hash
= key
->existingHash();
330 #if DUMP_PROPERTYMAP_STATS
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
);
341 #if DUMP_PROPERTYMAP_STATS
346 step
=WTF::doubleHash(key
->existingHash()) | 1;
349 #if DUMP_PROPERTYMAP_STATS
355 inline std::pair
<PropertyTable::find_iterator
, bool> PropertyTable::add(const ValueType
& entry
)
357 // Look for a value with a matching key already in the array.
358 find_iterator iter
= find(entry
.key
);
360 return std::make_pair(iter
, false);
365 // ensure capacity is available.
367 rehash(m_keyCount
+ 1);
368 iter
= find(entry
.key
);
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];
379 return std::make_pair(iter
, true);
382 inline void PropertyTable::remove(const find_iterator
& iter
)
384 // Removing a key that doesn't exist does nothing!
388 #if DUMP_PROPERTYMAP_STATS
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
;
398 ASSERT(m_keyCount
>= 1);
402 if (m_deletedCount
* 4 >= m_indexSize
)
406 inline void PropertyTable::remove(const KeyType
& key
)
411 // returns the number of values in the hashtable.
412 inline unsigned PropertyTable::size() const
417 inline bool PropertyTable::isEmpty() const
422 inline unsigned PropertyTable::propertyStorageSize() const
424 return size() + (m_deletedOffsets
? m_deletedOffsets
->size() : 0);
427 inline void PropertyTable::clearDeletedOffsets()
429 m_deletedOffsets
.clear();
432 inline bool PropertyTable::hasDeletedOffset()
434 return m_deletedOffsets
&& !m_deletedOffsets
->isEmpty();
437 inline unsigned PropertyTable::getDeletedOffset()
439 unsigned offset
= m_deletedOffsets
->last();
440 m_deletedOffsets
->removeLast();
444 inline void PropertyTable::addDeletedOffset(unsigned offset
)
446 if (!m_deletedOffsets
)
447 m_deletedOffsets
= adoptPtr(new Vector
<unsigned>);
448 m_deletedOffsets
->append(offset
);
451 inline PassOwnPtr
<PropertyTable
> PropertyTable::copy(JSGlobalData
& globalData
, JSCell
* owner
, unsigned newCapacity
)
453 ASSERT(newCapacity
>= m_keyCount
);
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 adoptPtr(new PropertyTable(globalData
, owner
, *this));
459 return adoptPtr(new PropertyTable(globalData
, owner
, newCapacity
, *this));
463 inline size_t PropertyTable::sizeInMemory()
465 size_t result
= sizeof(PropertyTable
) + dataSize();
466 if (m_deletedOffsets
)
467 result
+= (m_deletedOffsets
->capacity() * sizeof(unsigned));
472 inline void PropertyTable::reinsert(const ValueType
& entry
)
474 // Used to insert a value known not to be in the table, and where
475 // we know capacity to be available.
477 find_iterator iter
= find(entry
.key
);
480 unsigned entryIndex
= usedCount() + 1;
481 m_index
[iter
.second
] = entryIndex
;
482 table()[entryIndex
- 1] = entry
;
487 inline void PropertyTable::rehash(unsigned newCapacity
)
489 unsigned* oldEntryIndices
= m_index
;
490 iterator iter
= this->begin();
491 iterator end
= this->end();
493 m_indexSize
= sizeForCapacity(newCapacity
);
494 m_indexMask
= m_indexSize
- 1;
497 m_index
= static_cast<unsigned*>(fastZeroedMalloc(dataSize()));
499 for (; iter
!= end
; ++iter
) {
504 fastFree(oldEntryIndices
);
507 inline unsigned PropertyTable::tableCapacity() const { return m_indexSize
>> 1; }
509 inline unsigned PropertyTable::deletedEntryIndex() const { return tableCapacity() + 1; }
512 inline T
* PropertyTable::skipDeletedEntries(T
* valuePtr
)
514 while (valuePtr
->key
== PROPERTY_MAP_DELETED_ENTRY_KEY
)
519 inline PropertyTable::ValueType
* PropertyTable::table()
521 // The table of values lies after the hash index.
522 return reinterpret_cast<ValueType
*>(m_index
+ m_indexSize
);
525 inline const PropertyTable::ValueType
* PropertyTable::table() const
527 // The table of values lies after the hash index.
528 return reinterpret_cast<const ValueType
*>(m_index
+ m_indexSize
);
531 inline unsigned PropertyTable::usedCount() const
533 // Total number of used entries in the values array - by either valid entries, or deleted ones.
534 return m_keyCount
+ m_deletedCount
;
537 inline size_t PropertyTable::dataSize()
539 // The size in bytes of data needed for by the table.
540 return m_indexSize
* sizeof(unsigned) + ((tableCapacity()) + 1) * sizeof(ValueType
);
543 inline unsigned PropertyTable::sizeForCapacity(unsigned capacity
)
546 return MinimumTableSize
;
547 return nextPowerOf2(capacity
+ 1) * 2;
550 inline bool PropertyTable::canInsert()
552 return usedCount() < tableCapacity();
557 #endif // PropertyMapHashTable_h