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 +----------------------------------------------------------------------+
17 #ifndef incl_HPHP_SET_ARRAY_H_
18 #define incl_HPHP_SET_ARRAY_H_
20 #include "hphp/runtime/base/array-data.h"
21 #include "hphp/runtime/base/array-common.h"
22 #include "hphp/runtime/base/data-walker.h"
23 #include "hphp/runtime/base/hash-table.h"
24 #include "hphp/runtime/base/tv-val.h"
25 #include "hphp/runtime/base/string-data.h"
26 #include "hphp/runtime/base/tv-mutate.h"
27 #include "hphp/runtime/base/typed-value.h"
29 #include <folly/portability/Constexpr.h>
33 //////////////////////////////////////////////////////////////////////
36 struct ArrayAccessProfile
;
42 using hash_t
= strhash_t
;
44 * We store elements of the set here, but also some information
45 * local to this array: tv.m_aux.u_hash contains either a negative
46 * number (for an int key) or a string hashcode (31-bit and thus
47 * non-negative). The field tv.m_type == kInvalidDataType is used
48 * to mark a deleted element (tombstone); tv.m_type == KindOfUninit
49 * is used for debugging to mark empty elements.
53 static auto constexpr kTombstone
= kInvalidDataType
;
54 static auto constexpr kEmpty
= KindOfUninit
;
56 void setStrKey(StringData
* k
, strhash_t h
) {
59 tv
.m_type
= KindOfString
;
62 assertx(!isInvalid());
65 void setIntKey(int64_t k
, inthash_t h
) {
67 tv
.m_type
= KindOfInt64
;
69 tv
.hash() = h
| STRHASH_MSB
;
70 assertx(!isInvalid());
75 tv
.m_type
= kTombstone
;
76 static_assert(!isRefcountedType(kTombstone
), "");
79 bool isEmpty() const {
80 return tv
.m_type
== kEmpty
;
83 // Members below here are required for HashTable implemenation.
84 ALWAYS_INLINE
const TypedValue
* datatv() const {
88 ALWAYS_INLINE
bool hasStrKey() const {
90 * Currently string hash is 31-bit, thus it saves us some
91 * instructions to encode int keys as a negative hash, so
92 * that we don't have to care about the MSB when working
95 assertx(!isInvalid());
96 return tv
.hash() >= 0;
99 ALWAYS_INLINE StringData
* strKey() const {
100 assertx(hasStrKey());
101 return tv
.m_data
.pstr
;
104 ALWAYS_INLINE
bool hasIntKey() const {
105 assertx(!isInvalid());
106 return tv
.hash() < 0;
109 ALWAYS_INLINE
int64_t intKey() const {
110 return tv
.m_data
.num
;
113 ALWAYS_INLINE TypedValue
getKey() const {
114 assertx(!isInvalid());
118 ALWAYS_INLINE hash_t
hash() const {
122 ALWAYS_INLINE
bool isTombstone() const {
123 return tv
.m_type
== kTombstone
;
126 ALWAYS_INLINE
bool isInvalid() const {
127 return tv
.m_type
== kEmpty
|| isTombstone();
130 static constexpr ptrdiff_t keyOff() {
131 return offsetof(SetArrayElm
, tv
) + offsetof(TypedValue
, m_data
.pstr
);
133 static constexpr ptrdiff_t dataOff() {
134 return offsetof(SetArrayElm
, tv
) + offsetof(TypedValue
, m_data
);
136 static constexpr ptrdiff_t typeOff() {
137 return offsetof(SetArrayElm
, tv
) + offsetof(TypedValue
, m_type
);
139 static constexpr ptrdiff_t hashOff() {
140 return offsetof(SetArrayElm
, tv
) + offsetof(TypedValue
, m_aux
);
144 //////////////////////////////////////////////////////////////////////
146 struct SetArray final
: ArrayData
,
147 array::HashTable
<SetArray
, SetArrayElm
>,
148 type_scan::MarkCollectable
<SetArray
> {
150 //////////////////////////////////////////////////////////////////////
154 void scan(type_scan::Scanner
& scanner
) const {
155 auto const elms
= data();
156 scanner
.scan(*elms
, m_used
* sizeof(*elms
));
159 //////////////////////////////////////////////////////////////////////
160 // Initialization, Copies, and Conversions
164 * Allocate a new, empty, request-local set array, with enough space
165 * reserved for `size' members.
167 * The returned array is already incref'd.
169 static ArrayData
* MakeReserveSet(uint32_t size
);
170 static constexpr auto MakeReserve
= &MakeReserveSet
;
172 static ArrayData
* MakeSet(uint32_t size
, const TypedValue
* values
);
175 * Allocate an uncounted SetArray and copy the values from the
176 * input 'array' into the uncounted one.
178 * If withApcTypedValue is true, space for an APCTypedValue will be
179 * allocated in front of the returned pointer.
181 static ArrayData
* MakeUncounted(ArrayData
* array
,
182 bool withApcTypedValue
= false,
183 DataWalker::PointerMap
* m
= nullptr);
184 static ArrayData
* MakeUncounted(ArrayData
* array
, int) = delete;
185 static ArrayData
* MakeUncounted(ArrayData
* array
, size_t) = delete;
187 static void Release(ArrayData
*);
188 static void ReleaseUncounted(ArrayData
*);
191 * Safe downcast helpers.
193 static SetArray
* asSet(ArrayData
* ad
);
194 static const SetArray
* asSet(const ArrayData
* ad
);
196 static ArrayData
* MakeSetFromAPC(const APCArray
*);
199 * For array initialization using KeysetInit.
201 static ArrayData
* AddToSet(ArrayData
*, int64_t);
202 static ArrayData
* AddToSetInPlace(ArrayData
*, int64_t);
203 static ArrayData
* AddToSet(ArrayData
*, StringData
*);
204 static ArrayData
* AddToSetInPlace(ArrayData
*, StringData
*);
207 static bool ClearElms(Elm
* elms
, uint32_t count
);
209 enum class AllocMode
: bool { Request
, Static
};
211 static SetArray
* CopySet(const SetArray
& other
, AllocMode
);
212 SetArray
* copySet() const { return CopySet(*this, AllocMode::Request
); }
214 static ArrayData
* ToDArrayImpl(const SetArray
*);
218 SetArray(const SetArray
&) = delete;
219 SetArray
& operator=(const SetArray
&) = delete;
220 ~SetArray() = delete;
222 //////////////////////////////////////////////////////////////////////
226 TypedValue
getElm(ssize_t ei
) const;
229 const TypedValue
* tvOfPos(uint32_t) const;
231 template <class F
, bool inc
= true>
232 static void Iterate(const SetArray
* a
, F fn
) {
233 if (inc
) a
->incRefCount();
234 SCOPE_EXIT
{ if (inc
) decRefArr(const_cast<SetArray
*>(a
)); };
235 auto const* elm
= a
->data();
236 for (auto i
= a
->m_used
; i
--; elm
++) {
237 if (LIKELY(!elm
->isTombstone())) {
238 if (ArrayData::call_helper(fn
, elm
->tv
)) break;
243 //////////////////////////////////////////////////////////////////////
247 static ArrayData
* EscalateForSort(ArrayData
* ad
, SortFunction
);
249 static void Sort(ArrayData
*, int, bool);
250 static void Ksort(ArrayData
*, int, bool);
251 static constexpr auto Asort
= &Ksort
;
253 static bool Usort(ArrayData
*, const Variant
&);
254 static bool Uksort(ArrayData
*, const Variant
&);
255 static constexpr auto Uasort
= &Uksort
;
258 template <typename AccessorT
>
259 SortFlavor
preSort(const AccessorT
& acc
, bool checkTypes
);
262 //////////////////////////////////////////////////////////////////////
266 bool checkInvariants() const;
269 * These functions require !isFull(). The index of the position
270 * where the element was inserted is returned.
272 void insert(int64_t k
, inthash_t h
);
273 void insert(int64_t k
);
274 void insert(StringData
* k
, strhash_t h
);
275 void insert(StringData
* k
);
277 ssize_t
findElm(const Elm
& e
) const;
279 void erase(RemovePos
);
282 * Append idx at the end of the linked list containing the set
285 void linkLast(uint32_t idx
);
288 * Helper routine for inserting elements into a new array
289 * when grow()ing the array, that also checks for potentially
290 * unbalanced entries because of hash collision.
292 SetArray
* insertCheckUnbalanced(SetArray
* ad
, Elm
* table
,
294 Elm
* iter
, Elm
* stop
);
298 * Returns a copy of the set with twice the scale of the original. It
299 * rebuilds the hash table, but it does not compact the elements. If copy is
300 * true, it will copy elements instead of taking ownership of them.
302 SetArray
* grow(bool copy
);
305 * prepareForInsert ensures that the set has room to insert an element and
306 * has a refcount of 1, copying if requested and growing if needed.
308 SetArray
* prepareForInsert(bool copy
);
311 * compact() removes all tombstones from the hash table by going
312 * through the inner linked list.
319 bool isZombie() const { return m_used
+ 1 == 0; }
320 void setZombie() { m_used
= -uint32_t{1}; }
325 static bool EqualHelper(const ArrayData
*, const ArrayData
*, bool);
327 //////////////////////////////////////////////////////////////////////
332 //////////////////////////////////////////////////////////////////////
333 // JIT Supporting Routines
335 static constexpr ptrdiff_t tvOff() {
336 return offsetof(Elm
, tv
);
339 //////////////////////////////////////////////////////////////////////
340 // Reference Counting
343 using ArrayData::decRefCount
;
344 using ArrayData::hasMultipleRefs
;
345 using ArrayData::hasExactlyOneRef
;
346 using ArrayData::decWillRelease
;
347 using ArrayData::incRefCount
;
349 //////////////////////////////////////////////////////////////////////
350 // Misc ArrayData Methods
353 * These using directives ensure the full set of overloaded functions
354 * are visible in this class, to avoid triggering implicit conversions
355 * from a const Variant& key to int64.
358 using ArrayData::exists
;
360 using ArrayData::lval
;
361 using ArrayData::set
;
362 using ArrayData::remove
;
363 using ArrayData::release
;
365 //////////////////////////////////////////////////////////////////////
369 friend struct array::HashTable
<SetArray
, SetArrayElm
>;
370 friend struct MemoryProfile
;
371 friend struct jit::ArrayAccessProfile
;
372 friend struct PackedArray
;
373 friend struct MixedArray
;
374 friend struct HashCollection
;
375 friend struct BaseMap
;
377 friend struct c_ImmMap
;
378 friend struct BaseSet
;
380 friend struct c_ImmSet
;
381 friend struct c_AwaitAllWaitHandle
;
383 friend size_t getMemSize(const ArrayData
*, bool);
384 template <typename AccessorT
, class ArrayT
>
385 friend SortFlavor
genericPreSort(ArrayT
&, const AccessorT
&, bool);
387 //////////////////////////////////////////////////////////////////////
391 static TypedValue
GetPosVal(const ArrayData
*, ssize_t
);
392 static bool IsVectorData(const ArrayData
*);
393 static bool ExistsInt(const ArrayData
*, int64_t);
394 static bool ExistsStr(const ArrayData
*, const StringData
*);
395 static arr_lval
LvalInt(ArrayData
*, int64_t);
396 static arr_lval
LvalStr(ArrayData
*, StringData
*);
397 static ArrayData
* SetInt(ArrayData
*, int64_t, TypedValue
);
398 static constexpr auto SetIntMove
= &SetInt
;
399 static ArrayData
* SetStr(ArrayData
*, StringData
*, TypedValue
);
400 static constexpr auto SetStrMove
= &SetStr
;
401 static ArrayData
* RemoveInt(ArrayData
*, int64_t);
402 static ArrayData
* RemoveStr(ArrayData
*, const StringData
*);
403 static ArrayData
* Copy(const ArrayData
*);
404 static ArrayData
* CopyStatic(const ArrayData
*);
405 static ArrayData
* Append(ArrayData
*, TypedValue
);
406 static ArrayData
* Merge(ArrayData
*, const ArrayData
*);
407 static ArrayData
* Pop(ArrayData
*, Variant
&);
408 static ArrayData
* Dequeue(ArrayData
*, Variant
&);
409 static ArrayData
* Prepend(ArrayData
*, TypedValue
);
410 static ArrayData
* Renumber(ArrayData
*);
411 static void OnSetEvalScalar(ArrayData
*);
412 static constexpr auto ToDict
= &ArrayCommon::ToDict
;
413 static constexpr auto ToVec
= &ArrayCommon::ToVec
;
414 static ArrayData
* ToKeyset(ArrayData
*, bool);
415 static constexpr auto ToVArray
= &ArrayCommon::ToVArray
;
416 static ArrayData
* ToDArray(ArrayData
*, bool);
417 static bool Equal(const ArrayData
*, const ArrayData
*);
418 static bool NotEqual(const ArrayData
*, const ArrayData
*);
419 static bool Same(const ArrayData
*, const ArrayData
*);
420 static bool NotSame(const ArrayData
*, const ArrayData
*);
422 //////////////////////////////////////////////////////////////////////
426 static ArrayData
* RemoveImpl(ArrayData
*, K key
, bool, SetArrayElm::hash_t
);
427 static ArrayData
* AppendImpl(ArrayData
*, TypedValue
, bool);
431 static Initializer s_initializer
;
436 HASH_TABLE_CHECK_OFFSETS(SetArray
, SetArrayElm
)
437 //////////////////////////////////////////////////////////////////////
441 #endif // incl_HPHP_SET_ARRAY_H_