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_ARRAY_H_
18 #define incl_HPHP_ARRAY_H_
20 #include "hphp/runtime/base/array-data.h"
21 #include "hphp/runtime/base/datatype.h"
22 #include "hphp/runtime/base/req-ptr.h"
23 #include "hphp/runtime/base/tv-type.h"
24 #include "hphp/runtime/base/tv-val.h"
25 #include "hphp/runtime/base/tv-variant.h"
26 #include "hphp/runtime/base/types.h"
32 ///////////////////////////////////////////////////////////////////////////////
34 // Forward declare to avoid including tv-conversions.h and creating a cycle.
35 template <IntishCast IC
= IntishCast::None
>
36 ArrayData
* tvCastToArrayLikeData(TypedValue tv
);
39 struct VariableUnserializer
;
41 ////////////////////////////////////////////////////////////////////////////////
43 enum class AccessFlags
{
47 ErrorKey
= Error
| Key
,
50 inline AccessFlags
operator&(AccessFlags a
, AccessFlags b
) {
51 return static_cast<AccessFlags
>(static_cast<int>(a
) & static_cast<int>(b
));
54 constexpr bool any(AccessFlags a
) {
55 return a
!= AccessFlags::None
;
58 ////////////////////////////////////////////////////////////////////////////////
61 * Array type wrapping ArrayData to implement reference counting, copy-on-write
62 * and ArrayData escalation.
64 * "Escalation" happens when an underlying ArrayData cannot handle an operation
65 * and instead it needs to "upgrade" itself to be a more general (but slower)
66 * type of ArrayData to accomplish the task.
70 using Ptr
= req::ptr
<ArrayData
>;
71 using NoIncRef
= Ptr::NoIncRef
;
72 using NonNull
= Ptr::NonNull
;
74 using Flags
= AccessFlags
;
78 * Create an empty array.
80 static Array
Create() {
81 return Array(ArrayData::Create(), NoIncRef
{});
85 * There are existing callsites that we intentionally want to create a
86 * "traditional" PHP array with no specialization. Array::CreatePHPArray()
87 * are calls that have been audited and determined that the callsite should
90 static constexpr auto CreatePHPArray
= &Create
;
92 static Array
CreateVec() {
93 return Array(ArrayData::CreateVec(), NoIncRef
{});
96 static Array
CreateDict() {
97 return Array(ArrayData::CreateDict(), NoIncRef
{});
100 static Array
CreateKeyset() {
101 return Array(ArrayData::CreateKeyset(), NoIncRef
{});
104 static Array
CreateVArray() {
105 return Array(ArrayData::CreateVArray(), NoIncRef
{});
108 static Array
CreateDArray() {
109 return Array(ArrayData::CreateDArray(), NoIncRef
{});
112 /////////////////////////////////////////////////////////////////////////////
118 * Take ownership of `ad'.
120 static ALWAYS_INLINE Array
attach(ArrayData
* ad
) {
121 return Array(ad
, NoIncRef
{});
125 * Transfer ownership of our underlying ArrayData.
127 ArrayData
* detach() { return m_arr
.detach(); }
130 * Get or null out the underlying ArrayData pointer.
132 ArrayData
* get() const { return m_arr
.get(); }
133 void reset(ArrayData
* arr
= nullptr) { m_arr
.reset(arr
); }
136 * Delegate to the underlying ArrayData.
138 * Deliberately doesn't throw_null_pointer_exception as a perf optimization.
140 ArrayData
* operator->() const { return m_arr
.get(); }
143 * "Copy" constructors.
145 explicit Array(ArrayData
* data
) : m_arr(data
) { }
146 Array(const Array
& arr
) : m_arr(arr
.m_arr
) { }
149 * Special constructor for use from ArrayInit that creates an Array without a
150 * null check and without an inc-ref.
152 enum class ArrayInitCtor
{ Tag
};
153 explicit Array(ArrayData
* ad
, ArrayInitCtor
)
154 : m_arr(ad
, NoIncRef
{})
160 Array(Array
&& src
) noexcept
: m_arr(std::move(src
.m_arr
)) { }
165 Array
& operator=(ArrayData
* data
) {
169 Array
& operator=(const Array
& arr
) {
173 Array
& operator=(const Variant
& v
);
178 Array
& operator=(Array
&& src
) {
179 m_arr
= std::move(src
.m_arr
);
182 Array
& operator=(Variant
&& v
);
184 #define COPY_BODY(meth, def) \
185 if (!m_arr) return def; \
186 auto new_arr = m_arr->meth; \
187 return new_arr != m_arr ? Array{new_arr, NoIncRef{}} : Array{*this};
190 * Make a copy of this array.
192 * Like the underlying ArrayData::copy operation, the returned Array may
193 * point to the same underlying array as the original, or a new one.
195 Array
copy() const { COPY_BODY(copy(), Array
{}) }
196 Array
toVec() const { COPY_BODY(toVec(true), CreateVec()) }
197 Array
toDict() const { COPY_BODY(toDict(true), CreateDict()) }
198 Array
toKeyset() const { COPY_BODY(toKeyset(true), CreateKeyset()) }
199 Array
toPHPArray() const { COPY_BODY(toPHPArray(true), Array
{}) }
200 Array
toPHPArrayIntishCast() const {
201 COPY_BODY(toPHPArrayIntishCast(true), Array
{})
203 Array
toVArray() const { COPY_BODY(toVArray(true), CreateVArray()) }
204 Array
toDArray() const { COPY_BODY(toDArray(true), CreateDArray()) }
208 /////////////////////////////////////////////////////////////////////////////
213 bool isNull() const { return !m_arr
; }
218 bool empty() const { return !m_arr
|| m_arr
->empty(); }
219 ssize_t
size() const { return m_arr
? m_arr
->size() : 0; }
220 ssize_t
length() const { return m_arr
? m_arr
->size() : 0; }
225 bool isVecArray() const { return m_arr
&& m_arr
->isVecArrayType(); }
226 bool isDict() const { return m_arr
&& m_arr
->isDictType(); }
227 bool isKeyset() const { return m_arr
&& m_arr
->isKeysetType(); }
228 bool isHackArray() const { return m_arr
&& m_arr
->isHackArrayType(); }
229 bool isPHPArray() const { return !m_arr
|| m_arr
->isPHPArrayType(); }
230 bool isVArray() const { return m_arr
&& m_arr
->isVArray(); }
231 bool isDArray() const { return m_arr
&& m_arr
->isDArray(); }
232 bool isVecOrVArray() const { return m_arr
&& m_arr
->isVecOrVArray(); }
233 bool isDictOrDArray() const { return m_arr
&& m_arr
->isDictOrDArray(); }
235 /////////////////////////////////////////////////////////////////////////////
240 * See array-iterator.h for end() and next().
242 ArrayIter
begin(const String
& context
= null_string
) const;
245 * Converts `k' to a valid key for this array kind.
247 template <IntishCast IC
= IntishCast::None
>
248 TypedValue
convertKey(TypedValue k
) const;
249 template <IntishCast IC
= IntishCast::None
>
250 TypedValue
convertKey(const Variant
& k
) const;
253 * Should int-like string keys be implicitly converted to integers before they
256 bool useWeakKeys() const {
257 // If array isn't set we may implicitly create a mixed array, which uses
258 // weak keys. We never implicitly create a Hack array.
259 return !m_arr
|| m_arr
->useWeakKeys();
263 * Convert the underlying ArrayData to a static copy of itself.
265 void setEvalScalar() const;
267 /////////////////////////////////////////////////////////////////////////////
271 * PHP array union operator.
273 Array
operator+(ArrayData
* data
) const;
274 Array
operator+(const Array
& v
) const;
275 Array
operator+(const Variant
& v
) const = delete;
276 Array
& operator+=(ArrayData
* data
);
277 Array
& operator+=(const Array
& v
);
278 Array
& operator+=(const Variant
& v
);
281 * Implementation of array_merge().
283 * This is different from operator+(), where existing keys' values are NOT
284 * modified. This function will actually override with new values.
286 * When merging a packed array with another packed array, new elements are
287 * always appended, and this is also different from operator+() where
288 * existing numeric indices are not modified.
290 Array
& merge(const Array
& arr
);
293 * Comparison function for array operations.
295 using PFUNC_CMP
= int (*)(const Variant
& v1
, const Variant
& v2
,
299 * Return the entries that have keys and/or values that are (intersect), or
300 * are not (diff) present in `array'.
302 * Keys and values can be compared by user supplied functions and `key_data'
303 * or `value_data' will be passed into the corresponding `cmp_function' as
304 * the `data' parameter. Otherwise, equal() will be called for comparisons.
305 * If both `by_key' and `by_value' are true, both keys and values have to
306 * match to be included (intersect) or excluded (diff).
308 Array
diff(const Variant
& array
, bool by_key
, bool by_value
,
309 PFUNC_CMP key_cmp_function
= nullptr,
310 const void* key_data
= nullptr,
311 PFUNC_CMP value_cmp_function
= nullptr,
312 const void* value_data
= nullptr) const;
313 Array
intersect(const Variant
& array
, bool by_key
, bool by_value
,
314 PFUNC_CMP key_cmp_function
= nullptr,
315 const void* key_data
= nullptr,
316 PFUNC_CMP value_cmp_function
= nullptr,
317 const void* value_data
= nullptr) const;
322 static int SortRegularAscending(const Variant
& v1
, const Variant
& v2
,
324 static int SortNumericAscending(const Variant
& v1
, const Variant
& v2
,
326 static int SortStringAscending(const Variant
& v1
, const Variant
& v2
,
328 static int SortStringAscendingCase(const Variant
& v1
, const Variant
& v2
,
330 static int SortLocaleStringAscending(const Variant
& v1
, const Variant
& v2
,
333 static int SortRegularDescending(const Variant
& v1
, const Variant
& v2
,
335 static int SortNumericDescending(const Variant
& v1
, const Variant
& v2
,
337 static int SortStringDescending(const Variant
& v1
, const Variant
& v2
,
339 static int SortStringDescendingCase(const Variant
& v1
, const Variant
& v2
,
341 static int SortLocaleStringDescending(const Variant
& v1
, const Variant
& v2
,
344 static int SortNaturalAscending(const Variant
& v1
, const Variant
& v2
,
346 static int SortNaturalDescending(const Variant
& v1
, const Variant
& v2
,
348 static int SortNaturalCaseAscending(const Variant
& v1
, const Variant
& v2
,
350 static int SortNaturalCaseDescending(const Variant
& v1
, const Variant
& v2
,
353 void sort(PFUNC_CMP cmp_func
, bool by_key
, bool renumber
,
354 const void* data
= nullptr);
357 * Sort multiple arrays at once similar to how ORDER BY clause works in SQL.
365 std::vector
<ssize_t
> positions
;
367 static bool MultiSort(std::vector
<SortData
>& data
);
369 static void SortImpl(std::vector
<int>& indices
, const Array
& source
,
370 Array::SortData
& opaque
,
371 Array::PFUNC_CMP cmp_func
,
372 bool by_key
, const void* data
= nullptr);
377 bool toBoolean() const { return m_arr
&& !m_arr
->empty(); }
378 int8_t toByte() const { return toBoolean() ? 1 : 0; }
379 int16_t toInt16() const { return toBoolean() ? 1 : 0; }
380 int32_t toInt32() const { return toBoolean() ? 1 : 0; }
381 int64_t toInt64() const { return toBoolean() ? 1 : 0; }
382 double toDouble() const { return toBoolean() ? 1.0 : 0.0; }
383 String
toString() const;
386 * Enable the legacy behavior bit on this array
388 void setLegacyArray(bool);
393 bool same(const Array
& v2
) const;
394 bool equal(const Array
& v2
) const;
395 bool less(const Array
& v2
, bool flip
= false) const;
396 bool less(const Variant
& v2
) const;
397 bool more(const Array
& v2
, bool flip
= true) const;
398 bool more(const Variant
& v2
) const;
399 int compare(const Array
& v2
, bool flip
= false) const;
401 /////////////////////////////////////////////////////////////////////////////
402 // Element rval/lval.
404 #define FOR_EACH_KEY_TYPE(...) \
405 C(TypedValue, __VA_ARGS__) \
406 I(int, __VA_ARGS__) \
407 I(int64_t, __VA_ARGS__) \
408 V(const String&, __VA_ARGS__) \
409 V(const Variant&, __VA_ARGS__) \
410 D(double, __VA_ARGS__)
413 * Get a refcounted copy of the element at `key'.
415 Variant
operator[](TypedValue key
) const;
416 Variant
operator[](int key
) const;
417 Variant
operator[](int64_t key
) const;
418 Variant
operator[](const String
& key
) const;
419 Variant
operator[](const Variant
& key
) const;
420 Variant
operator[](double key
) const = delete;
421 Variant
operator[](const char*) const = delete;
423 #define C(key_t, name, ret_t, cns) \
424 ret_t name(key_t, Flags = Flags::None) cns;
427 #define D(key_t, name, ret_t, cns) \
428 ret_t name(key_t, Flags = Flags::None) cns = delete;
431 * Get an rval to the element at `key'.
433 FOR_EACH_KEY_TYPE(rval
, tv_rval
, const)
436 * Get an lval to the element at `key'.
438 * This is ArrayData::lval, with COW and escalation handled internally.
440 * lvalForce() has the legacy lval() behavior---if the key is not present,
441 * it writes null, then returns the lval.
443 FOR_EACH_KEY_TYPE(lval
, tv_lval
, )
444 FOR_EACH_KEY_TYPE(lvalForce
, tv_lval
, )
451 /////////////////////////////////////////////////////////////////////////////
452 // Element access and mutation.
454 #define C(key_t, ret_t, name, cns) ret_t name(key_t, bool isKey = false) cns;
456 #define I(key_t, ret_t, name, cns) ret_t name(key_t) cns;
457 #define D(key_t, ret_t, name, cns) ret_t name(key_t) cns = delete;
462 FOR_EACH_KEY_TYPE(bool, exists
, const)
467 FOR_EACH_KEY_TYPE(void, remove
, )
474 #define C(key_t, name, value_t) \
475 void name(key_t k, value_t v, bool isKey = false);
477 #define I(key_t, name, value_t) void name(key_t k, value_t v);
478 #define D(key_t, name, value_t) void name(key_t k, value_t v) = delete;
481 * Set an element to `v'.
483 FOR_EACH_KEY_TYPE(set
, TypedValue
)
490 #define C(key_t, name, value_t)
491 #define V(key_t, name, value_t) \
492 void name(key_t k, value_t v, bool isKey = false);
493 #define I(key_t, name, value_t) void name(key_t k, value_t v);
494 #define D(key_t, name, value_t) void name(key_t k, value_t v) = delete;
499 FOR_EACH_KEY_TYPE(set
, const Variant
&)
507 * Append or prepend an element, with semantics like set().
509 void append(TypedValue v
);
510 void append(const Variant
& v
);
511 void prepend(TypedValue v
);
512 void prepend(const Variant
& v
);
515 * Remove all elements.
517 void clear() { operator=(Create()); }
520 * Stack/queue-like functions.
525 #undef FOR_EACH_KEY_TYPE
527 /////////////////////////////////////////////////////////////////////////////
530 Array(ArrayData
* ad
, NoIncRef
) : m_arr(ad
, NoIncRef
{}) {}
532 Array
& plusImpl(ArrayData
* data
);
533 Array
& mergeImpl(ArrayData
* data
);
534 Array
diffImpl(const Array
& array
, bool by_key
, bool by_value
, bool match
,
535 PFUNC_CMP key_cmp_function
, const void* key_data
,
536 PFUNC_CMP value_cmp_function
, const void* value_data
) const;
538 template<typename T
> tv_rval
rvalImpl(const T
& key
, Flags
) const;
539 template<typename T
> tv_lval
lvalImpl(const T
& key
, Flags
);
540 template<typename T
> tv_lval
lvalForceImpl(const T
& key
, Flags
);
542 template<typename T
> bool existsImpl(const T
& key
) const;
543 template<typename T
> void removeImpl(const T
& key
);
544 template<typename T
> void setImpl(const T
& key
, TypedValue v
);
546 static void compileTimeAssertions();
548 /////////////////////////////////////////////////////////////////////////////
554 ///////////////////////////////////////////////////////////////////////////////
557 explicit ArrNR(ArrayData
* data
= nullptr) { m_px
= data
; }
558 explicit ArrNR(const ArrayData
*ad
) : m_px(const_cast<ArrayData
*>(ad
)) {}
560 ArrNR(const ArrNR
& a
) { m_px
= a
.m_px
; }
564 m_px
= reinterpret_cast<ArrayData
*>(0xdeadbeeffaceb004);
568 ArrayData
* get() const { return m_px
; }
570 operator const Array
&() const { return asArray(); }
573 return *reinterpret_cast<Array
*>(this); // XXX
575 const Array
& asArray() const {
576 return const_cast<ArrNR
*>(this)->asArray();
580 static void compileTimeAssertions();
586 ///////////////////////////////////////////////////////////////////////////////
589 * An Array wrapper that doesn't run a destructor unless you
590 * explicitly ask it to.
592 * This is used for the dynPropTable in ExecutionContext, so that at
593 * sweep time we don't run these destructors.
596 /* implicit */ ArrayNoDtor(ArrayData
* table
) { new (&m_arr
) Array(table
); }
597 ArrayNoDtor() { new (&m_arr
) Array(); }
598 ArrayNoDtor(ArrayNoDtor
&& o
) noexcept
{
599 new (&m_arr
) Array(std::move(o
.m_arr
));
602 Array
& arr() { return m_arr
; }
603 const Array
& arr() const { return m_arr
; }
604 void destroy() { m_arr
.~Array(); }
606 union { Array m_arr
; };
609 ///////////////////////////////////////////////////////////////////////////////
611 ALWAYS_INLINE Array
empty_array() {
612 return Array::attach(ArrayData::Create());
615 ALWAYS_INLINE Array
empty_varray() {
616 return Array::attach(ArrayData::CreateVArray());
619 ALWAYS_INLINE Array
empty_darray() {
620 return Array::attach(ArrayData::CreateDArray());
623 ALWAYS_INLINE Array
empty_vec_array() {
624 return Array::attach(ArrayData::CreateVec());
627 ALWAYS_INLINE Array
empty_dict_array() {
628 return Array::attach(ArrayData::CreateDict());
631 ALWAYS_INLINE Array
empty_keyset() {
632 return Array::attach(ArrayData::CreateKeyset());
635 ///////////////////////////////////////////////////////////////////////////////
638 * Type-pun a referenced ArrayData* as an Array&.
640 * This lets us take advantage of Array's CoW and escalation machinery to
641 * possibly mutate `ad', without the overhead or behavioral change of
644 * asArrRef() unconditionally removes Persistent bits from the referenced type.
646 ALWAYS_INLINE Array
& asArrRef(tv_lval tv
) {
647 assertx(tvIsArrayLike(tv
));
648 tv
.type() = tv
.val().parr
->toDataType();
649 return *reinterpret_cast<Array
*>(&val(tv
).parr
);
652 ALWAYS_INLINE
const Array
& asCArrRef(tv_rval tv
) {
653 assertx(tvIsArrayLike(tv
));
654 return *reinterpret_cast<const Array
*>(&val(tv
).parr
);
657 template <IntishCast IC
= IntishCast::None
>
658 ALWAYS_INLINE Array
toArray(tv_rval rval
) {
659 if (isArrayLikeType(type(rval
))) {
660 return Array
{assert_not_null(val(rval
).parr
)};
662 return Array::attach(tvCastToArrayLikeData
<IC
>(*rval
));
665 ///////////////////////////////////////////////////////////////////////////////