#ifndef MapData_h
#define MapData_h
#include "JSCell.h"
#include "WeakGCMapInlines.h"
#include <wtf/HashFunctions.h>
#include <wtf/HashMap.h>
#include <wtf/MathExtras.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/Vector.h>
namespace JSC {
class ExecState;
class VM;
template<typename Entry, typename JSIterator>
class MapDataImpl {
public:
enum : int32_t {
minimumMapSize = 8
};
class IteratorData {
public:
friend class MapDataImpl;
IteratorData(const MapDataImpl*);
bool next(WTF::KeyValuePair<JSValue, JSValue>&);
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;
};
struct KeyType {
ALWAYS_INLINE KeyType() { }
KeyType(JSValue);
JSValue value;
};
MapDataImpl(VM&);
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);
private:
typedef WTF::UnsignedWithZeroKeyHashTraits<int32_t> IndexTraits;
typedef HashMap<JSCell*, int32_t, typename WTF::DefaultHash<JSCell*>::Hash, WTF::HashTraits<JSCell*>, IndexTraits> CellKeyedMap;
typedef HashMap<EncodedJSValue, int32_t, EncodedJSValueHash, EncodedJSValueHashTraits, IndexTraits> ValueKeyedMap;
typedef HashMap<StringImpl*, int32_t, typename WTF::DefaultHash<StringImpl*>::Hash, WTF::HashTraits<StringImpl*>, IndexTraits> StringKeyedMap;
typedef HashMap<SymbolImpl*, int32_t, typename WTF::PtrHash<SymbolImpl*>, WTF::HashTraits<SymbolImpl*>, IndexTraits> SymbolKeyedMap;
size_t capacityInBytes() { return m_capacity * sizeof(Entry); }
ALWAYS_INLINE Entry* find(ExecState*, KeyType);
ALWAYS_INLINE Entry* add(ExecState*, JSCell* owner, KeyType);
template <typename Map, typename Key> 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;
Entry* m_entries;
WeakGCMap<JSIterator*, JSIterator> m_iterators;
};
template<typename Entry, typename JSIterator>
ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::MapDataImpl(VM& vm)
: m_capacity(0)
, m_size(0)
, m_deletedCount(0)
, m_entries(nullptr)
, m_iterators(vm)
{
}
template<typename Entry, typename JSIterator>
ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::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<int>(v.asDouble());
if (i != d)
value = v;
else
value = jsNumber(i);
}
template<typename Entry, typename JSIterator>
ALWAYS_INLINE MapDataImpl<Entry, JSIterator>::IteratorData::IteratorData(const MapDataImpl<Entry, JSIterator>* mapData)
: m_mapData(mapData)
, m_index(0)
{
}
template<typename Entry, typename JSIterator>
ALWAYS_INLINE bool MapDataImpl<Entry, JSIterator>::IteratorData::next(WTF::KeyValuePair<JSValue, JSValue>& pair)
{
if (!ensureSlot())
return false;
Entry* entry = &m_mapData->m_entries[m_index];
pair = WTF::KeyValuePair<JSValue, JSValue>(entry->key().get(), entry->value().get());
m_index += 1;
return true;
}
template<typename Entry, typename JSIterator>
ALWAYS_INLINE bool MapDataImpl<Entry, JSIterator>::IteratorData::ensureSlot() const
{
int32_t index = refreshCursor();
return static_cast<size_t>(index) < static_cast<size_t>(m_mapData->m_size);
}
template<typename Entry, typename JSIterator>
ALWAYS_INLINE int32_t MapDataImpl<Entry, JSIterator>::IteratorData::refreshCursor() const
{
if (isFinished())
return m_index;
Entry* entries = m_mapData->m_entries;
size_t end = m_mapData->m_size;
while (static_cast<size_t>(m_index) < end && !entries[m_index].key())
m_index++;
return m_index;
}
}
#endif