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
39extern int numProbes;
40extern int numCollisions;
41extern int numRehashes;
42extern int numRemoves;
43
44#endif
45
46#define PROPERTY_MAP_DELETED_ENTRY_KEY ((StringImpl*)1)
47
48namespace JSC {
49
50inline 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
57inline 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
73struct PropertyMapEntry {
74    StringImpl* key;
75    unsigned offset;
76    unsigned attributes;
77    WriteBarrier<JSCell> specificValue;
78
79    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
88class 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
122        ordered_iterator(T* valuePtr)
123            : m_valuePtr(valuePtr)
124        {
125        }
126
127    private:
128        T* m_valuePtr;
129    };
130
131public:
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
187private:
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
238inline 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
248inline 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
271inline 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
295inline 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
304inline PropertyTable::iterator PropertyTable::begin()
305{
306    return iterator(skipDeletedEntries(table()));
307}
308
309inline PropertyTable::iterator PropertyTable::end()
310{
311    return iterator(table() + usedCount());
312}
313
314inline PropertyTable::const_iterator PropertyTable::begin() const
315{
316    return const_iterator(skipDeletedEntries(table()));
317}
318
319inline PropertyTable::const_iterator PropertyTable::end() const
320{
321    return const_iterator(table() + usedCount());
322}
323
324inline 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
355inline 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
382inline 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
406inline void PropertyTable::remove(const KeyType& key)
407{
408    remove(find(key));
409}
410
411// returns the number of values in the hashtable.
412inline unsigned PropertyTable::size() const
413{
414    return m_keyCount;
415}
416
417inline bool PropertyTable::isEmpty() const
418{
419    return !m_keyCount;
420}
421
422inline unsigned PropertyTable::propertyStorageSize() const
423{
424    return size() + (m_deletedOffsets ? m_deletedOffsets->size() : 0);
425}
426
427inline void PropertyTable::clearDeletedOffsets()
428{
429    m_deletedOffsets.clear();
430}
431
432inline bool PropertyTable::hasDeletedOffset()
433{
434    return m_deletedOffsets && !m_deletedOffsets->isEmpty();
435}
436
437inline unsigned PropertyTable::getDeletedOffset()
438{
439    unsigned offset = m_deletedOffsets->last();
440    m_deletedOffsets->removeLast();
441    return offset;
442}
443
444inline void PropertyTable::addDeletedOffset(unsigned offset)
445{
446    if (!m_deletedOffsets)
447        m_deletedOffsets.set(new Vector<unsigned>);
448    m_deletedOffsets->append(offset);
449}
450
451inline 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
463inline 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
472inline 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
487inline 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
507inline unsigned PropertyTable::tableCapacity() const { return m_indexSize >> 1; }
508
509inline unsigned PropertyTable::deletedEntryIndex() const { return tableCapacity() + 1; }
510
511template<typename T>
512inline T* PropertyTable::skipDeletedEntries(T* valuePtr)
513{
514    while (valuePtr->key == PROPERTY_MAP_DELETED_ENTRY_KEY)
515        ++valuePtr;
516    return valuePtr;
517}
518
519inline 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
525inline 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
531inline 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
537inline 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
543inline unsigned PropertyTable::sizeForCapacity(unsigned capacity)
544{
545    if (capacity < 8)
546        return MinimumTableSize;
547    return nextPowerOf2(capacity + 1) * 2;
548}
549
550inline bool PropertyTable::canInsert()
551{
552    return usedCount() < tableCapacity();
553}
554
555} // namespace JSC
556
557#endif // PropertyMapHashTable_h
558