Make comparison IR ops layout agnostic
[hiphop-php.git] / hphp / runtime / base / array-init.h
blobf4252b34a3bc3e836c142f4858d7b9077a8b7e31
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 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_ARRAY_INIT_H_
17 #define incl_HPHP_ARRAY_INIT_H_
19 #include <boost/variant.hpp>
20 #include <type_traits>
22 #include "hphp/runtime/base/array-data-defs.h"
23 #include "hphp/runtime/base/array-data.h"
24 #include "hphp/runtime/base/tv-val.h"
25 #include "hphp/runtime/base/mixed-array.h"
26 #include "hphp/runtime/base/packed-array.h"
27 #include "hphp/runtime/base/set-array.h"
28 #include "hphp/runtime/base/request-info.h"
29 #include "hphp/runtime/base/type-variant.h"
30 #include "hphp/runtime/base/typed-value.h"
32 #include "hphp/runtime/vm/vm-regs.h"
34 #include "hphp/util/match.h"
36 namespace HPHP {
38 ///////////////////////////////////////////////////////////////////////////////
40 // SetInPlace helpers that assume that the input array has a mixed layout.
41 // These helpers work for both mixed PHP arrays and dicts.
42 namespace arr_init {
43 inline ArrayData* SetInPlace(ArrayData* ad, int64_t k, TypedValue v) {
44 return MixedArray::SetIntInPlace(ad, k, tvToInit(v));
46 inline ArrayData* SetInPlace(ArrayData* ad, StringData* k, TypedValue v) {
47 return MixedArray::SetStrInPlace(ad, k, tvToInit(v));
49 inline ArrayData* SetInPlace(ArrayData* ad, const String& k, TypedValue v) {
50 return MixedArray::SetStrInPlace(ad, k.get(), tvToInit(v));
52 inline ArrayData* SetInPlace(ArrayData* ad, TypedValue k, TypedValue v) {
53 if (isIntType(k.m_type)) {
54 return MixedArray::SetIntInPlace(ad, k.m_data.num, tvToInit(v));
55 } else if (isStringType(k.m_type)) {
56 return MixedArray::SetStrInPlace(ad, k.m_data.pstr, tvToInit(v));
58 return ad->set(k, v);
62 ///////////////////////////////////////////////////////////////////////////////
65 * Flag indicating whether an array allocation should be pre-checked for OOM.
67 enum class CheckAllocation {};
70 * Base class for ArrayInits specialized on array kind.
72 * Takes two template parameters:
73 * - TArray is a bag of static class functions, such as MixedArray. See the
74 * `detail' namespace below for the requirements.
75 * - DT is the DataType for the arrays created by the ArrayInit. If DT is
76 * the sentinel KindOfUninit, we won't make assumptions about the type.
78 template<typename TArray, DataType DT>
79 struct ArrayInitBase {
80 explicit ArrayInitBase(size_t n)
81 : m_arr(TArray::MakeReserve(n))
82 #ifndef NDEBUG
83 , m_addCount(0)
84 , m_expectedCount(n)
85 #endif
87 assertx(m_arr->hasExactlyOneRef());
90 ArrayInitBase(ArrayInitBase&& other) noexcept
91 : m_arr(other.m_arr)
92 #ifndef NDEBUG
93 , m_addCount(other.m_addCount)
94 , m_expectedCount(other.m_expectedCount)
95 #endif
97 assertx(!m_arr || DT == KindOfUninit || m_arr->toDataType() == DT);
98 other.m_arr = nullptr;
99 #ifndef NDEBUG
100 other.m_expectedCount = 0;
101 #endif
104 ArrayInitBase(const ArrayInitBase&) = delete;
105 ArrayInitBase& operator=(const ArrayInitBase&) = delete;
107 ~ArrayInitBase() {
108 // In case an exception interrupts the initialization.
109 assertx(!m_arr || DT == KindOfUninit ||
110 (m_arr->hasExactlyOneRef() && m_arr->toDataType() == DT));
111 if (m_arr) TArray::Release(m_arr);
114 /////////////////////////////////////////////////////////////////////////////
116 * Finish routines.
118 * These all invalidate the ArrayInit and return the initialized array.
120 Variant toVariant() {
121 if (DT == KindOfUninit) {
122 return Array(create(), Array::ArrayInitCtor::Tag);
124 return Variant(create(), DT, Variant::ArrayInitCtor{});
126 Array toArray() {
127 return Array(create(), Array::ArrayInitCtor::Tag);
130 ArrayData* create() {
131 assertx(m_arr->hasExactlyOneRef());
132 assertx(DT == KindOfUninit || m_arr->toDataType() == DT);
133 auto const ptr = m_arr;
134 m_arr = nullptr;
135 #ifndef NDEBUG
136 m_expectedCount = 0; // reset; no more adds allowed
137 #endif
138 return ptr;
141 /////////////////////////////////////////////////////////////////////////////
143 protected:
145 * Checked-allocation constructor.
147 * Only used by the constructors of derived classes.
149 ArrayInitBase(size_t n, CheckAllocation)
150 #ifndef NDEBUG
151 : m_addCount(0)
152 , m_expectedCount(n)
153 #endif
156 template<class Operation>
157 ALWAYS_INLINE void performOp(Operation oper) {
158 DEBUG_ONLY auto newp = oper();
159 // Array escalation must not happen during these reserved initializations.
160 assertx(newp == m_arr);
161 // You cannot add/set more times than you reserved with ArrayInit.
162 assertx(++m_addCount <= m_expectedCount);
165 protected:
166 ArrayData* m_arr;
167 #ifndef NDEBUG
168 size_t m_addCount;
169 size_t m_expectedCount;
170 #endif
173 ///////////////////////////////////////////////////////////////////////////////
176 * Dummy MixedArray-like bags of statics for Hack arrays.
178 namespace detail {
180 struct VArray {
181 static constexpr auto MakeReserve = &PackedArray::MakeReserveVArray;
182 static constexpr auto Release = PackedArray::Release;
185 struct DArray {
186 static constexpr auto MakeReserve = &MixedArray::MakeReserveDArray;
187 static constexpr auto Release = MixedArray::Release;
190 struct Vec {
191 static constexpr auto MakeReserve = &PackedArray::MakeReserveVec;
192 static constexpr auto Release = PackedArray::Release;
195 struct DictArray {
196 static constexpr auto MakeReserve = &MixedArray::MakeReserveDict;
197 static constexpr auto Release = MixedArray::Release;
202 ///////////////////////////////////////////////////////////////////////////////
206 * Initializer for a MixedArray.
208 template <typename TArray>
209 struct MixedPHPArrayInitBase : ArrayInitBase<TArray, KindOfUninit> {
210 enum class Map {};
211 // This is the same as map right now, but is here for documentation
212 // so we can find them later.
213 using Mixed = Map;
216 * Extension code should avoid using ArrayInit, and instead use DArrayInit
217 * or VArrayInit. If you really want to create a plain PHP array, your only
218 * option now is to use a mixed array.
220 * For large array allocations, consider passing CheckAllocation, which will
221 * throw if the allocation would OOM the request.
223 MixedPHPArrayInitBase(size_t n, Map)
224 : ArrayInitBase<TArray, KindOfUninit>(n) {}
225 MixedPHPArrayInitBase(size_t n, Map, CheckAllocation);
227 MixedPHPArrayInitBase(MixedPHPArrayInitBase&& o) noexcept
228 : ArrayInitBase<TArray, KindOfUninit>(std::move(o)) {}
230 /////////////////////////////////////////////////////////////////////////////
233 * Call append() on the underlying array.
235 MixedPHPArrayInitBase& append(TypedValue tv) {
236 this->performOp([&]{
237 return MixedArray::Append(this->m_arr, tvToInit(tv));
239 return *this;
241 MixedPHPArrayInitBase& append(const Variant& v) {
242 return append(*v.asTypedValue());
246 * Call set() on the underlying ArrayData.
248 MixedPHPArrayInitBase& set(int64_t name, TypedValue tv) {
249 this->performOp([&]{ return arr_init::SetInPlace(this->m_arr, name, tv); });
250 return *this;
252 MixedPHPArrayInitBase& set(const String& name, TypedValue tv) {
253 this->performOp([&]{ return arr_init::SetInPlace(this->m_arr, name, tv); });
254 return *this;
256 template<class T>
257 MixedPHPArrayInitBase& set(const T& name, TypedValue tv) {
258 this->performOp([&]{ return arr_init::SetInPlace(this->m_arr, name, tv); });
259 return *this;
262 #define IMPL_SET(KeyType) \
263 MixedPHPArrayInitBase& set(KeyType name, const Variant& v) { \
264 return set(name, *v.asTypedValue()); \
267 IMPL_SET(int64_t)
268 IMPL_SET(const String&)
269 template<typename T> IMPL_SET(const T&)
271 MixedPHPArrayInitBase& set(const Variant& name, const Variant& v) = delete;
273 #undef IMPL_SET
276 * Same as set(), but for the deleted double `const Variant&' overload.
278 MixedPHPArrayInitBase& setValidKey(TypedValue name, TypedValue v) {
279 set(tvToInit(name), v);
280 return *this;
282 MixedPHPArrayInitBase& setValidKey(const Variant& name, const Variant& v) {
283 return setValidKey(*name.asTypedValue(), *v.asTypedValue());
287 * This function is deprecated and exists for backward compatibility with the
288 * MixedPHPArrayInitBase API.
290 * Generally you should be able to figure out if your key is a pure string
291 * (not-integer-like) or not when using MixedPHPArrayInitBase, and if not you
292 * should probably use toKey yourself.
294 template <IntishCast IC = IntishCast::None>
295 MixedPHPArrayInitBase& setUnknownKey(const Variant& name, const Variant& v) {
296 auto const k = name.toKey<IC>(this->m_arr).tv();
297 if (LIKELY(!isNullType(k.m_type))) set(k, *v.asTypedValue());
298 return *this;
302 * Call add() on the underlying array.
304 MixedPHPArrayInitBase& add(int64_t name, TypedValue tv,
305 bool /*keyConverted*/ = false) {
306 this->performOp([&]{
307 return MixedArray::AddInt(this->m_arr, name, tvToInit(tv), false);
309 return *this;
312 MixedPHPArrayInitBase& add(const String& name, TypedValue tv,
313 bool keyConverted = false) {
314 if (keyConverted) {
315 this->performOp([&]{
316 return MixedArray::AddStr(this->m_arr, name.get(), tvToInit(tv), false);
318 } else if (!name.isNull()) {
319 set(VarNR::MakeKey(name).tv(), tv);
321 return *this;
324 MixedPHPArrayInitBase& add(const Variant& name, TypedValue tv,
325 bool keyConverted = false) {
326 if (keyConverted) {
327 set(name.asInitTVTmp(), tv);
328 } else {
329 auto const k = name.toKey(this->m_arr).tv();
330 if (!isNullType(k.m_type)) set(k, tv);
332 return *this;
335 template<typename T>
336 MixedPHPArrayInitBase& add(const T& name, TypedValue tv,
337 bool keyConverted = false) {
338 if (keyConverted) {
339 set(name, tv);
340 } else {
341 auto const k = Variant(name).toKey(this->m_arr).tv();
342 if (!isNullType(k.m_type)) set(k, tv);
344 return *this;
347 #define IMPL_ADD(KeyType) \
348 MixedPHPArrayInitBase& add(KeyType name, const Variant& v, \
349 bool keyConverted = false) { \
350 return add(name, *v.asTypedValue(), keyConverted); \
353 IMPL_ADD(int64_t)
354 IMPL_ADD(const String&)
355 IMPL_ADD(const Variant&)
356 template<typename T> IMPL_ADD(const T&)
358 #undef IMPL_ADD
361 using ArrayInit = MixedPHPArrayInitBase<MixedArray>;
363 struct MixedArrayInit : ArrayInit {
364 explicit MixedArrayInit(size_t n) : ArrayInit(n, Map{}) {}
365 MixedArrayInit(size_t n, CheckAllocation c) : ArrayInit(n, Map{}, c) {}
366 MixedArrayInit(MixedArrayInit&& o) noexcept : ArrayInit(std::move(o)) {}
369 ///////////////////////////////////////////////////////////////////////////////
371 struct DictInit : ArrayInitBase<detail::DictArray, KindOfDict> {
372 using ArrayInitBase<detail::DictArray, KindOfDict>::ArrayInitBase;
375 * For large array allocations, consider passing CheckAllocation, which will
376 * throw if the allocation would OOM the request.
378 DictInit(size_t n, CheckAllocation);
380 /////////////////////////////////////////////////////////////////////////////
382 DictInit& append(TypedValue tv) {
383 performOp([&]{ return MixedArray::Append(m_arr, tvToInit(tv)); });
384 return *this;
386 DictInit& append(const Variant& v) {
387 return append(*v.asTypedValue());
390 DictInit& set(int64_t name, TypedValue tv) {
391 performOp([&]{ return arr_init::SetInPlace(m_arr, name, tv); });
392 return *this;
394 DictInit& set(StringData* name, TypedValue tv) {
395 performOp([&]{ return arr_init::SetInPlace(m_arr, name, tv); });
396 return *this;
398 DictInit& set(const String& name, TypedValue tv) {
399 performOp([&]{ return arr_init::SetInPlace(m_arr, name, tv); });
400 return *this;
403 #define IMPL_SET(KeyType) \
404 DictInit& set(KeyType name, const Variant& v) { \
405 return set(name, *v.asTypedValue()); \
408 IMPL_SET(int64_t)
409 IMPL_SET(StringData*)
410 IMPL_SET(const String&)
412 DictInit& set(const char*, TypedValue tv) = delete;
413 DictInit& set(const char*, const Variant& v) = delete;
414 DictInit& set(const Variant& name, const Variant& v) = delete;
416 #undef IMPL_SET
418 DictInit& setValidKey(TypedValue name, TypedValue v) {
419 performOp([&]{
420 assertx(isIntType(name.m_type) || isStringType(name.m_type));
421 return arr_init::SetInPlace(m_arr, name, v);
423 return *this;
425 DictInit& setValidKey(const Variant& name, const Variant& v) {
426 return setValidKey(*name.asTypedValue(), *v.asTypedValue());
430 ///////////////////////////////////////////////////////////////////////////////
433 * Base initializer for Packed-layout arrays.
435 template<typename TArray, DataType DT>
436 struct PackedArrayInitBase final : ArrayInitBase<TArray, DT> {
437 using ArrayInitBase<TArray, DT>::ArrayInitBase;
440 * Before allocating, check if the allocation would cause the request to OOM.
442 * @throws: RequestMemoryExceededException if allocating would OOM.
444 PackedArrayInitBase(size_t n, CheckAllocation) :
445 ArrayInitBase<TArray, DT>(n, CheckAllocation{})
447 auto allocsz = sizeof(ArrayData) + sizeof(TypedValue) * n;
448 if (UNLIKELY(allocsz > kMaxSmallSize && tl_heap->preAllocOOM(allocsz))) {
449 check_non_safepoint_surprise();
451 this->m_arr = TArray::MakeReserve(n);
452 assertx(this->m_arr->hasExactlyOneRef());
453 check_non_safepoint_surprise();
456 PackedArrayInitBase& append(TypedValue tv) {
457 this->performOp([&]{
458 return PackedArray::AppendInPlace(this->m_arr, tvToInit(tv));
460 return *this;
462 PackedArrayInitBase& append(const Variant& v) {
463 return append(*v.asTypedValue());
469 * Initializer for a Hack vector array.
471 using VecInit = PackedArrayInitBase<detail::Vec, KindOfVec>;
473 ///////////////////////////////////////////////////////////////////////////////
475 struct VArrayInit {
476 explicit VArrayInit(size_t n)
477 : m_arr(PackedArray::MakeReserveVArray(n))
478 #ifndef NDEBUG
479 , m_addCount(0)
480 , m_expectedCount(n)
481 #endif
483 assertx(m_arr->hasExactlyOneRef());
486 VArrayInit(VArrayInit&& other) noexcept
487 : m_arr(other.m_arr)
488 #ifndef NDEBUG
489 , m_addCount(other.m_addCount)
490 , m_expectedCount(other.m_expectedCount)
491 #endif
493 assertx(!m_arr || m_arr->isHAMSafeVArray());
494 other.m_arr = nullptr;
495 #ifndef NDEBUG
496 other.m_expectedCount = 0;
497 #endif
500 VArrayInit(const VArrayInit&) = delete;
501 VArrayInit& operator=(const VArrayInit&) = delete;
503 ~VArrayInit() {
504 // In case an exception interrupts the initialization.
505 assertx(!m_arr || (m_arr->hasExactlyOneRef() &&
506 m_arr->isHAMSafeVArray()));
507 if (m_arr) m_arr->release();
510 VArrayInit& append(TypedValue tv) {
511 performOp([&]{ return PackedArray::AppendInPlace(m_arr, tvToInit(tv)); });
512 return *this;
514 VArrayInit& append(const Variant& v) {
515 return append(*v.asTypedValue());
518 Variant toVariant() {
519 assertx(m_arr->hasExactlyOneRef());
520 assertx(m_arr->isHAMSafeVArray());
521 auto const ptr = m_arr;
522 m_arr = nullptr;
523 #ifndef NDEBUG
524 m_expectedCount = 0; // reset; no more adds allowed
525 #endif
526 return Variant(ptr, ptr->toDataType(), Variant::ArrayInitCtor{});
529 Array toArray() {
530 assertx(m_arr->hasExactlyOneRef());
531 assertx(m_arr->isHAMSafeVArray());
532 auto const ptr = m_arr;
533 m_arr = nullptr;
534 #ifndef NDEBUG
535 m_expectedCount = 0; // reset; no more adds allowed
536 #endif
537 return Array(ptr, Array::ArrayInitCtor::Tag);
540 ArrayData* create() {
541 assertx(m_arr->hasExactlyOneRef());
542 assertx(m_arr->isHAMSafeVArray());
543 auto const ptr = m_arr;
544 m_arr = nullptr;
545 #ifndef NDEBUG
546 m_expectedCount = 0; // reset; no more adds allowed
547 #endif
548 return ptr;
551 private:
553 template<class Operation>
554 ALWAYS_INLINE void performOp(Operation oper) {
555 DEBUG_ONLY auto newp = oper();
556 // Array escalation must not happen during these reserved initializations.
557 assertx(newp == m_arr);
558 // You cannot add/set more times than you reserved with ArrayInit.
559 assertx(++m_addCount <= m_expectedCount);
562 ArrayData* m_arr;
563 #ifndef NDEBUG
564 size_t m_addCount;
565 size_t m_expectedCount;
566 #endif
569 struct DArrayInit {
570 explicit DArrayInit(size_t n)
571 : m_arr(MixedArray::MakeReserveDArray(n))
572 #ifndef NDEBUG
573 , m_addCount(0)
574 , m_expectedCount(n)
575 #endif
577 assertx(m_arr->hasExactlyOneRef());
580 DArrayInit(size_t, CheckAllocation);
582 DArrayInit(DArrayInit&& other) noexcept
583 : m_arr(other.m_arr)
584 #ifndef NDEBUG
585 , m_addCount(other.m_addCount)
586 , m_expectedCount(other.m_expectedCount)
587 #endif
589 assertx(!m_arr || m_arr->isHAMSafeDArray());
590 other.m_arr = nullptr;
591 #ifndef NDEBUG
592 other.m_expectedCount = 0;
593 #endif
596 DArrayInit(const DArrayInit&) = delete;
597 DArrayInit& operator=(const DArrayInit&) = delete;
599 ~DArrayInit() {
600 // In case an exception interrupts the initialization.
601 assertx(!m_arr || (m_arr->hasExactlyOneRef() &&
602 m_arr->isHAMSafeDArray()));
603 if (m_arr) m_arr->release();
606 DArrayInit& append(TypedValue tv) {
607 performOp([&]{ return m_arr->append(tvToInit(tv)); });
608 return *this;
610 DArrayInit& append(const Variant& v) {
611 return append(*v.asTypedValue());
615 * Call add() on the underlying array.
617 DArrayInit& add(int64_t name, TypedValue tv,
618 bool /*keyConverted*/ = false) {
619 set(name, tv);
620 return *this;
623 DArrayInit& add(const String& name, TypedValue tv,
624 bool keyConverted = false) {
625 if (keyConverted) {
626 set(name, tv);
627 } else if (!name.isNull()) {
628 set(VarNR::MakeKey(name).tv(), tv);
630 return *this;
633 DArrayInit& add(const Variant& name, TypedValue tv,
634 bool keyConverted = false) {
635 if (keyConverted) {
636 set(name.asInitTVTmp(), tv);
637 } else {
638 auto const k = name.toKey(m_arr).tv();
639 if (!isNullType(k.m_type)) set(k, tv);
641 return *this;
644 template<typename T>
645 DArrayInit& add(const T& name, TypedValue tv,
646 bool keyConverted = false) {
647 if (keyConverted) {
648 set(name, tv);
649 } else {
650 auto const k = Variant(name).toKey(m_arr).tv();
651 if (!isNullType(k.m_type)) set(k, tv);
653 return *this;
656 #define IMPL_ADD(KeyType) \
657 DArrayInit& add(KeyType name, const Variant& v, \
658 bool keyConverted = false) { \
659 return add(name, *v.asTypedValue(), keyConverted); \
662 IMPL_ADD(int64_t)
663 IMPL_ADD(const String&)
664 IMPL_ADD(const Variant&)
665 template<typename T> IMPL_ADD(const T&)
666 #undef IMPL_ADD
669 * Call set() on the underlying ArrayData.
671 DArrayInit& set(int64_t name, TypedValue tv) {
672 performOp([&]{ return arr_init::SetInPlace(m_arr, name, tv); });
673 return *this;
675 DArrayInit& set(const String& name, TypedValue tv) {
676 performOp([&]{ return arr_init::SetInPlace(m_arr, name, tv); });
677 return *this;
679 template<class T>
680 DArrayInit& set(const T& name, TypedValue tv) {
681 performOp([&]{ return arr_init::SetInPlace(m_arr, name, tv); });
682 return *this;
685 #define IMPL_SET(KeyType) \
686 DArrayInit& set(KeyType name, const Variant& v) { \
687 return set(name, *v.asTypedValue()); \
690 IMPL_SET(int64_t)
691 IMPL_SET(const String&)
692 template<typename T> IMPL_SET(const T&)
694 DArrayInit& set(const Variant& name, const Variant& v) = delete;
695 #undef IMPL_SET
697 DArrayInit& setValidKey(TypedValue name, TypedValue v) {
698 set(tvToInit(name), v);
699 return *this;
701 DArrayInit& setValidKey(const Variant& name, const Variant& v) {
702 return setValidKey(*name.asTypedValue(), *v.asTypedValue());
705 template <IntishCast IC = IntishCast::None>
706 DArrayInit& setUnknownKey(const Variant& name, const Variant& v) {
707 auto const k = name.toKey<IC>(m_arr).tv();
708 if (LIKELY(!isNullType(k.m_type))) set(k, v.asInitTVTmp());
709 return *this;
712 Variant toVariant() {
713 assertx(m_arr->hasExactlyOneRef());
714 assertx(m_arr->isHAMSafeDArray());
715 auto const ptr = m_arr;
716 m_arr = nullptr;
717 #ifndef NDEBUG
718 m_expectedCount = 0; // reset; no more adds allowed
719 #endif
720 return Variant(ptr, ptr->toDataType(), Variant::ArrayInitCtor{});
723 Array toArray() {
724 assertx(m_arr->hasExactlyOneRef());
725 assertx(m_arr->isHAMSafeDArray());
726 auto const ptr = m_arr;
727 m_arr = nullptr;
728 #ifndef NDEBUG
729 m_expectedCount = 0; // reset; no more adds allowed
730 #endif
731 return Array(ptr, Array::ArrayInitCtor::Tag);
734 ArrayData* create() {
735 assertx(m_arr->hasExactlyOneRef());
736 assertx(m_arr->isHAMSafeDArray());
737 auto const ptr = m_arr;
738 m_arr = nullptr;
739 #ifndef NDEBUG
740 m_expectedCount = 0; // reset; no more adds allowed
741 #endif
742 return ptr;
745 private:
747 template<class Operation>
748 ALWAYS_INLINE void performOp(Operation oper) {
749 DEBUG_ONLY auto newp = oper();
750 // Array escalation must not happen during these reserved initializations.
751 assertx(newp == m_arr);
752 // You cannot add/set more times than you reserved with ArrayInit.
753 assertx(++m_addCount <= m_expectedCount);
756 ArrayData* m_arr;
757 #ifndef NDEBUG
758 size_t m_addCount;
759 size_t m_expectedCount;
760 #endif
763 ///////////////////////////////////////////////////////////////////////////////
766 * Initializer for a Hack keyset.
768 struct KeysetInit : ArrayInitBase<SetArray, KindOfKeyset> {
769 using ArrayInitBase<SetArray, KindOfKeyset>::ArrayInitBase;
772 * Before allocating, check if the allocation would cause the request to OOM.
774 * @throws RequestMemoryExceededException if allocating would OOM.
776 KeysetInit(size_t n, CheckAllocation);
778 KeysetInit& add(int64_t v) {
779 performOp([&]{ return SetArray::AddToSetInPlace(m_arr, v); });
780 return *this;
782 KeysetInit& add(StringData* v) {
783 performOp([&]{ return SetArray::AddToSetInPlace(m_arr, v); });
784 return *this;
786 KeysetInit& add(TypedValue tv) {
787 performOp([&]{ return SetArray::Append(m_arr, tvToInit(tv)); });
788 return *this;
790 KeysetInit& add(const Variant& v) {
791 return add(*v.asTypedValue());
795 ///////////////////////////////////////////////////////////////////////////////
797 namespace make_array_detail {
799 inline void varray_impl(VArrayInit&) {}
801 template<class Val, class... Vals>
802 void varray_impl(VArrayInit& init, Val&& val, Vals&&... vals) {
803 init.append(Variant(std::forward<Val>(val)));
804 varray_impl(init, std::forward<Vals>(vals)...);
807 inline void vec_impl(VecInit&) {}
809 template<class Val, class... Vals>
810 void vec_impl(VecInit& init, Val&& val, Vals&&... vals) {
811 init.append(Variant(std::forward<Val>(val)));
812 vec_impl(init, std::forward<Vals>(vals)...);
815 inline String init_key(const char* s) { return String(s); }
816 inline int64_t init_key(int k) { return k; }
817 inline int64_t init_key(int64_t k) { return k; }
818 inline const String& init_key(const String& k) { return k; }
819 inline const String init_key(StringData* k) { return String{k}; }
821 inline void map_impl(ArrayInit&) {}
823 template<class Key, class Val, class... KVPairs>
824 void map_impl(ArrayInit& init, Key&& key, Val&& val, KVPairs&&... kvpairs) {
825 init.set(init_key(std::forward<Key>(key)), Variant(std::forward<Val>(val)));
826 map_impl(init, std::forward<KVPairs>(kvpairs)...);
829 inline String darray_init_key(const char* s) { return String(s); }
830 inline int64_t darray_init_key(int k) { return k; }
831 inline int64_t darray_init_key(int64_t k) { return k; }
832 inline const String& darray_init_key(const String& k) { return k; }
833 inline const String darray_init_key(StringData* k) { return String{k}; }
835 inline void darray_impl(DArrayInit&) {}
837 template<class Key, class Val, class... KVPairs>
838 void darray_impl(DArrayInit& init, Key&& key,
839 Val&& val, KVPairs&&... kvpairs) {
840 init.set(darray_init_key(std::forward<Key>(key)),
841 Variant(std::forward<Val>(val)));
842 darray_impl(init, std::forward<KVPairs>(kvpairs)...);
845 inline String dict_init_key(const char* s) { return String(s); }
846 inline int64_t dict_init_key(int k) { return k; }
847 inline int64_t dict_init_key(int64_t k) { return k; }
848 inline StringData* dict_init_key(const String& k) { return k.get(); }
849 inline StringData* dict_init_key(StringData* k) { return k; }
851 inline void dict_impl(DictInit&) {}
853 template<class Key, class Val, class... KVPairs>
854 void dict_impl(DictInit& init, Key&& key, Val&& val, KVPairs&&... kvpairs) {
855 init.set(dict_init_key(std::forward<Key>(key)),
856 Variant(std::forward<Val>(val)));
857 dict_impl(init, std::forward<KVPairs>(kvpairs)...);
860 inline String keyset_init_key(const char* s) { return String(s); }
861 inline int64_t keyset_init_key(int k) { return k; }
862 inline int64_t keyset_init_key(int64_t k) { return k; }
863 inline StringData* keyset_init_key(const String& k) { return k.get(); }
864 inline StringData* keyset_init_key(StringData* k) { return k; }
866 inline void keyset_impl(KeysetInit&) {}
868 template<class Val, class... Vals>
869 void keyset_impl(KeysetInit& init, Val&& val, Vals&&... vals) {
870 init.add(keyset_init_key(std::forward<Val>(val)));
871 keyset_impl(init, std::forward<Vals>(vals)...);
877 * Helper for creating packed varrays.
879 * Usage:
881 * auto newArray = make_varray(1, 2, 3, 4);
883 template<class... Vals>
884 Array make_varray(Vals&&... vals) {
885 static_assert(sizeof...(vals), "use Array::CreateVArray() instead");
886 VArrayInit init(sizeof...(vals));
887 make_array_detail::varray_impl(init, std::forward<Vals>(vals)...);
888 return init.toArray();
892 * Helper for creating Hack vec arrays (vector-like).
894 * Usage:
896 * auto newArray = make_vec_array(1, 2, 3, 4);
898 template<class... Vals>
899 Array make_vec_array(Vals&&... vals) {
900 static_assert(sizeof...(vals), "use Array::CreateVec() instead");
901 VecInit init(sizeof...(vals));
902 make_array_detail::vec_impl(init, std::forward<Vals>(vals)...);
903 return init.toArray();
906 template<class... Vals>
907 Array make_vec_array_tagged(arrprov::Tag tag, Vals&&... vals) {
908 arrprov::TagOverride to{tag};
909 auto ret = make_vec_array(std::forward<Vals>(vals)...);
910 return ret;
914 * Helper for creating map-like arrays (kMixedKind). Takes pairs of
915 * arguments for the keys and values.
917 * Usage:
919 * auto newArray = make_map_array(keyOne, valueOne,
920 * otherKey, otherValue);
922 * TODO(T58820726): Remove by migrating remaining callers.
924 template<class... KVPairs>
925 Array make_map_array(KVPairs&&... kvpairs) {
926 static_assert(
927 sizeof...(kvpairs) % 2 == 0, "make_map_array needs key value pairs");
928 ArrayInit init(sizeof...(kvpairs) / 2, ArrayInit::Map{});
929 make_array_detail::map_impl(init, std::forward<KVPairs>(kvpairs)...);
930 return init.toArray();
934 * Helper for creating darrays. Takes pairs of arguments for the keys and
935 * values.
937 * Usage:
939 * auto newArray = make_darray(keyOne, valueOne, otherKey, otherValue);
942 template<class... KVPairs>
943 Array make_darray(KVPairs&&... kvpairs) {
944 static_assert(sizeof...(kvpairs), "use Array::CreateDArray() instead");
945 static_assert(
946 sizeof...(kvpairs) % 2 == 0, "make_darray needs key value pairs");
947 DArrayInit init(sizeof...(kvpairs) / 2);
948 make_array_detail::darray_impl(init, std::forward<KVPairs>(kvpairs)...);
949 return init.toArray();
952 template<class... KVPairs>
953 Array make_darray_tagged(arrprov::Tag tag, KVPairs&&... kvpairs) {
954 arrprov::TagOverride to{tag};
955 auto ret = make_darray(std::forward<KVPairs>(kvpairs)...);
956 return ret;
960 * Helper for creating Hack dictionaries.
962 * Usage:
964 * auto newArray = make_dict_array(1, 2, 3, 4);
966 template<class... KVPairs>
967 Array make_dict_array(KVPairs&&... kvpairs) {
968 static_assert(sizeof...(kvpairs), "use Array::CreateDict() instead");
969 static_assert(
970 sizeof...(kvpairs) % 2 == 0, "make_dict_array needs key value pairs");
971 DictInit init(sizeof...(kvpairs) / 2);
972 make_array_detail::dict_impl(init, std::forward<KVPairs>(kvpairs)...);
973 return init.toArray();
977 * Helper for creating Hack keysets.
979 * Usage:
981 * auto newArray = make_keyset_array(1, 2, 3, 4);
983 template<class... Vals>
984 Array make_keyset_array(Vals&&... vals) {
985 static_assert(sizeof...(vals), "use Array::CreateKeyset() instead");
986 KeysetInit init(sizeof...(vals));
987 make_array_detail::keyset_impl(init, std::forward<Vals>(vals)...);
988 return init.toArray();
991 ///////////////////////////////////////////////////////////////////////////////
993 template <typename TArray>
994 MixedPHPArrayInitBase<TArray>::MixedPHPArrayInitBase(size_t n,
995 Map,
996 CheckAllocation)
997 // TODO(T58820726): Remove by migrating remaining callers.
998 : ArrayInitBase<TArray, KindOfUninit>(n, CheckAllocation{})
1000 if (n > std::numeric_limits<int>::max()) {
1001 tl_heap->forceOOM();
1002 check_non_safepoint_surprise();
1004 auto const allocsz = MixedArray::computeAllocBytes(
1005 MixedArray::computeScaleFromSize(n)
1007 if (UNLIKELY(allocsz > kMaxSmallSize && tl_heap->preAllocOOM(allocsz))) {
1008 check_non_safepoint_surprise();
1010 this->m_arr = TArray::MakeReserve(n);
1011 assertx(this->m_arr->hasExactlyOneRef());
1012 check_non_safepoint_surprise();
1015 ///////////////////////////////////////////////////////////////////////////////
1019 #endif