Return member_lval from Array::lval{,Ref}()
[hiphop-php.git] / hphp / runtime / base / type-array.h
blob952a3d304f8dd5ca99266eefeb30a519c4aa3c9f
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
25 #include <algorithm>
26 #include <vector>
28 namespace HPHP {
29 ///////////////////////////////////////////////////////////////////////////////
31 struct ArrayIter;
32 struct VariableUnserializer;
34 ////////////////////////////////////////////////////////////////////////////////
36 enum class AccessFlags {
37 None = 0,
38 Error = 1,
39 Key = 2,
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.
61 struct Array {
62 private:
63 using Ptr = req::ptr<ArrayData>;
64 using NoIncRef = Ptr::NoIncRef;
65 using NonNull = Ptr::NonNull;
67 using Flags = AccessFlags;
69 public:
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 /////////////////////////////////////////////////////////////////////////////
100 Array() {}
101 ~Array();
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{})
144 * Move constructor.
146 Array(Array&& src) noexcept : m_arr(std::move(src.m_arr)) { }
149 * Assignment.
151 Array& operator=(ArrayData* data) {
152 m_arr = data;
153 return *this;
155 Array& operator=(const Array& arr) {
156 m_arr = arr.m_arr;
157 return *this;
159 Array& operator=(const Variant& v);
162 * Move assignment.
164 Array& operator=(Array&& src) {
165 m_arr = std::move(src.m_arr);
166 return *this;
168 Array& operator=(Variant&& v);
171 * Escalate the underlying ArrayData.
173 void escalate();
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{}) }
193 #undef COPY_BODY
195 /////////////////////////////////////////////////////////////////////////////
198 * Nullability.
200 bool isNull() const { return !m_arr; }
203 * Size.
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; }
210 * Array kind.
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 /////////////////////////////////////////////////////////////////////////////
222 * Start iterator.
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
236 * are inserted?
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 /////////////////////////////////////////////////////////////////////////////
250 // PHP operations.
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,
283 const void* data);
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;
307 * Sorting.
309 static int SortRegularAscending(const Variant& v1, const Variant& v2,
310 const void* data);
311 static int SortNumericAscending(const Variant& v1, const Variant& v2,
312 const void* data);
313 static int SortStringAscending(const Variant& v1, const Variant& v2,
314 const void* data);
315 static int SortStringAscendingCase(const Variant& v1, const Variant& v2,
316 const void* data);
317 static int SortLocaleStringAscending(const Variant& v1, const Variant& v2,
318 const void* data);
320 static int SortRegularDescending(const Variant& v1, const Variant& v2,
321 const void* data);
322 static int SortNumericDescending(const Variant& v1, const Variant& v2,
323 const void* data);
324 static int SortStringDescending(const Variant& v1, const Variant& v2,
325 const void* data);
326 static int SortStringDescendingCase(const Variant& v1, const Variant& v2,
327 const void* data);
328 static int SortLocaleStringDescending(const Variant& v1, const Variant& v2,
329 const void* data);
331 static int SortNaturalAscending(const Variant& v1, const Variant& v2,
332 const void* data);
333 static int SortNaturalDescending(const Variant& v1, const Variant& v2,
334 const void* data);
335 static int SortNaturalCaseAscending(const Variant& v1, const Variant& v2,
336 const void* data);
337 static int SortNaturalCaseDescending(const Variant& v1, const Variant& v2,
338 const void* data);
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.
346 struct SortData {
347 Variant* original;
348 const Array* array;
349 bool by_key;
350 PFUNC_CMP cmp_func;
351 const void* data;
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);
362 * Type conversions.
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;
373 * Comparisons.
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;
413 #define I V
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&, )
432 #undef D
433 #undef I
434 #undef V
435 #undef C
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;
447 #define V C
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;
452 * Membership.
454 FOR_EACH_KEY_TYPE(bool, exists, const)
457 * Remove an element.
459 FOR_EACH_KEY_TYPE(void, remove, )
461 #undef D
462 #undef I
463 #undef V
464 #undef C
466 #define C(key_t, name, value_t) \
467 void name(key_t k, value_t v, bool isKey = false);
468 #define V C
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&)
488 * Add an element.
490 * Like set(), but with the precondition that the key does not already exist
491 * in the array.
493 FOR_EACH_KEY_TYPE(add, TypedValue)
495 #undef D
496 #undef I
497 #undef V
498 #undef C
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;
507 * Variant overloads.
509 FOR_EACH_KEY_TYPE(set, const Variant&)
510 FOR_EACH_KEY_TYPE(setWithRef, const Variant&)
511 FOR_EACH_KEY_TYPE(add, const Variant&)
513 #undef D
514 #undef I
515 #undef V
516 #undef C
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.
537 Variant pop();
538 Variant dequeue();
540 #undef FOR_EACH_KEY_TYPE
542 /////////////////////////////////////////////////////////////////////////////
544 private:
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 /////////////////////////////////////////////////////////////////////////////
568 private:
569 Ptr m_arr;
572 ///////////////////////////////////////////////////////////////////////////////
574 struct ArrNR {
575 explicit ArrNR(ArrayData* data = nullptr) { m_px = data; }
577 ArrNR(const ArrNR& a) { m_px = a.m_px; }
579 ~ArrNR() {
580 if (debug) {
581 m_px = reinterpret_cast<ArrayData*>(0xdeadbeeffaceb004);
585 ArrayData* get() const { return m_px; }
587 operator const Array&() const { return asArray(); }
589 Array& asArray() {
590 return *reinterpret_cast<Array*>(this); // XXX
592 const Array& asArray() const {
593 return const_cast<ArrNR*>(this)->asArray();
596 private:
597 static void compileTimeAssertions();
599 private:
600 ArrayData* m_px;
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.
612 struct ArrayNoDtor {
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));
618 ~ArrayNoDtor() {}
619 Array& arr() { return m_arr; }
620 const Array& arr() const { return m_arr; }
621 void destroy() { m_arr.~Array(); }
622 private:
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 ///////////////////////////////////////////////////////////////////////////////
639 #endif