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
*);
190 * Recursively register {allocation, rootAPCHandle} with APCGCManager
192 static void RegisterUncountedAllocations(ArrayData
* ad
,
193 APCHandle
* rootAPCHandle
);
196 * Safe downcast helpers.
198 static SetArray
* asSet(ArrayData
* ad
);
199 static const SetArray
* asSet(const ArrayData
* ad
);
201 static ArrayData
* MakeSetFromAPC(const APCArray
*);
204 * For array initialization using KeysetInit.
206 static ArrayData
* AddToSet(ArrayData
*, int64_t);
207 static ArrayData
* AddToSetInPlace(ArrayData
*, int64_t);
208 static ArrayData
* AddToSet(ArrayData
*, StringData
*);
209 static ArrayData
* AddToSetInPlace(ArrayData
*, StringData
*);
212 static bool ClearElms(Elm
* elms
, uint32_t count
);
214 enum class AllocMode
: bool { Request
, Static
};
216 static SetArray
* CopySet(const SetArray
& other
, AllocMode
);
217 SetArray
* copySet() const { return CopySet(*this, AllocMode::Request
); }
219 template <typename Init
, IntishCast IC
>
220 static ArrayData
* ToArrayImpl(ArrayData
*, bool);
224 SetArray(const SetArray
&) = delete;
225 SetArray
& operator=(const SetArray
&) = delete;
226 ~SetArray() = delete;
228 //////////////////////////////////////////////////////////////////////
232 TypedValue
getElm(ssize_t ei
) const;
235 const TypedValue
* tvOfPos(uint32_t) const;
237 template <class F
, bool inc
= true>
238 static void Iterate(const SetArray
* a
, F fn
) {
239 if (inc
) a
->incRefCount();
240 SCOPE_EXIT
{ if (inc
) decRefArr(const_cast<SetArray
*>(a
)); };
241 auto const* elm
= a
->data();
242 for (auto i
= a
->m_used
; i
--; elm
++) {
243 if (LIKELY(!elm
->isTombstone())) {
244 if (ArrayData::call_helper(fn
, elm
->tv
)) break;
249 //////////////////////////////////////////////////////////////////////
253 static ArrayData
* EscalateForSort(ArrayData
* ad
, SortFunction
);
255 static void Sort(ArrayData
*, int, bool);
256 static void Ksort(ArrayData
*, int, bool);
257 static constexpr auto Asort
= &Ksort
;
259 static bool Usort(ArrayData
*, const Variant
&);
260 static bool Uksort(ArrayData
*, const Variant
&);
261 static constexpr auto Uasort
= &Uksort
;
264 template <typename AccessorT
>
265 SortFlavor
preSort(const AccessorT
& acc
, bool checkTypes
);
268 //////////////////////////////////////////////////////////////////////
272 bool checkInvariants() const;
275 * These functions require !isFull(). The index of the position
276 * where the element was inserted is returned.
278 void insert(int64_t k
, inthash_t h
);
279 void insert(int64_t k
);
280 void insert(StringData
* k
, strhash_t h
);
281 void insert(StringData
* k
);
283 ssize_t
findElm(const Elm
& e
) const;
288 * Append idx at the end of the linked list containing the set
291 void linkLast(uint32_t idx
);
294 * Helper routine for inserting elements into a new array
295 * when grow()ing the array, that also checks for potentially
296 * unbalanced entries because of hash collision.
298 SetArray
* insertCheckUnbalanced(SetArray
* ad
, Elm
* table
,
300 Elm
* iter
, Elm
* stop
);
304 * Returns a copy of the set with twice the scale of the original. It
305 * rebuilds the hash table, but it does not compact the elements. If copy is
306 * true, it will copy elements instead of taking ownership of them.
308 SetArray
* grow(bool copy
);
311 * prepareForInsert ensures that the set has room to insert an element and
312 * has a refcount of 1, copying if requested and growing if needed.
314 SetArray
* prepareForInsert(bool copy
);
317 * compact() removes all tombstones from the hash table by going
318 * through the inner linked list.
325 bool isZombie() const { return m_used
+ 1 == 0; }
326 void setZombie() { m_used
= -uint32_t{1}; }
331 static bool EqualHelper(const ArrayData
*, const ArrayData
*, bool);
333 //////////////////////////////////////////////////////////////////////
338 //////////////////////////////////////////////////////////////////////
339 // JIT Supporting Routines
341 static constexpr ptrdiff_t tvOff(uint32_t pos
) {
342 return dataOff() + pos
* sizeof(Elm
) + offsetof(Elm
, tv
);
345 //////////////////////////////////////////////////////////////////////
346 // Reference Counting
349 using ArrayData::decRefCount
;
350 using ArrayData::hasMultipleRefs
;
351 using ArrayData::hasExactlyOneRef
;
352 using ArrayData::decWillRelease
;
353 using ArrayData::incRefCount
;
355 //////////////////////////////////////////////////////////////////////
356 // Misc ArrayData Methods
359 * These using directives ensure the full set of overloaded functions
360 * are visible in this class, to avoid triggering implicit conversions
361 * from a const Variant& key to int64.
364 using ArrayData::exists
;
366 using ArrayData::rval
;
367 using ArrayData::lval
;
368 using ArrayData::set
;
369 using ArrayData::remove
;
370 using ArrayData::release
;
372 //////////////////////////////////////////////////////////////////////
376 friend struct array::HashTable
<SetArray
, SetArrayElm
>;
377 friend struct MemoryProfile
;
378 friend struct jit::ArrayAccessProfile
;
379 friend struct EmptyArray
;
380 friend struct PackedArray
;
381 friend struct StructArray
;
382 friend struct MixedArray
;
383 friend struct HashCollection
;
384 friend struct BaseMap
;
386 friend struct c_ImmMap
;
387 friend struct BaseSet
;
389 friend struct c_ImmSet
;
390 friend struct c_AwaitAllWaitHandle
;
392 friend size_t getMemSize(const ArrayData
*, bool);
393 template <typename AccessorT
, class ArrayT
>
394 friend SortFlavor
genericPreSort(ArrayT
&, const AccessorT
&, bool);
396 //////////////////////////////////////////////////////////////////////
400 static size_t Vsize(const ArrayData
*);
401 static TypedValue
GetPosVal(const ArrayData
*, ssize_t
);
402 static bool IsVectorData(const ArrayData
*);
403 static bool ExistsInt(const ArrayData
*, int64_t);
404 static bool ExistsStr(const ArrayData
*, const StringData
*);
405 static arr_lval
LvalInt(ArrayData
*, int64_t, bool);
406 static arr_lval
LvalStr(ArrayData
*, StringData
*, bool);
407 static ArrayData
* SetInt(ArrayData
*, int64_t, TypedValue
);
408 static constexpr auto SetIntMove
= &SetInt
;
409 static ArrayData
* SetStr(ArrayData
*, StringData
*, TypedValue
);
410 static constexpr auto SetStrMove
= &SetStr
;
411 static ArrayData
* RemoveInt(ArrayData
*, int64_t);
412 static ArrayData
* RemoveStr(ArrayData
*, const StringData
*);
413 static ArrayData
* Copy(const ArrayData
*);
414 static ArrayData
* CopyStatic(const ArrayData
*);
415 static ArrayData
* Append(ArrayData
*, TypedValue
);
416 static ArrayData
* PlusEq(ArrayData
*, const ArrayData
*);
417 static ArrayData
* Merge(ArrayData
*, const ArrayData
*);
418 static ArrayData
* Pop(ArrayData
*, Variant
&);
419 static ArrayData
* Dequeue(ArrayData
*, Variant
&);
420 static ArrayData
* Prepend(ArrayData
*, TypedValue
);
421 static void Renumber(ArrayData
*);
422 static void OnSetEvalScalar(ArrayData
*);
423 static constexpr auto ToDict
= &ArrayCommon::ToDict
;
424 static constexpr auto ToVec
= &ArrayCommon::ToVec
;
425 static ArrayData
* ToPHPArray(ArrayData
*, bool);
426 static ArrayData
* ToPHPArrayIntishCast(ArrayData
*, bool);
427 static ArrayData
* ToKeyset(ArrayData
*, bool);
428 static constexpr auto ToVArray
= &ArrayCommon::ToVArray
;
429 static ArrayData
* ToDArray(ArrayData
*, bool);
430 static bool Equal(const ArrayData
*, const ArrayData
*);
431 static bool NotEqual(const ArrayData
*, const ArrayData
*);
432 static bool Same(const ArrayData
*, const ArrayData
*);
433 static bool NotSame(const ArrayData
*, const ArrayData
*);
435 //////////////////////////////////////////////////////////////////////
439 static ArrayData
* RemoveImpl(ArrayData
*, K key
, bool, SetArrayElm::hash_t
);
440 static ArrayData
* AppendImpl(ArrayData
*, TypedValue
, bool);
444 static Initializer s_initializer
;
449 HASH_TABLE_CHECK_OFFSETS(SetArray
, SetArrayElm
)
450 //////////////////////////////////////////////////////////////////////
454 #endif // incl_HPHP_SET_ARRAY_H_