Eliminate ArrayData::lvalSilent
[hiphop-php.git] / hphp / runtime / base / type-array.h
blob0d1fb848b3426821f1adde6691c993f98b609039
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/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"
28 #include <algorithm>
29 #include <vector>
31 namespace HPHP {
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);
38 struct ArrayIter;
39 struct VariableUnserializer;
41 ////////////////////////////////////////////////////////////////////////////////
43 enum class AccessFlags {
44 None = 0,
45 Error = 1,
46 Key = 2,
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.
68 struct Array {
69 private:
70 using Ptr = req::ptr<ArrayData>;
71 using NoIncRef = Ptr::NoIncRef;
72 using NonNull = Ptr::NonNull;
74 using Flags = AccessFlags;
76 public:
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
88 * never be converted.
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 /////////////////////////////////////////////////////////////////////////////
114 Array() {}
115 ~Array();
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{})
158 * Move constructor.
160 Array(Array&& src) noexcept : m_arr(std::move(src.m_arr)) { }
163 * Assignment.
165 Array& operator=(ArrayData* data) {
166 m_arr = data;
167 return *this;
169 Array& operator=(const Array& arr) {
170 m_arr = arr.m_arr;
171 return *this;
173 Array& operator=(const Variant& v);
176 * Move assignment.
178 Array& operator=(Array&& src) {
179 m_arr = std::move(src.m_arr);
180 return *this;
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()) }
206 #undef COPY_BODY
208 /////////////////////////////////////////////////////////////////////////////
211 * Nullability.
213 bool isNull() const { return !m_arr; }
216 * Size.
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; }
223 * Array kind.
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 /////////////////////////////////////////////////////////////////////////////
238 * Start iterator.
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
254 * are inserted?
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 /////////////////////////////////////////////////////////////////////////////
268 // PHP operations.
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,
296 const void* data);
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;
320 * Sorting.
322 static int SortRegularAscending(const Variant& v1, const Variant& v2,
323 const void* data);
324 static int SortNumericAscending(const Variant& v1, const Variant& v2,
325 const void* data);
326 static int SortStringAscending(const Variant& v1, const Variant& v2,
327 const void* data);
328 static int SortStringAscendingCase(const Variant& v1, const Variant& v2,
329 const void* data);
330 static int SortLocaleStringAscending(const Variant& v1, const Variant& v2,
331 const void* data);
333 static int SortRegularDescending(const Variant& v1, const Variant& v2,
334 const void* data);
335 static int SortNumericDescending(const Variant& v1, const Variant& v2,
336 const void* data);
337 static int SortStringDescending(const Variant& v1, const Variant& v2,
338 const void* data);
339 static int SortStringDescendingCase(const Variant& v1, const Variant& v2,
340 const void* data);
341 static int SortLocaleStringDescending(const Variant& v1, const Variant& v2,
342 const void* data);
344 static int SortNaturalAscending(const Variant& v1, const Variant& v2,
345 const void* data);
346 static int SortNaturalDescending(const Variant& v1, const Variant& v2,
347 const void* data);
348 static int SortNaturalCaseAscending(const Variant& v1, const Variant& v2,
349 const void* data);
350 static int SortNaturalCaseDescending(const Variant& v1, const Variant& v2,
351 const void* data);
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.
359 struct SortData {
360 Variant* original;
361 const Array* array;
362 bool by_key;
363 PFUNC_CMP cmp_func;
364 const void* data;
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);
375 * Type conversions.
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);
391 * Comparisons.
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;
425 #define V C
426 #define I V
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, )
446 #undef D
447 #undef I
448 #undef V
449 #undef C
451 /////////////////////////////////////////////////////////////////////////////
452 // Element access and mutation.
454 #define C(key_t, ret_t, name, cns) ret_t name(key_t, bool isKey = false) cns;
455 #define V C
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;
460 * Membership.
462 FOR_EACH_KEY_TYPE(bool, exists, const)
465 * Remove an element.
467 FOR_EACH_KEY_TYPE(void, remove, )
469 #undef D
470 #undef I
471 #undef V
472 #undef C
474 #define C(key_t, name, value_t) \
475 void name(key_t k, value_t v, bool isKey = false);
476 #define V C
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)
485 #undef D
486 #undef I
487 #undef V
488 #undef C
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;
497 * Variant overloads.
499 FOR_EACH_KEY_TYPE(set, const Variant&)
501 #undef D
502 #undef I
503 #undef V
504 #undef C
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.
522 Variant pop();
523 Variant dequeue();
525 #undef FOR_EACH_KEY_TYPE
527 /////////////////////////////////////////////////////////////////////////////
529 private:
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 /////////////////////////////////////////////////////////////////////////////
550 private:
551 Ptr m_arr;
554 ///////////////////////////////////////////////////////////////////////////////
556 struct ArrNR {
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; }
562 ~ArrNR() {
563 if (debug) {
564 m_px = reinterpret_cast<ArrayData*>(0xdeadbeeffaceb004);
568 ArrayData* get() const { return m_px; }
570 operator const Array&() const { return asArray(); }
572 Array& asArray() {
573 return *reinterpret_cast<Array*>(this); // XXX
575 const Array& asArray() const {
576 return const_cast<ArrNR*>(this)->asArray();
579 private:
580 static void compileTimeAssertions();
582 private:
583 ArrayData* m_px;
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.
595 struct ArrayNoDtor {
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));
601 ~ArrayNoDtor() {}
602 Array& arr() { return m_arr; }
603 const Array& arr() const { return m_arr; }
604 void destroy() { m_arr.~Array(); }
605 private:
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
642 * refcounting.
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 ///////////////////////////////////////////////////////////////////////////////
668 #endif