2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_PROPERTY_TABLE_H_
17 #define incl_HPHP_PROPERTY_TABLE_H_
19 #include "hphp/runtime/base/string-data.h"
25 // Hash table that preserves insertion order.
26 struct PropertyTable
{
28 PropertyTable(const PropertyTable
&);
31 PropertyTable
& operator=(const PropertyTable
&) = delete;
33 uint32_t add(const StringData
*);
34 uint32_t offsetFor(const StringData
*) const;
35 const StringData
* keyForOffset(uint32_t) const;
37 static constexpr uint32_t kInvalidOffset
= UINT_MAX
;
41 const StringData
* key
;
46 static uint32_t* baseToOffsets(void* base
);
48 const uint32_t* offsets() const;
50 static Entry
* baseToEntries(void* base
, size_t capacity
);
52 const Entry
* entries() const;
54 enum PropertyTypeTag
{
59 template<PropertyTypeTag
>
60 uint32_t bucketFor(const StringData
*);
61 template<PropertyTypeTag
>
62 uint32_t bucketFor(const StringData
*) const;
63 template<PropertyTypeTag
>
64 uint32_t bucketFor(const StringData
*, const Entry
*, size_t capacity
);
65 template<PropertyTypeTag
>
66 uint32_t bucketFor(const StringData
*, const Entry
*, size_t capacity
) const;
68 static size_t bytesForCapacity(size_t capacity
);
69 double loadFactor() const;
70 bool shouldGrow() const;
72 void rehash(void* oldBase
, size_t oldCap
, void* newBase
, size_t newCap
);
74 // The capacity below which we ignore the load factor. The table
75 // depends on having at least one free slot to signal the end of a hash
76 // lookup. If we always followed the load factor, table sizes below a
77 // certain limit would be guaranteed to fill completely, violating this
79 static constexpr size_t kLoadFactorCapacityThreshold
= 4;
80 static constexpr double kMaxLoadFactor
=
81 1.0 - (1.0/static_cast<double>(kLoadFactorCapacityThreshold
));
83 uint32_t m_nextOffset
;
89 inline size_t PropertyTable::bytesForCapacity(size_t capacity
) {
90 return capacity
* (sizeof(uint32_t) + sizeof(Entry
));
93 inline uint32_t* PropertyTable::baseToOffsets(void* base
) {
94 return static_cast<uint32_t*>(base
);
97 inline PropertyTable::Entry
* PropertyTable::baseToEntries(
101 return reinterpret_cast<Entry
*>(baseToOffsets(base
) + capacity
);
104 inline uint32_t* PropertyTable::offsets() {
105 return const_cast<uint32_t*>(
106 const_cast<const PropertyTable
*>(this)->baseToOffsets(m_base
));
109 inline const uint32_t* PropertyTable::offsets() const {
110 return baseToOffsets(m_base
);
113 inline PropertyTable::Entry
* PropertyTable::entries() {
114 return const_cast<PropertyTable::Entry
*>(
115 const_cast<const PropertyTable
*>(this)->entries());
118 inline const PropertyTable::Entry
* PropertyTable::entries() const {
119 return baseToEntries(m_base
, m_capacity
);
122 inline double PropertyTable::loadFactor() const {
123 return static_cast<double>(m_size
) / static_cast<double>(m_capacity
);
126 inline bool PropertyTable::shouldGrow() const {
127 if (m_capacity
>= kLoadFactorCapacityThreshold
) {
128 return loadFactor() >= kMaxLoadFactor
;
130 // We have to keep at least one extra slot free so that we're guaranteed to
131 // eventually hit it when doing the hash lookup.
132 return (m_size
+ 1) >= m_capacity
;
135 template<PropertyTable::PropertyTypeTag propType
>
136 inline uint32_t PropertyTable::bucketFor(const StringData
* property
) {
137 return bucketFor
<propType
>(property
, entries(), m_capacity
);
140 template<PropertyTable::PropertyTypeTag propType
>
141 inline uint32_t PropertyTable::bucketFor(const StringData
* property
) const {
142 return bucketFor
<propType
>(property
, entries(), m_capacity
);
145 inline const StringData
* PropertyTable::keyForOffset(uint32_t offset
) const {
146 if (offset
>= m_size
) return nullptr;
147 return entries()[offsets()[offset
]].key
;
150 inline uint32_t PropertyTable::offsetFor(const StringData
* property
) const {
151 auto bucketIndex
= property
->isStatic() ? bucketFor
<Static
>(property
)
152 : bucketFor
<NotStatic
>(property
);
153 auto& bucket
= entries()[bucketIndex
];
154 return bucket
.key
? bucket
.offset
: kInvalidOffset
;
157 template<PropertyTable::PropertyTypeTag propType
>
158 inline uint32_t PropertyTable::bucketFor(
159 const StringData
* property
,
160 const Entry
* entries
,
163 return const_cast<const PropertyTable
*>(this)->bucketFor
<propType
>(
164 property
, entries
, capacity
);
167 template<PropertyTable::PropertyTypeTag propType
>
168 inline uint32_t PropertyTable::bucketFor(
169 const StringData
* property
,
170 const Entry
* entries
,
173 assertx(propType
== NotStatic
|| property
->isStatic());
174 assertx(m_size
< m_capacity
);
175 auto hash
= property
->hash();
176 auto start
= hash
& (capacity
- 1);
177 auto current
= start
;
179 const auto key
= entries
[current
].key
;
181 if (key
== property
) break;
182 if (propType
== NotStatic
) {
183 if (entries
[current
].hash
== property
->hash()) {
184 if (key
->same(property
)) break;
187 current
= (current
+ 1) & (capacity
- 1);