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 +----------------------------------------------------------------------+
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"
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.
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
));
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
))
87 assertx(m_arr
->hasExactlyOneRef());
90 ArrayInitBase(ArrayInitBase
&& other
) noexcept
93 , m_addCount(other
.m_addCount
)
94 , m_expectedCount(other
.m_expectedCount
)
97 assertx(!m_arr
|| DT
== KindOfUninit
|| m_arr
->toDataType() == DT
);
98 other
.m_arr
= nullptr;
100 other
.m_expectedCount
= 0;
104 ArrayInitBase(const ArrayInitBase
&) = delete;
105 ArrayInitBase
& operator=(const ArrayInitBase
&) = delete;
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 /////////////////////////////////////////////////////////////////////////////
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
{});
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
;
136 m_expectedCount
= 0; // reset; no more adds allowed
141 /////////////////////////////////////////////////////////////////////////////
145 * Checked-allocation constructor.
147 * Only used by the constructors of derived classes.
149 ArrayInitBase(size_t n
, CheckAllocation
)
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
);
169 size_t m_expectedCount
;
173 ///////////////////////////////////////////////////////////////////////////////
176 * Dummy MixedArray-like bags of statics for Hack arrays.
181 static constexpr auto MakeReserve
= &PackedArray::MakeReserveVArray
;
182 static constexpr auto Release
= PackedArray::Release
;
186 static constexpr auto MakeReserve
= &MixedArray::MakeReserveDArray
;
187 static constexpr auto Release
= MixedArray::Release
;
191 static constexpr auto MakeReserve
= &PackedArray::MakeReserveVec
;
192 static constexpr auto Release
= PackedArray::Release
;
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
> {
211 // This is the same as map right now, but is here for documentation
212 // so we can find them later.
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
) {
237 return MixedArray::Append(this->m_arr
, tvToInit(tv
));
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
); });
252 MixedPHPArrayInitBase
& set(const String
& name
, TypedValue tv
) {
253 this->performOp([&]{ return arr_init::SetInPlace(this->m_arr
, name
, tv
); });
257 MixedPHPArrayInitBase
& set(const T
& name
, TypedValue tv
) {
258 this->performOp([&]{ return arr_init::SetInPlace(this->m_arr
, name
, tv
); });
262 #define IMPL_SET(KeyType) \
263 MixedPHPArrayInitBase& set(KeyType name, const Variant& v) { \
264 return set(name, *v.asTypedValue()); \
268 IMPL_SET(const String
&)
269 template<typename T
> IMPL_SET(const T
&)
271 MixedPHPArrayInitBase
& set(const Variant
& name
, const Variant
& v
) = delete;
276 * Same as set(), but for the deleted double `const Variant&' overload.
278 MixedPHPArrayInitBase
& setValidKey(TypedValue name
, TypedValue v
) {
279 set(tvToInit(name
), v
);
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());
302 * Call add() on the underlying array.
304 MixedPHPArrayInitBase
& add(int64_t name
, TypedValue tv
,
305 bool /*keyConverted*/ = false) {
307 return MixedArray::AddInt(this->m_arr
, name
, tvToInit(tv
), false);
312 MixedPHPArrayInitBase
& add(const String
& name
, TypedValue tv
,
313 bool keyConverted
= false) {
316 return MixedArray::AddStr(this->m_arr
, name
.get(), tvToInit(tv
), false);
318 } else if (!name
.isNull()) {
319 set(VarNR::MakeKey(name
).tv(), tv
);
324 MixedPHPArrayInitBase
& add(const Variant
& name
, TypedValue tv
,
325 bool keyConverted
= false) {
327 set(name
.asInitTVTmp(), tv
);
329 auto const k
= name
.toKey(this->m_arr
).tv();
330 if (!isNullType(k
.m_type
)) set(k
, tv
);
336 MixedPHPArrayInitBase
& add(const T
& name
, TypedValue tv
,
337 bool keyConverted
= false) {
341 auto const k
= Variant(name
).toKey(this->m_arr
).tv();
342 if (!isNullType(k
.m_type
)) set(k
, tv
);
347 #define IMPL_ADD(KeyType) \
348 MixedPHPArrayInitBase& add(KeyType name, const Variant& v, \
349 bool keyConverted = false) { \
350 return add(name, *v.asTypedValue(), keyConverted); \
354 IMPL_ADD(const String
&)
355 IMPL_ADD(const Variant
&)
356 template<typename T
> IMPL_ADD(const T
&)
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
)); });
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
); });
394 DictInit
& set(StringData
* name
, TypedValue tv
) {
395 performOp([&]{ return arr_init::SetInPlace(m_arr
, name
, tv
); });
398 DictInit
& set(const String
& name
, TypedValue tv
) {
399 performOp([&]{ return arr_init::SetInPlace(m_arr
, name
, tv
); });
403 #define IMPL_SET(KeyType) \
404 DictInit& set(KeyType name, const Variant& v) { \
405 return set(name, *v.asTypedValue()); \
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;
418 DictInit
& setValidKey(TypedValue name
, TypedValue v
) {
420 assertx(isIntType(name
.m_type
) || isStringType(name
.m_type
));
421 return arr_init::SetInPlace(m_arr
, name
, v
);
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
) {
458 return PackedArray::AppendInPlace(this->m_arr
, tvToInit(tv
));
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 ///////////////////////////////////////////////////////////////////////////////
476 explicit VArrayInit(size_t n
)
477 : m_arr(PackedArray::MakeReserveVArray(n
))
483 assertx(m_arr
->hasExactlyOneRef());
486 VArrayInit(VArrayInit
&& other
) noexcept
489 , m_addCount(other
.m_addCount
)
490 , m_expectedCount(other
.m_expectedCount
)
493 assertx(!m_arr
|| m_arr
->isHAMSafeVArray());
494 other
.m_arr
= nullptr;
496 other
.m_expectedCount
= 0;
500 VArrayInit(const VArrayInit
&) = delete;
501 VArrayInit
& operator=(const VArrayInit
&) = delete;
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
)); });
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
;
524 m_expectedCount
= 0; // reset; no more adds allowed
526 return Variant(ptr
, ptr
->toDataType(), Variant::ArrayInitCtor
{});
530 assertx(m_arr
->hasExactlyOneRef());
531 assertx(m_arr
->isHAMSafeVArray());
532 auto const ptr
= m_arr
;
535 m_expectedCount
= 0; // reset; no more adds allowed
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
;
546 m_expectedCount
= 0; // reset; no more adds allowed
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
);
565 size_t m_expectedCount
;
570 explicit DArrayInit(size_t n
)
571 : m_arr(MixedArray::MakeReserveDArray(n
))
577 assertx(m_arr
->hasExactlyOneRef());
580 DArrayInit(size_t, CheckAllocation
);
582 DArrayInit(DArrayInit
&& other
) noexcept
585 , m_addCount(other
.m_addCount
)
586 , m_expectedCount(other
.m_expectedCount
)
589 assertx(!m_arr
|| m_arr
->isHAMSafeDArray());
590 other
.m_arr
= nullptr;
592 other
.m_expectedCount
= 0;
596 DArrayInit(const DArrayInit
&) = delete;
597 DArrayInit
& operator=(const DArrayInit
&) = delete;
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
)); });
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) {
623 DArrayInit
& add(const String
& name
, TypedValue tv
,
624 bool keyConverted
= false) {
627 } else if (!name
.isNull()) {
628 set(VarNR::MakeKey(name
).tv(), tv
);
633 DArrayInit
& add(const Variant
& name
, TypedValue tv
,
634 bool keyConverted
= false) {
636 set(name
.asInitTVTmp(), tv
);
638 auto const k
= name
.toKey(m_arr
).tv();
639 if (!isNullType(k
.m_type
)) set(k
, tv
);
645 DArrayInit
& add(const T
& name
, TypedValue tv
,
646 bool keyConverted
= false) {
650 auto const k
= Variant(name
).toKey(m_arr
).tv();
651 if (!isNullType(k
.m_type
)) set(k
, tv
);
656 #define IMPL_ADD(KeyType) \
657 DArrayInit& add(KeyType name, const Variant& v, \
658 bool keyConverted = false) { \
659 return add(name, *v.asTypedValue(), keyConverted); \
663 IMPL_ADD(const String
&)
664 IMPL_ADD(const Variant
&)
665 template<typename T
> IMPL_ADD(const T
&)
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
); });
675 DArrayInit
& set(const String
& name
, TypedValue tv
) {
676 performOp([&]{ return arr_init::SetInPlace(m_arr
, name
, tv
); });
680 DArrayInit
& set(const T
& name
, TypedValue tv
) {
681 performOp([&]{ return arr_init::SetInPlace(m_arr
, name
, tv
); });
685 #define IMPL_SET(KeyType) \
686 DArrayInit& set(KeyType name, const Variant& v) { \
687 return set(name, *v.asTypedValue()); \
691 IMPL_SET(const String
&)
692 template<typename T
> IMPL_SET(const T
&)
694 DArrayInit
& set(const Variant
& name
, const Variant
& v
) = delete;
697 DArrayInit
& setValidKey(TypedValue name
, TypedValue v
) {
698 set(tvToInit(name
), v
);
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());
712 Variant
toVariant() {
713 assertx(m_arr
->hasExactlyOneRef());
714 assertx(m_arr
->isHAMSafeDArray());
715 auto const ptr
= m_arr
;
718 m_expectedCount
= 0; // reset; no more adds allowed
720 return Variant(ptr
, ptr
->toDataType(), Variant::ArrayInitCtor
{});
724 assertx(m_arr
->hasExactlyOneRef());
725 assertx(m_arr
->isHAMSafeDArray());
726 auto const ptr
= m_arr
;
729 m_expectedCount
= 0; // reset; no more adds allowed
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
;
740 m_expectedCount
= 0; // reset; no more adds allowed
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
);
759 size_t m_expectedCount
;
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
); });
782 KeysetInit
& add(StringData
* v
) {
783 performOp([&]{ return SetArray::AddToSetInPlace(m_arr
, v
); });
786 KeysetInit
& add(TypedValue tv
) {
787 performOp([&]{ return SetArray::Append(m_arr
, tvToInit(tv
)); });
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.
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).
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
)...);
914 * Helper for creating map-like arrays (kMixedKind). Takes pairs of
915 * arguments for the keys and values.
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
) {
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
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");
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
)...);
960 * Helper for creating Hack dictionaries.
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");
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.
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
,
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 ///////////////////////////////////////////////////////////////////////////////