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/req-ptr.h"
22 #include "hphp/runtime/base/tv-variant.h"
23 #include "hphp/runtime/base/types.h"
29 ///////////////////////////////////////////////////////////////////////////////
32 struct VariableUnserializer
;
34 ////////////////////////////////////////////////////////////////////////////////
36 enum class AccessFlags
{
40 ErrorKey
= Error
| Key
,
43 inline AccessFlags
operator&(AccessFlags a
, AccessFlags b
) {
44 return static_cast<AccessFlags
>(static_cast<int>(a
) & static_cast<int>(b
));
47 constexpr bool any(AccessFlags a
) {
48 return a
!= AccessFlags::None
;
51 ////////////////////////////////////////////////////////////////////////////////
54 * Array type wrapping ArrayData to implement reference counting, copy-on-write
55 * and ArrayData escalation.
57 * "Escalation" happens when an underlying ArrayData cannot handle an operation
58 * and instead it needs to "upgrade" itself to be a more general (but slower)
59 * type of ArrayData to accomplish the task.
63 using Ptr
= req::ptr
<ArrayData
>;
64 using NoIncRef
= Ptr::NoIncRef
;
65 using NonNull
= Ptr::NonNull
;
67 using Flags
= AccessFlags
;
71 * Create an empty array or an array with one element.
73 * Note these are different than the copy (or copy-like) constructors that
74 * also take one value.
76 static Array
Create() {
77 return Array(ArrayData::Create(), NoIncRef
{});
80 static Array
CreateVec() {
81 return Array(ArrayData::CreateVec(), NoIncRef
{});
84 static Array
CreateDict() {
85 return Array(ArrayData::CreateDict(), NoIncRef
{});
88 static Array
CreateKeyset() {
89 return Array(ArrayData::CreateKeyset(), NoIncRef
{});
92 static Array
Create(const Variant
& value
) {
93 return Array(ArrayData::Create(value
), NoIncRef
{});
96 static Array
Create(const Variant
& key
, const Variant
& value
);
98 /////////////////////////////////////////////////////////////////////////////
104 * Take ownership of `ad'.
106 static ALWAYS_INLINE Array
attach(ArrayData
* ad
) {
107 return Array(ad
, NoIncRef
{});
111 * Transfer ownership of our underlying ArrayData.
113 ArrayData
* detach() { return m_arr
.detach(); }
116 * Get or null out the underlying ArrayData pointer.
118 ArrayData
* get() const { return m_arr
.get(); }
119 void reset(ArrayData
* arr
= nullptr) { m_arr
.reset(arr
); }
122 * Delegate to the underlying ArrayData.
124 * Deliberately doesn't throw_null_pointer_exception as a perf optimization.
126 ArrayData
* operator->() const { return m_arr
.get(); }
129 * "Copy" constructors.
131 explicit Array(ArrayData
* data
) : m_arr(data
) { }
132 Array(const Array
& arr
) : m_arr(arr
.m_arr
) { }
135 * Special constructor for use from ArrayInit that creates an Array without a
136 * null check and without an inc-ref.
138 enum class ArrayInitCtor
{ Tag
};
139 explicit Array(ArrayData
* ad
, ArrayInitCtor
)
140 : m_arr(ad
, NoIncRef
{})
146 Array(Array
&& src
) noexcept
: m_arr(std::move(src
.m_arr
)) { }
151 Array
& operator=(ArrayData
* data
) {
155 Array
& operator=(const Array
& arr
) {
159 Array
& operator=(const Variant
& v
);
164 Array
& operator=(Array
&& src
) {
165 m_arr
= std::move(src
.m_arr
);
168 Array
& operator=(Variant
&& v
);
171 * Escalate the underlying ArrayData.
175 #define COPY_BODY(meth, def) \
176 if (!m_arr) return def; \
177 auto new_arr = m_arr->meth; \
178 return new_arr != m_arr ? Array{new_arr, NoIncRef{}} : Array{*this};
181 * Make a copy of this array.
183 * Like the underlying ArrayData::copy operation, the returned Array may
184 * point to the same underlying array as the original, or a new one.
186 Array
copy() const { COPY_BODY(copy(), Array
{}) }
187 Array
toVec() const { COPY_BODY(toVec(true), CreateVec()) }
188 Array
toDict() const { COPY_BODY(toDict(true), CreateDict()) }
189 Array
toKeyset() const { COPY_BODY(toKeyset(true), CreateKeyset()) }
190 Array
toPHPArray() const { COPY_BODY(toPHPArray(true), Array
{}) }
191 Array
toVArray() const { COPY_BODY(toVArray(true), Array
{}) }
195 /////////////////////////////////////////////////////////////////////////////
200 bool isNull() const { return !m_arr
; }
205 bool empty() const { return !m_arr
|| m_arr
->empty(); }
206 ssize_t
size() const { return m_arr
? m_arr
->size() : 0; }
207 ssize_t
length() const { return m_arr
? m_arr
->size() : 0; }
212 bool isVecArray() const { return m_arr
&& m_arr
->isVecArray(); }
213 bool isDict() const { return m_arr
&& m_arr
->isDict(); }
214 bool isKeyset() const { return m_arr
&& m_arr
->isKeyset(); }
215 bool isHackArray() const { return m_arr
&& m_arr
->isHackArray(); }
216 bool isPHPArray() const { return !m_arr
|| m_arr
->isPHPArray(); }
217 bool isVArray() const { return !m_arr
|| m_arr
->isVArray(); }
219 /////////////////////////////////////////////////////////////////////////////
224 * See array-iterator.h for end() and next().
226 ArrayIter
begin(const String
& context
= null_string
) const;
229 * Converts `k' to a valid key for this array kind.
231 Cell
convertKey(Cell k
) const;
232 Cell
convertKey(const Variant
& k
) const;
235 * Should int-like string keys be implicitly converted to integers before they
238 bool useWeakKeys() const {
239 // If array isn't set we may implicitly create a mixed array, which uses
240 // weak keys. We never implicitly create a Hack array.
241 return !m_arr
|| m_arr
->useWeakKeys();
245 * Convert the underlying ArrayData to a static copy of itself.
247 void setEvalScalar() const;
249 /////////////////////////////////////////////////////////////////////////////
253 * Get a packed array of this Array's values.
255 Array
values() const;
258 * PHP array union operator.
260 Array
operator+(ArrayData
* data
) const;
261 Array
operator+(const Array
& v
) const;
262 Array
operator+(const Variant
& v
) const = delete;
263 Array
& operator+=(ArrayData
* data
);
264 Array
& operator+=(const Array
& v
);
265 Array
& operator+=(const Variant
& v
);
268 * Implementation of array_merge().
270 * This is different from operator+(), where existing keys' values are NOT
271 * modified. This function will actually override with new values.
273 * When merging a packed array with another packed array, new elements are
274 * always appended, and this is also different from operator+() where
275 * existing numeric indices are not modified.
277 Array
& merge(const Array
& arr
);
280 * Comparison function for array operations.
282 using PFUNC_CMP
= int (*)(const Variant
& v1
, const Variant
& v2
,
286 * Return the entries that have keys and/or values that are (intersect), or
287 * are not (diff) present in `array'.
289 * Keys and values can be compared by user supplied functions and `key_data'
290 * or `value_data' will be passed into the corresponding `cmp_function' as
291 * the `data' parameter. Otherwise, equal() will be called for comparisons.
292 * If both `by_key' and `by_value' are true, both keys and values have to
293 * match to be included (intersect) or excluded (diff).
295 Array
diff(const Variant
& array
, bool by_key
, bool by_value
,
296 PFUNC_CMP key_cmp_function
= nullptr,
297 const void* key_data
= nullptr,
298 PFUNC_CMP value_cmp_function
= nullptr,
299 const void* value_data
= nullptr) const;
300 Array
intersect(const Variant
& array
, bool by_key
, bool by_value
,
301 PFUNC_CMP key_cmp_function
= nullptr,
302 const void* key_data
= nullptr,
303 PFUNC_CMP value_cmp_function
= nullptr,
304 const void* value_data
= nullptr) const;
309 static int SortRegularAscending(const Variant
& v1
, const Variant
& v2
,
311 static int SortNumericAscending(const Variant
& v1
, const Variant
& v2
,
313 static int SortStringAscending(const Variant
& v1
, const Variant
& v2
,
315 static int SortStringAscendingCase(const Variant
& v1
, const Variant
& v2
,
317 static int SortLocaleStringAscending(const Variant
& v1
, const Variant
& v2
,
320 static int SortRegularDescending(const Variant
& v1
, const Variant
& v2
,
322 static int SortNumericDescending(const Variant
& v1
, const Variant
& v2
,
324 static int SortStringDescending(const Variant
& v1
, const Variant
& v2
,
326 static int SortStringDescendingCase(const Variant
& v1
, const Variant
& v2
,
328 static int SortLocaleStringDescending(const Variant
& v1
, const Variant
& v2
,
331 static int SortNaturalAscending(const Variant
& v1
, const Variant
& v2
,
333 static int SortNaturalDescending(const Variant
& v1
, const Variant
& v2
,
335 static int SortNaturalCaseAscending(const Variant
& v1
, const Variant
& v2
,
337 static int SortNaturalCaseDescending(const Variant
& v1
, const Variant
& v2
,
340 void sort(PFUNC_CMP cmp_func
, bool by_key
, bool renumber
,
341 const void* data
= nullptr);
344 * Sort multiple arrays at once similar to how ORDER BY clause works in SQL.
352 std::vector
<ssize_t
> positions
;
354 static bool MultiSort(std::vector
<SortData
>& data
, bool renumber
);
356 static void SortImpl(std::vector
<int>& indices
, const Array
& source
,
357 Array::SortData
& opaque
,
358 Array::PFUNC_CMP cmp_func
,
359 bool by_key
, const void* data
= nullptr);
364 bool toBoolean() const { return m_arr
&& !m_arr
->empty(); }
365 int8_t toByte() const { return toBoolean() ? 1 : 0; }
366 int16_t toInt16() const { return toBoolean() ? 1 : 0; }
367 int32_t toInt32() const { return toBoolean() ? 1 : 0; }
368 int64_t toInt64() const { return toBoolean() ? 1 : 0; }
369 double toDouble() const { return toBoolean() ? 1.0 : 0.0; }
370 String
toString() const;
375 bool same(const Array
& v2
) const;
376 bool same(const Object
& v2
) const;
377 bool equal(const Array
& v2
) const;
378 bool equal(const Object
& v2
) const;
379 bool less(const Array
& v2
, bool flip
= false) const;
380 bool less(const Object
& v2
) const;
381 bool less(const Variant
& v2
) const;
382 bool more(const Array
& v2
, bool flip
= true) const;
383 bool more(const Object
& v2
) const;
384 bool more(const Variant
& v2
) const;
385 int compare(const Array
& v2
, bool flip
= false) const;
387 /////////////////////////////////////////////////////////////////////////////
388 // Element rval/lval.
390 #define FOR_EACH_KEY_TYPE(...) \
391 C(Cell, __VA_ARGS__) \
392 I(int, __VA_ARGS__) \
393 I(int64_t, __VA_ARGS__) \
394 V(const String&, __VA_ARGS__) \
395 V(const Variant&, __VA_ARGS__) \
396 D(double, __VA_ARGS__)
399 * Get a refcounted copy of the element at `key'.
401 Variant
operator[](Cell key
) const;
402 Variant
operator[](int key
) const;
403 Variant
operator[](int64_t key
) const;
404 Variant
operator[](const String
& key
) const;
405 Variant
operator[](const Variant
& key
) const;
406 Variant
operator[](double key
) const = delete;
407 Variant
operator[](const char*) const = delete;
409 #define C(key_t, name, ret_t, var_ret_t, cns) \
410 ret_t name(key_t, Flags = Flags::None) cns;
411 #define V(key_t, name, ret_t, var_ret_t, cns) \
412 var_ret_t name(key_t, Flags = Flags::None) cns;
414 #define D(key_t, name, ret_t, var_ret_t, cns) \
415 var_ret_t name(key_t, Flags = Flags::None) cns = delete;
418 * Get an rval to the element at `key'.
420 FOR_EACH_KEY_TYPE(rvalAt
, member_rval
, member_rval
, const)
423 * Get an lval to the element at `key'.
425 * These are ArrayData::lval() and ArrayData::lvalRef(), with CoW and
426 * escalation. As with those functions, the Ref versions should be used if
427 * the lval will be boxed, and the non-Ref versions should be used otherwise.
429 FOR_EACH_KEY_TYPE(lvalAt
, member_lval
, Variant
&, )
430 FOR_EACH_KEY_TYPE(lvalAtRef
, member_lval
, Variant
&, )
438 * Get an lval to a newly created element.
440 member_lval
lvalAt();
441 member_lval
lvalAtRef();
443 /////////////////////////////////////////////////////////////////////////////
444 // Element access and mutation.
446 #define C(key_t, ret_t, name, cns) ret_t name(key_t, bool isKey = false) cns;
448 #define I(key_t, ret_t, name, cns) ret_t name(key_t) cns;
449 #define D(key_t, ret_t, name, cns) ret_t name(key_t) cns = delete;
454 FOR_EACH_KEY_TYPE(bool, exists
, const)
459 FOR_EACH_KEY_TYPE(void, remove
, )
466 #define C(key_t, name, value_t) \
467 void name(key_t k, value_t v, bool isKey = false);
469 #define I(key_t, name, value_t) void name(key_t k, value_t v);
470 #define D(key_t, name, value_t) void name(key_t k, value_t v) = delete;
473 * Set an element to `v', unboxing `v' if it's boxed.
475 FOR_EACH_KEY_TYPE(set
, TypedValue
)
478 * Set an element to `v', preserving refs unless they are singly-referenced.
480 FOR_EACH_KEY_TYPE(setWithRef
, TypedValue
)
483 * Set an element to a reference to `v', boxing it if it's unboxed.
485 FOR_EACH_KEY_TYPE(setRef
, Variant
&)
490 * Like set(), but with the precondition that the key does not already exist
493 FOR_EACH_KEY_TYPE(add
, TypedValue
)
500 #define C(key_t, name, value_t)
501 #define V(key_t, name, value_t) \
502 void name(key_t k, value_t v, bool isKey = false);
503 #define I(key_t, name, value_t) void name(key_t k, value_t v);
504 #define D(key_t, name, value_t) void name(key_t k, value_t v) = delete;
509 FOR_EACH_KEY_TYPE(set
, const Variant
&)
510 FOR_EACH_KEY_TYPE(setWithRef
, const Variant
&)
511 FOR_EACH_KEY_TYPE(add
, const Variant
&)
519 * Append or prepend an element, with semantics like set{,WithRef}().
521 void append(TypedValue v
);
522 void append(const Variant
& v
);
523 void appendWithRef(TypedValue v
);
524 void appendWithRef(const Variant
& v
);
525 void appendRef(Variant
& v
);
526 void prepend(TypedValue v
);
527 void prepend(const Variant
& v
);
530 * Remove all elements.
532 void clear() { operator=(Create()); }
535 * Stack/queue-like functions.
540 #undef FOR_EACH_KEY_TYPE
542 /////////////////////////////////////////////////////////////////////////////
545 Array(ArrayData
* ad
, NoIncRef
) : m_arr(ad
, NoIncRef
{}) {}
547 Array
& plusImpl(ArrayData
* data
);
548 Array
& mergeImpl(ArrayData
* data
);
549 Array
diffImpl(const Array
& array
, bool by_key
, bool by_value
, bool match
,
550 PFUNC_CMP key_cmp_function
, const void* key_data
,
551 PFUNC_CMP value_cmp_function
, const void* value_data
) const;
553 template<typename T
> member_rval
rvalAtImpl(const T
& key
, Flags
) const;
554 template<typename T
> member_lval
lvalAtImpl(const T
& key
, Flags
);
555 template<typename T
> member_lval
lvalAtRefImpl(const T
& key
, Flags
);
557 template<typename T
> bool existsImpl(const T
& key
) const;
558 template<typename T
> void removeImpl(const T
& key
);
559 template<typename T
> void setImpl(const T
& key
, TypedValue v
);
560 template<typename T
> void setWithRefImpl(const T
& key
, TypedValue v
);
561 template<typename T
> void setRefImpl(const T
& key
, Variant
& v
);
562 template<typename T
> void addImpl(const T
& key
, TypedValue v
);
564 static void compileTimeAssertions();
566 /////////////////////////////////////////////////////////////////////////////
572 ///////////////////////////////////////////////////////////////////////////////
575 explicit ArrNR(ArrayData
* data
= nullptr) { m_px
= data
; }
577 ArrNR(const ArrNR
& a
) { m_px
= a
.m_px
; }
581 m_px
= reinterpret_cast<ArrayData
*>(0xdeadbeeffaceb004);
585 ArrayData
* get() const { return m_px
; }
587 operator const Array
&() const { return asArray(); }
590 return *reinterpret_cast<Array
*>(this); // XXX
592 const Array
& asArray() const {
593 return const_cast<ArrNR
*>(this)->asArray();
597 static void compileTimeAssertions();
603 ///////////////////////////////////////////////////////////////////////////////
606 * An Array wrapper that doesn't run a destructor unless you
607 * explicitly ask it to.
609 * This is used for the dynPropTable in ExecutionContext, so that at
610 * sweep time we don't run these destructors.
613 /* implicit */ ArrayNoDtor(ArrayData
* table
) { new (&m_arr
) Array(table
); }
614 ArrayNoDtor() { new (&m_arr
) Array(); }
615 ArrayNoDtor(ArrayNoDtor
&& o
) noexcept
{
616 new (&m_arr
) Array(std::move(o
.m_arr
));
619 Array
& arr() { return m_arr
; }
620 const Array
& arr() const { return m_arr
; }
621 void destroy() { m_arr
.~Array(); }
623 union { Array m_arr
; };
626 ///////////////////////////////////////////////////////////////////////////////
628 ALWAYS_INLINE Array
empty_array() {
629 return Array::attach(staticEmptyArray());
632 ALWAYS_INLINE Array
empty_vec_array() {
633 return Array::attach(staticEmptyVecArray());
636 ///////////////////////////////////////////////////////////////////////////////