// Copyright 2018 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_OBJECTS_ORDERED_HASH_TABLE_H_ #define V8_OBJECTS_ORDERED_HASH_TABLE_H_ #include "src/base/export-template.h" #include "src/common/globals.h" #include "src/objects/fixed-array.h" #include "src/objects/internal-index.h" #include "src/objects/js-objects.h" #include "src/objects/keys.h" #include "src/objects/smi.h" #include "src/roots/roots.h" // Has to be the last include (doesn't have include guards): #include "src/objects/object-macros.h" namespace v8 { namespace internal { // OrderedHashTable is a HashTable with Object keys that preserves // insertion order. There are Map and Set interfaces (OrderedHashMap // and OrderedHashTable, below). It is meant to be used by JSMap/JSSet. // // Only Object keys are supported, with Object::SameValueZero() used as the // equality operator and Object::GetHash() for the hash function. // // Based on the "Deterministic Hash Table" as described by Jason Orendorff at // https://wiki.mozilla.org/User:Jorend/Deterministic_hash_tables // Originally attributed to Tyler Close. // // Memory layout: // [0] : Prefix // [kPrefixSize]: element count // [kPrefixSize + 1]: deleted element count // [kPrefixSize + 2]: bucket count // [kPrefixSize + 3..(kPrefixSize + 3 + NumberOfBuckets() - 1)]: "hash table", // where each item is an offset into the // data table (see below) where the first // item in this bucket is stored. // [kPrefixSize + 3 + NumberOfBuckets()..length]: "data table", an // array of length Capacity() * kEntrySize, // where the first entrysize items are // handled by the derived class and the // item at kChainOffset is another entry // into the data table indicating the next // entry in this hash bucket. // // When we transition the table to a new version we obsolete it and reuse parts // of the memory to store information how to transition an iterator to the new // table: // // Memory layout for obsolete table: // [0] : Prefix // [kPrefixSize + 0]: Next newer table // [kPrefixSize + 1]: deleted element count or kClearedTableSentinel if // the table was cleared // [kPrefixSize + 2]: bucket count // [kPrefixSize + 3..(kPrefixSize + 3 + NumberOfDeletedElements() - 1)]: // The indexes of the removed holes. This part is only // usable for non-cleared tables, as clearing removes the // deleted elements count. // [kPrefixSize + 3 + NumberOfDeletedElements()..length]: Not used template <class Derived, int entrysize> class OrderedHashTable : public FixedArray { … }; class V8_EXPORT_PRIVATE OrderedHashSet : public OrderedHashTable<OrderedHashSet, 1> { … }; class V8_EXPORT_PRIVATE OrderedHashMap : public OrderedHashTable<OrderedHashMap, 2> { … }; // This is similar to the OrderedHashTable, except for the memory // layout where we use byte instead of Smi. The max capacity of this // is only 254, we transition to an OrderedHashTable beyond that // limit. // // Each bucket and chain value is a byte long. The padding exists so // that the DataTable entries start aligned. A bucket or chain value // of 255 is used to denote an unknown entry. // // The prefix size is calculated as the kPrefixSize * kTaggedSize. // // Memory layout: [ Prefix ] [ Header ] [ Padding ] [ DataTable ] [ HashTable ] // [ Chains ] // // The index are represented as bytes, on a 64 bit machine with // kEntrySize = 1, capacity = 4 and entries = 2: // // [ 0 ] : Prefix // // Note: For the sake of brevity, the following start with index 0 // but, they actually start from kPrefixSize * kTaggedSize to // account for the the prefix. // // [ Header ] : // [0] : Number of elements // [1] : Number of deleted elements // [2] : Number of buckets // // [ Padding ] : // [3 .. 7] : Padding // // [ DataTable ] : // [8 .. 15] : Entry 1 // [16 .. 23] : Entry 2 // [24 .. 31] : empty // [32 .. 39] : empty // // [ HashTable ] : // [40] : First chain-link for bucket 1 // [41] : empty // // [ Chains ] : // [42] : Next chain link for bucket 1 // [43] : empty // [44] : empty // [45] : empty // template <class Derived> class SmallOrderedHashTable : public HeapObject { … }; class SmallOrderedHashSet : public SmallOrderedHashTable<SmallOrderedHashSet> { … }; static_assert(kSmallOrderedHashSetMinCapacity == SmallOrderedHashSet::kMinCapacity); class SmallOrderedHashMap : public SmallOrderedHashTable<SmallOrderedHashMap> { … }; static_assert(kSmallOrderedHashMapMinCapacity == SmallOrderedHashMap::kMinCapacity); // TODO(gsathya): Rename this to OrderedHashTable, after we rename // OrderedHashTable to LargeOrderedHashTable. Also set up a // OrderedHashSetBase class as a base class for the two tables and use // that instead of a HeapObject here. template <class SmallTable, class LargeTable> class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) OrderedHashTableHandler { … }; extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap>; class V8_EXPORT_PRIVATE OrderedHashMapHandler : public OrderedHashTableHandler<SmallOrderedHashMap, OrderedHashMap> { … }; extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet>; class V8_EXPORT_PRIVATE OrderedHashSetHandler : public OrderedHashTableHandler<SmallOrderedHashSet, OrderedHashSet> { … }; class V8_EXPORT_PRIVATE OrderedNameDictionary : public OrderedHashTable<OrderedNameDictionary, 3> { … }; extern template class EXPORT_TEMPLATE_DECLARE(V8_EXPORT_PRIVATE) OrderedHashTableHandler<SmallOrderedNameDictionary, OrderedNameDictionary>; class V8_EXPORT_PRIVATE OrderedNameDictionaryHandler : public OrderedHashTableHandler<SmallOrderedNameDictionary, OrderedNameDictionary> { … }; class SmallOrderedNameDictionary : public SmallOrderedHashTable<SmallOrderedNameDictionary> { … }; } // namespace internal } // namespace v8 #include "src/objects/object-macros-undef.h" #endif // V8_OBJECTS_ORDERED_HASH_TABLE_H_