2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2016 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/types.h"
28 ///////////////////////////////////////////////////////////////////////////////
31 struct VariableUnserializer
;
33 ////////////////////////////////////////////////////////////////////////////////
35 enum class AccessFlags
{
39 ErrorKey
= Error
| Key
,
42 inline AccessFlags
operator&(AccessFlags a
, AccessFlags b
) {
43 return static_cast<AccessFlags
>(static_cast<int>(a
) & static_cast<int>(b
));
46 constexpr bool any(AccessFlags a
) {
47 return a
!= AccessFlags::None
;
50 ////////////////////////////////////////////////////////////////////////////////
53 * Array type wrapping around ArrayData to implement reference
54 * counting, copy-on-write and ArrayData escalation.
56 * "Escalation" happens when an underlying ArrayData cannot handle an operation
57 * and instead it needs to "upgrade" itself to be a more general (but slower)
58 * type of ArrayData to accomplish the task. This "upgrade" is called
63 using Ptr
= req::ptr
<ArrayData
>;
64 using NoIncRef
= Ptr::NoIncRef
;
65 using NonNull
= Ptr::NonNull
;
67 using Flags
= AccessFlags
;
71 Array(ArrayData
* ad
, NoIncRef
) : m_arr(ad
, NoIncRef
{}) {}
74 template<class F
> void scan(F
& mark
) const {
80 * Create an empty array or an array with one element. Note these are
81 * different than those copying constructors that also take one value.
83 static Array
Create() {
84 return Array(ArrayData::Create(), NoIncRef
{});
87 static Array
CreateVec() {
88 return Array(ArrayData::CreateVec(), NoIncRef
{});
91 static Array
CreateDict() {
92 return Array(ArrayData::CreateDict(), NoIncRef
{});
95 static Array
Create(const Variant
& value
) {
96 return Array(ArrayData::Create(value
), NoIncRef
{});
99 static Array
Create(const Variant
& key
, const Variant
& value
);
105 // Take ownership of this ArrayData.
106 static ALWAYS_INLINE Array
attach(ArrayData
* ad
) {
107 return Array(ad
, NoIncRef
{});
110 // Transfer ownership of our reference to this ArrayData.
111 ArrayData
* detach() { return m_arr
.detach(); }
113 ArrayData
* get() const { return m_arr
.get(); }
114 void reset(ArrayData
* arr
= nullptr) { m_arr
.reset(arr
); }
116 // Deliberately doesn't throw_null_pointer_exception as a perf
118 ArrayData
* operator->() const { return m_arr
.get(); }
122 // Make a copy of this array. Like the underlying ArrayData::copy operation,
123 // the returned Array may point to the same underlying array as the original,
128 auto new_arr
= m_arr
->copy();
129 return (new_arr
!= m_arr
) ?
130 Array
{new_arr
, NoIncRef
{}} : Array
{*this};
133 Array
toVec() const {
134 if (!m_arr
) return CreateVec();
135 auto new_arr
= m_arr
->toVec();
136 return (new_arr
!= m_arr
) ? Array
{new_arr
, NoIncRef
{}} : Array
{*this};
139 Array
toDict() const {
140 if (!m_arr
) return CreateDict();
141 auto new_arr
= m_arr
->toDict();
142 return (new_arr
!= m_arr
) ? Array
{new_arr
, NoIncRef
{}} : Array
{*this};
146 * Constructors. Those that take "arr" or "var" are copy constructors, taking
147 * array value from the parameter, and they are NOT constructing an array
148 * with that single value (then one should use Array::Create() functions).
150 explicit Array(ArrayData
* data
) : m_arr(data
) { }
151 /* implicit */ Array(const Array
& arr
) : m_arr(arr
.m_arr
) { }
154 * Special constructor for use from ArrayInit that creates an Array without a
155 * null check and without an inc-ref.
157 enum class ArrayInitCtor
{ Tag
};
158 explicit Array(ArrayData
* ad
, ArrayInitCtor
)
159 : m_arr(ad
, NoIncRef
{})
163 Array(Array
&& src
) noexcept
: m_arr(std::move(src
.m_arr
)) { }
166 Array
& operator=(Array
&& src
) {
167 m_arr
= std::move(src
.m_arr
);
175 return !m_arr
|| m_arr
->empty();
177 ssize_t
size() const {
178 return m_arr
? m_arr
->size() : 0;
180 ssize_t
length() const {
181 return m_arr
? m_arr
->size() : 0;
183 bool isNull() const {
186 Array
values() const;
188 bool useWeakKeys() const {
189 // If array isn't set we may implicitly create a mixed array. We never
190 // implicitly create a dict array or vec.
191 return !m_arr
|| m_arr
->useWeakKeys();
195 * Converts k to a valid key for this array type
197 VarNR
convertKey(const Variant
& k
) const;
202 Array
& operator=(ArrayData
* data
) {
206 Array
& operator=(const Array
& arr
) {
210 Array
& operator=(const Variant
& v
);
211 Array
operator+(ArrayData
* data
) const;
212 Array
operator+(const Array
& v
) const;
213 Array
operator+(const Variant
& v
) const = delete;
214 Array
& operator+=(ArrayData
* data
);
215 Array
& operator+=(const Array
& v
);
216 Array
& operator+=(const Variant
& v
);
219 Array
& operator=(Variant
&& v
);
222 * Returns the entries that have keys and/or values that are not present in
223 * specified array. Keys and values can be compared by user supplied
224 * functions and key_data or value_data will be passed into PFUNC_CMP as
225 * "data" parameter. Otherwise, equal() will be called for comparisons. If
226 * both by_key and by_value, both keys and values have to match to be
229 typedef int (*PFUNC_CMP
)(const Variant
& v1
, const Variant
& v2
, const void* data
);
230 Array
diff(const Variant
& array
, bool by_key
, bool by_value
,
231 PFUNC_CMP key_cmp_function
= nullptr,
232 const void* key_data
= nullptr,
233 PFUNC_CMP value_cmp_function
= nullptr,
234 const void* value_data
= nullptr) const;
237 * Returns the entries that have keys and/or values that are present in
238 * specified array. Keys and values can be compared by user supplied
239 * functions and key_data or value_data will be passed into PFUNC_CMP as
240 * "data" parameter. Otherwise, equal() will be called for comparisons. If
241 * both by_key and by_value, both keys and values have to match to be
244 Array
intersect(const Variant
& array
, bool by_key
, bool by_value
,
245 PFUNC_CMP key_cmp_function
= nullptr,
246 const void* key_data
= nullptr,
247 PFUNC_CMP value_cmp_function
= nullptr,
248 const void* value_data
= nullptr) const;
251 * Iterator functions. See array-iterator.h for end() and next().
253 ArrayIter
begin(const String
& context
= null_string
) const;
258 * Merge: This is different from operator "+", where existing key's values
259 * are NOT modified. This function will actually override with new values.
260 * When merging a vector with a vector, new elements are always appended, and
261 * this is also different from operator "+", where existing numeric indices
264 * Slice: Taking a slice. When "preserve_keys" is true, a vector will turn
265 * into numerically keyed map.
267 Array
& merge(const Array
& arr
);
272 static int SortRegularAscending(const Variant
& v1
, const Variant
& v2
,
274 static int SortNumericAscending(const Variant
& v1
, const Variant
& v2
,
276 static int SortStringAscending(const Variant
& v1
, const Variant
& v2
,
278 static int SortStringAscendingCase(const Variant
& v1
, const Variant
& v2
,
280 static int SortLocaleStringAscending(const Variant
& v1
, const Variant
& v2
,
283 static int SortRegularDescending(const Variant
& v1
, const Variant
& v2
,
285 static int SortNumericDescending(const Variant
& v1
, const Variant
& v2
,
287 static int SortStringDescending(const Variant
& v1
, const Variant
& v2
,
289 static int SortStringDescendingCase(const Variant
& v1
, const Variant
& v2
,
291 static int SortLocaleStringDescending(const Variant
& v1
, const Variant
& v2
,
294 static int SortNaturalAscending(const Variant
& v1
, const Variant
& v2
,
296 static int SortNaturalDescending(const Variant
& v1
, const Variant
& v2
,
298 static int SortNaturalCaseAscending(const Variant
& v1
, const Variant
& v2
,
300 static int SortNaturalCaseDescending(const Variant
& v1
, const Variant
& v2
,
303 void sort(PFUNC_CMP cmp_func
, bool by_key
, bool renumber
,
304 const void* data
= nullptr);
307 * Sort multiple arrays at once similar to how ORDER BY clause works in SQL.
315 std::vector
<ssize_t
> positions
;
317 static bool MultiSort(std::vector
<SortData
>& data
, bool renumber
);
319 static void SortImpl(std::vector
<int>& indices
, const Array
& source
,
320 Array::SortData
& opaque
,
321 Array::PFUNC_CMP cmp_func
,
322 bool by_key
, const void* data
= nullptr);
327 bool toBoolean() const { return m_arr
&& !m_arr
->empty(); }
328 char toByte () const { return (m_arr
&& !m_arr
->empty()) ? 1 : 0; }
329 short toInt16 () const { return (m_arr
&& !m_arr
->empty()) ? 1 : 0; }
330 int toInt32 () const { return (m_arr
&& !m_arr
->empty()) ? 1 : 0; }
331 int64_t toInt64 () const { return (m_arr
&& !m_arr
->empty()) ? 1 : 0; }
332 double toDouble () const { return (m_arr
&& !m_arr
->empty()) ? 1.0 : 0.0; }
333 String
toString () const;
338 bool same (const Array
& v2
) const;
339 bool same (const Object
& v2
) const;
340 bool equal(const Array
& v2
) const;
341 bool equal(const Object
& v2
) const;
342 bool less (const Array
& v2
, bool flip
= false) const;
343 bool less (const Object
& v2
) const;
345 bool less (const Variant
& v2
) const;
346 bool more (const Array
& v2
, bool flip
= true) const;
347 bool more (const Object
& v2
) const;
348 bool more (const Variant
& v2
) const;
349 int compare (const Array
& v2
, bool flip
= false) const;
354 Variant
rvalAt(int key
, Flags
= Flags::None
) const;
355 Variant
rvalAt(int64_t key
, Flags
= Flags::None
) const;
356 Variant
rvalAt(double key
, Flags
= Flags::None
) const = delete;
357 Variant
rvalAt(const String
& key
, Flags
= Flags::None
) const;
358 Variant
rvalAt(const Variant
& key
, Flags
= Flags::None
) const;
361 * To get offset for temporary usage
363 const Variant
& rvalAtRef(int key
, Flags
= Flags::None
) const;
364 const Variant
& rvalAtRef(int64_t key
, Flags
= Flags::None
) const;
365 const Variant
& rvalAtRef(double key
, Flags
= Flags::None
) const = delete;
366 const Variant
& rvalAtRef(const Variant
& key
, Flags
= Flags::None
) const;
367 const Variant
& rvalAtRef(const String
& key
, Flags
= Flags::None
) const;
369 const Variant
operator[](int key
) const;
370 const Variant
operator[](int64_t key
) const;
371 const Variant
operator[](double key
) const = delete;
372 const Variant
operator[](const String
& key
) const;
373 const Variant
operator[](const Variant
& key
) const;
374 const Variant
operator[](const char*) const = delete; // use const String&
377 * Get an lval reference to a newly created element, with the intent
378 * of reading or writing to it as a Cell.
383 * Get an lval reference to a newly created element, with the intent
384 * of using binding assignment with the newly created element.
386 Variant
& lvalAtRef();
389 * Get an lval reference to an element.
391 Variant
& lvalAt(int key
, Flags flags
= Flags::None
) {
392 return lvalAtImpl(key
, flags
);
394 Variant
& lvalAt(int64_t key
, Flags flags
= Flags::None
) {
395 return lvalAtImpl(key
, flags
);
397 Variant
& lvalAt(double key
, Flags
= Flags::None
) = delete;
398 Variant
& lvalAt(const String
& key
, Flags
= Flags::None
);
399 Variant
& lvalAt(const Variant
& key
, Flags
= Flags::None
);
401 Variant
& lvalAtRef(int key
, Flags flags
= Flags::None
) {
402 return lvalAtRefImpl(key
, flags
);
404 Variant
& lvalAtRef(int64_t key
, Flags flags
= Flags::None
) {
405 return lvalAtRefImpl(key
, flags
);
407 Variant
& lvalAtRef(double key
, Flags
= Flags::None
) = delete;
408 Variant
& lvalAtRef(const String
& key
, Flags
= Flags::None
);
409 Variant
& lvalAtRef(const Variant
& key
, Flags
= Flags::None
);
412 * Set an element to a value.
414 void set(int key
, const Variant
& v
) { set(int64_t(key
), v
); }
415 void set(int64_t key
, const Variant
& v
);
416 void set(double key
, const Variant
& v
) = delete;
417 void set(const String
& key
, const Variant
& v
, bool isKey
= false);
418 void set(const Variant
& key
, const Variant
& v
, bool isKey
= false);
421 * Set an element to a reference.
423 void setRef(int key
, Variant
& v
) { setRef(int64_t(key
), v
); }
424 void setRef(int64_t key
, Variant
& v
);
425 void setRef(double key
, Variant
& v
) = delete;
426 void setRef(const String
& key
, Variant
& v
, bool isKey
= false);
427 void setRef(const Variant
& key
, Variant
& v
, bool isKey
= false);
429 void setWithRef(const Variant
& key
, const Variant
& v
, bool isKey
= false);
434 void add(int key
, const Variant
& v
) { add(int64_t(key
), v
); }
435 void add(int64_t key
, const Variant
& v
);
436 void add(double key
, const Variant
& v
) = delete;
437 void add(const String
& key
, const Variant
& v
, bool isKey
= false);
438 void add(const Variant
& key
, const Variant
& v
, bool isKey
= false);
441 * Membership functions.
443 bool exists(char key
) const { return existsImpl((int64_t)key
); }
444 bool exists(short key
) const { return existsImpl((int64_t)key
); }
445 bool exists(int key
) const { return existsImpl((int64_t)key
); }
446 bool exists(int64_t key
) const { return existsImpl(key
); }
447 bool exists(double key
) const = delete;
448 bool exists(const String
& key
, bool isKey
= false) const;
449 bool exists(const Variant
& key
, bool isKey
= false) const;
454 void remove(char key
) { removeImpl((int64_t)key
); }
455 void remove(short key
) { removeImpl((int64_t)key
); }
456 void remove(int key
) { removeImpl((int64_t)key
); }
457 void remove(int64_t key
) { removeImpl(key
); }
458 void remove(double key
) = delete;
459 void remove(const String
& key
, bool isString
= false);
460 void remove(const Variant
& key
);
463 * Remove all elements.
465 void clear() { operator=(Create()); }
470 const Variant
& append(const Variant
& v
);
471 const Variant
& appendRef(Variant
& v
);
472 const Variant
& appendWithRef(const Variant
& v
);
475 * Stack/queue-like functions.
479 void prepend(const Variant
& v
);
481 void setEvalScalar() const;
484 Array
& plusImpl(ArrayData
* data
);
485 Array
& mergeImpl(ArrayData
* data
);
486 Array
diffImpl(const Array
& array
, bool by_key
, bool by_value
, bool match
,
487 PFUNC_CMP key_cmp_function
, const void* key_data
,
488 PFUNC_CMP value_cmp_function
, const void* value_data
) const;
490 template<typename T
> void setImpl(const T
& key
, const Variant
& v
);
491 template<typename T
> void setRefImpl(const T
& key
, Variant
& v
);
492 template<typename T
> void addImpl(const T
& key
, const Variant
& v
);
495 bool existsImpl(const T
& key
) const {
496 if (m_arr
) return m_arr
->exists(key
);
501 void removeImpl(const T
& key
) {
503 ArrayData
* escalated
= m_arr
->remove(key
, m_arr
->cowCheck());
504 if (escalated
!= m_arr
) m_arr
= Ptr::attach(escalated
);
509 Variant
& lvalAtImpl(const T
& key
, Flags
= Flags::None
) {
510 if (!m_arr
) m_arr
= Ptr::attach(ArrayData::Create());
511 Variant
* ret
= nullptr;
512 ArrayData
* escalated
= m_arr
->lval(key
, ret
, m_arr
->cowCheck());
513 if (escalated
!= m_arr
) m_arr
= Ptr::attach(escalated
);
519 Variant
& lvalAtRefImpl(const T
& key
, Flags
= Flags::None
) {
520 if (!m_arr
) m_arr
= Ptr::attach(ArrayData::Create());
521 Variant
* ret
= nullptr;
522 ArrayData
* escalated
= m_arr
->lvalRef(key
, ret
, m_arr
->cowCheck());
523 if (escalated
!= m_arr
) m_arr
= Ptr::attach(escalated
);
528 static void compileTimeAssertions();
531 ///////////////////////////////////////////////////////////////////////////////
535 explicit ArrNR(ArrayData
* data
= nullptr) {
539 ArrNR(const ArrNR
& a
) {
545 m_px
= reinterpret_cast<ArrayData
*>(0xdeadbeeffaceb004);
549 operator const Array
&() const { return asArray(); }
552 return *reinterpret_cast<Array
*>(this); // XXX
555 const Array
& asArray() const {
556 return const_cast<ArrNR
*>(this)->asArray();
559 ArrayData
* get() const { return m_px
; }
565 static void compileTimeAssertions();
568 ///////////////////////////////////////////////////////////////////////////////
571 * An Array wrapper that doesn't run a destructor unless you
572 * explicitly ask it to.
574 * This is used for the dynPropTable in ExecutionContext, so that at
575 * sweep time we don't run these destructors.
578 /* implicit */ ArrayNoDtor(ArrayData
* table
) { new (&m_arr
) Array(table
); }
579 ArrayNoDtor() { new (&m_arr
) Array(); }
580 ArrayNoDtor(ArrayNoDtor
&& o
) noexcept
{
581 new (&m_arr
) Array(std::move(o
.m_arr
));
584 Array
& arr() { return m_arr
; }
585 const Array
& arr() const { return m_arr
; }
586 void destroy() { m_arr
.~Array(); }
588 union { Array m_arr
; };
591 ///////////////////////////////////////////////////////////////////////////////
593 ALWAYS_INLINE Array
empty_array() {
594 return Array::attach(staticEmptyArray());
597 ///////////////////////////////////////////////////////////////////////////////