/* * Copyright (C) 2013, 2015-2016 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef MapData_h #define MapData_h #include "CopyBarrier.h" #include "JSCell.h" #include "WeakGCMapInlines.h" #include #include #include #include #include #include namespace JSC { class ExecState; class VM; template class MapDataImpl { public: enum : int32_t { minimumMapSize = 8 }; class IteratorData { public: friend class MapDataImpl; IteratorData(const MapDataImpl*); bool next(WTF::KeyValuePair&); // This function is called while packing a map's backing store. The // passed-in index is the new index the entry would have after packing. void didRemoveEntry(int32_t packedIndex) { if (isFinished()) return; if (m_index <= packedIndex) return; --m_index; } void didRemoveAllEntries() { if (isFinished()) return; m_index = 0; } void finish() { m_index = -1; } private: bool ensureSlot() const; bool isFinished() const { return m_index == -1; } int32_t refreshCursor() const; const MapDataImpl* m_mapData; mutable int32_t m_index; }; STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(IteratorData); struct KeyType { ALWAYS_INLINE KeyType() { } KeyType(JSValue); JSValue value; }; MapDataImpl(VM&, JSCell* owner); void set(ExecState*, JSCell* owner, KeyType, JSValue); JSValue get(ExecState*, KeyType); bool remove(ExecState*, KeyType); bool contains(ExecState*, KeyType); size_t size(ExecState*) const { return m_size - m_deletedCount; } IteratorData createIteratorData(JSIterator*); void clear(); void visitChildren(JSCell* owner, SlotVisitor&); void copyBackingStore(CopyVisitor&, CopyToken); size_t capacityInBytes() const { return m_capacity * sizeof(Entry); } private: typedef WTF::UnsignedWithZeroKeyHashTraits IndexTraits; typedef HashMap::Hash, WTF::HashTraits, IndexTraits> CellKeyedMap; typedef HashMap ValueKeyedMap; typedef HashMap::Hash, WTF::HashTraits, IndexTraits> StringKeyedMap; typedef HashMap, WTF::HashTraits, IndexTraits> SymbolKeyedMap; ALWAYS_INLINE Entry* find(ExecState*, KeyType); ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, KeyType); template ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, Map&, Key, KeyType); ALWAYS_INLINE bool shouldPack() const { return m_deletedCount; } CheckedBoolean ensureSpaceForAppend(ExecState*, JSCell* owner); ALWAYS_INLINE void replaceAndPackBackingStore(Entry* destination, int32_t newSize); ALWAYS_INLINE void replaceBackingStore(Entry* destination, int32_t newSize); CellKeyedMap m_cellKeyedTable; ValueKeyedMap m_valueKeyedTable; StringKeyedMap m_stringKeyedTable; SymbolKeyedMap m_symbolKeyedTable; int32_t m_capacity; int32_t m_size; int32_t m_deletedCount; JSCell* m_owner; CopyBarrier m_entries; WeakGCMap m_iterators; }; template ALWAYS_INLINE MapDataImpl::MapDataImpl(VM& vm, JSCell* owner) : m_capacity(0) , m_size(0) , m_deletedCount(0) , m_owner(owner) , m_iterators(vm) { } template ALWAYS_INLINE MapDataImpl::KeyType::KeyType(JSValue v) { if (!v.isDouble()) { value = v; return; } double d = v.asDouble(); if (std::isnan(d)) { value = v; return; } int i = static_cast(v.asDouble()); if (i != d) value = v; else value = jsNumber(i); } template ALWAYS_INLINE MapDataImpl::IteratorData::IteratorData(const MapDataImpl* mapData) : m_mapData(mapData) , m_index(0) { } template ALWAYS_INLINE bool MapDataImpl::IteratorData::next(WTF::KeyValuePair& pair) { if (!ensureSlot()) return false; Entry* entry = &m_mapData->m_entries.get()[m_index]; pair = WTF::KeyValuePair(entry->key().get(), entry->value().get()); m_index += 1; return true; } // This is a bit gnarly. We use an index of -1 to indicate the // finished state. By casting to unsigned we can immediately // test if both iterators are at the end of their iteration. template ALWAYS_INLINE bool MapDataImpl::IteratorData::ensureSlot() const { int32_t index = refreshCursor(); return static_cast(index) < static_cast(m_mapData->m_size); } template ALWAYS_INLINE int32_t MapDataImpl::IteratorData::refreshCursor() const { if (isFinished()) return m_index; Entry* entries = m_mapData->m_entries.get(); size_t end = m_mapData->m_size; while (static_cast(m_index) < end && !entries[m_index].key()) m_index++; return m_index; } } #endif /* !defined(MapData_h) */