2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010- 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 #include <runtime/base/complex_types.h>
18 #include <runtime/base/types.h>
19 #include <runtime/base/comparisons.h>
20 #include <util/exception.h>
21 #include <runtime/base/shared/shared_map.h>
22 #include <runtime/base/variable_serializer.h>
23 #include <runtime/base/variable_unserializer.h>
24 #include <runtime/base/comparisons.h>
25 #include <runtime/base/zend/zend_string.h>
26 #include <runtime/base/zend/zend_qsort.h>
27 #include <runtime/base/zend/zend_printf.h>
28 #include <runtime/base/array/array_util.h>
29 #include <runtime/base/runtime_option.h>
30 #include <runtime/ext/ext_iconv.h>
31 #include <unicode/coll.h> // icu
32 #include <util/parser/hphp.tab.hpp>
36 const Array null_array
= Array();
37 const Array empty_array
= HphpArray::GetStaticEmptyArray();
39 void Array::setEvalScalar() const {
40 Array
* thisPtr
= const_cast<Array
*>(this);
41 if (!m_px
) *thisPtr
= ArrayData::Create();
42 if (!m_px
->isStatic()) {
43 ArrayData
*ad
= ArrayData::GetScalarArray(m_px
);
48 ///////////////////////////////////////////////////////////////////////////////
51 Array
Array::Create(CVarRef name
, CVarRef var
) {
52 return ArrayData::Create(name
.isString() ? name
.toKey() : name
, var
);
57 ///////////////////////////////////////////////////////////////////////////////
60 Array
&Array::operator=(ArrayData
*data
) {
61 ArrayBase::operator=(data
);
66 Array
&Array::operator=(CArrRef arr
) {
67 ArrayBase::operator=(arr
.m_px
);
71 Array
&Array::operator=(CVarRef var
) {
72 return operator=(var
.toArray());
76 Array
&Array::operator = (Variant
&& v
) {
77 if (v
.m_type
== KindOfArray
) {
81 *this = const_cast<CVarRef
>(v
);
86 Array
Array::operator+(ArrayData
*data
) const {
87 return Array(m_px
).operator+=(data
);
90 Array
Array::operator+(CVarRef var
) const {
91 if (var
.getType() != KindOfArray
) {
92 throw BadArrayMergeException();
94 return operator+(var
.getArrayData());
97 Array
Array::operator+(CArrRef arr
) const {
98 return operator+(arr
.m_px
);
101 Array
&Array::operator+=(ArrayData
*data
) {
102 return mergeImpl(data
, ArrayData::Plus
);
104 Array
&Array::operator+=(CVarRef var
) {
105 if (var
.getType() != KindOfArray
) {
106 throw BadArrayMergeException();
108 return operator+=(var
.getArrayData());
111 Array
&Array::operator+=(CArrRef arr
) {
112 return mergeImpl(arr
.m_px
, ArrayData::Plus
);
115 Array
Array::diff(CVarRef array
, bool by_key
, bool by_value
,
116 PFUNC_CMP key_cmp_function
/* = NULL */,
117 const void *key_data
/* = NULL */,
118 PFUNC_CMP value_cmp_function
/* = NULL */,
119 const void *value_data
/* = NULL */) const {
120 if (!array
.isArray()) {
121 throw_bad_array_exception();
124 return diffImpl(array
.getArrayData(), by_key
, by_value
, false,
125 key_cmp_function
, key_data
,
126 value_cmp_function
, value_data
);
129 Array
Array::intersect(CVarRef array
, bool by_key
, bool by_value
,
130 PFUNC_CMP key_cmp_function
/* = NULL */,
131 const void *key_data
/* = NULL */,
132 PFUNC_CMP value_cmp_function
/* = NULL */,
133 const void *value_data
/* = NULL */) const {
134 if (!array
.isArray()) {
135 throw_bad_array_exception();
138 return diffImpl(array
.getArrayData(), by_key
, by_value
, true,
139 key_cmp_function
, key_data
,
140 value_cmp_function
, value_data
);
143 int Array::CompareAsStrings(CVarRef v1
, CVarRef v2
, const void *data
) {
144 return equalAsStr(v1
, v2
) ? 0 : -1;
147 Array
Array::diffImpl(CArrRef array
, bool by_key
, bool by_value
, bool match
,
148 PFUNC_CMP key_cmp_function
,
149 const void *key_data
,
150 PFUNC_CMP value_cmp_function
,
151 const void *value_data
) const {
152 assert(by_key
|| by_value
);
153 assert(by_key
|| key_cmp_function
== nullptr);
154 assert(by_value
|| value_cmp_function
== nullptr);
155 PFUNC_CMP value_cmp_as_string_function
= value_cmp_function
;
156 if (!value_cmp_function
) {
157 value_cmp_function
= SortStringAscending
;
158 value_cmp_as_string_function
= CompareAsStrings
;
161 Array ret
= Array::Create();
162 if (by_key
&& !key_cmp_function
) {
164 for (ArrayIter
iter(*this); iter
; ++iter
) {
165 Variant
key(iter
.first());
166 CVarRef
value(iter
.secondRef());
168 if (array
->exists(key
)) {
170 found
= value_cmp_as_string_function(
171 value
, array
.rvalAt(key
, AccessFlags::Key
), value_data
) == 0;
176 if (found
== match
) {
177 ret
.addLval(key
, true).setWithRef(value
);
183 if (!key_cmp_function
) {
184 key_cmp_function
= SortRegularAscending
;
190 int top
= array
.size();
192 const void *cmp_data
;
194 cmp
= key_cmp_function
;
197 cmp
= value_cmp_function
;
198 cmp_data
= value_data
;
200 SortImpl(perm1
, array
, opaque1
, cmp
, by_key
, cmp_data
);
202 for (ArrayIter
iter(*this); iter
; ++iter
) {
205 target
= iter
.first();
207 target
= iter
.second();
214 mid
= (max
+ min
) / 2;
215 ssize_t pos
= opaque1
.positions
[perm1
[mid
]];
216 int cmp_res
= cmp(target
,
217 by_key
? array
->getKey(pos
)
218 : array
->getValueRef(pos
),
220 if (cmp_res
> 0) { // outer is bigger
222 } else if (cmp_res
== 0) {
229 if (min
< max
) { // found
230 // if checking both, check value
231 if (by_key
&& by_value
) {
232 CVarRef
val(iter
.secondRef());
233 // Have to look up and down for matches
234 for (int i
= mid
; i
< max
; i
++) {
235 ssize_t pos
= opaque1
.positions
[perm1
[i
]];
236 if (key_cmp_function(target
, array
->getKey(pos
), key_data
) != 0) {
239 if (value_cmp_as_string_function(val
, array
->getValueRef(pos
),
246 for (int i
= mid
-1; i
>= min
; i
--) {
247 ssize_t pos
= opaque1
.positions
[perm1
[i
]];
248 if (key_cmp_function(target
, array
->getKey(pos
), key_data
) != 0) {
251 if (value_cmp_as_string_function(val
, array
->getValueRef(pos
),
264 if (found
== match
) {
265 ret
.addLval(iter
.first(), true).setWithRef(iter
.secondRef());
271 ///////////////////////////////////////////////////////////////////////////////
274 Array
&Array::merge(CArrRef arr
) {
275 return mergeImpl(arr
.m_px
, ArrayData::Merge
);
279 Array
&Array::mergeImpl(ArrayData
*data
, ArrayData::ArrayOp op
) {
280 if (m_px
== nullptr || data
== nullptr) {
281 throw BadArrayMergeException();
283 if (!data
->empty()) {
284 if (op
!= ArrayData::Merge
&& m_px
->empty()) {
285 ArrayBase::operator=(data
);
286 } else if (m_px
!= data
|| op
== ArrayData::Merge
) {
287 ArrayData
*escalated
= m_px
->append(data
, op
, m_px
->getCount() > 1);
288 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
290 } else if (op
== ArrayData::Merge
) {
296 Array
Array::slice(int offset
, int length
, bool preserve_keys
) const {
297 if (m_px
== nullptr) return Array();
298 return ArrayUtil::Slice(m_px
, offset
, length
, preserve_keys
);
301 ///////////////////////////////////////////////////////////////////////////////
304 Object
Array::toObject() const {
305 return HPHP::toObject(m_px
);
308 ///////////////////////////////////////////////////////////////////////////////
311 bool Array::same(CArrRef v2
) const {
312 if (m_px
== nullptr && v2
.get() == nullptr) return true;
313 if (m_px
&& v2
.get()) {
314 return m_px
->equal(v2
.get(), true);
319 bool Array::same(CObjRef v2
) const {
323 bool Array::equal(CArrRef v2
) const {
324 if (m_px
== nullptr || v2
.get() == nullptr) {
325 return HPHP::equal(toBoolean(), v2
.toBoolean());
327 return m_px
->equal(v2
.get(), false);
330 bool Array::equal(CObjRef v2
) const {
331 if (m_px
== nullptr || v2
.get() == nullptr) {
332 return HPHP::equal(toBoolean(), v2
.toBoolean());
337 bool Array::less(CArrRef v2
, bool flip
/* = false */) const {
338 if (m_px
== nullptr || v2
.get() == nullptr) {
339 return HPHP::less(toBoolean(), v2
.toBoolean());
342 return v2
.get()->compare(m_px
) > 0;
344 return m_px
->compare(v2
.get()) < 0;
347 bool Array::less(CObjRef v2
) const {
348 if (m_px
== nullptr || v2
.get() == nullptr) {
349 return HPHP::less(toBoolean(), v2
.toBoolean());
351 check_collection_compare(v2
.get());
355 bool Array::less(CVarRef v2
) const {
356 if (m_px
== nullptr || v2
.isNull()) {
357 return HPHP::less(toBoolean(), v2
.toBoolean());
359 if (v2
.getType() == KindOfArray
) {
360 return m_px
->compare(v2
.toArray().get()) < 0;
362 return v2
.more(*this);
365 bool Array::more(CArrRef v2
, bool flip
/* = true */) const {
366 if (m_px
== nullptr || v2
.get() == nullptr) {
367 return HPHP::more(toBoolean(), v2
.toBoolean());
370 return v2
.get()->compare(m_px
) < 0;
372 return m_px
->compare(v2
.get()) > 0;
375 bool Array::more(CObjRef v2
) const {
376 if (m_px
== nullptr || v2
.get() == nullptr) {
377 return HPHP::more(toBoolean(), v2
.toBoolean());
379 check_collection_compare(v2
.get());
383 bool Array::more(CVarRef v2
) const {
384 if (m_px
== nullptr || v2
.isNull()) {
385 return HPHP::more(toBoolean(), v2
.toBoolean());
387 if (v2
.getType() == KindOfArray
) {
388 return v2
.toArray().get()->compare(m_px
) < 0;
390 return v2
.less(*this);
393 ///////////////////////////////////////////////////////////////////////////////
397 ArrayIter
Array::begin(CStrRef context
/* = null_string */) const {
398 return ArrayIter(m_px
);
401 void Array::escalate() {
402 if (m_px
) ArrayBase::operator=(m_px
->escalate());
405 ///////////////////////////////////////////////////////////////////////////////
408 Variant
Array::rvalAt(int key
, ACCESSPARAMS_IMPL
) const {
409 if (m_px
) return m_px
->get((int64_t)key
, flags
& AccessFlags::Error
);
413 CVarRef
Array::rvalAtRef(int key
, ACCESSPARAMS_IMPL
) const {
414 if (m_px
) return m_px
->get((int64_t)key
, flags
& AccessFlags::Error
);
418 Variant
Array::rvalAt(int64_t key
, ACCESSPARAMS_IMPL
) const {
419 if (m_px
) return m_px
->get(key
, flags
& AccessFlags::Error
);
423 CVarRef
Array::rvalAtRef(int64_t key
, ACCESSPARAMS_IMPL
) const {
424 if (m_px
) return m_px
->get(key
, flags
& AccessFlags::Error
);
428 Variant
Array::rvalAt(double key
, ACCESSPARAMS_IMPL
) const {
429 if (m_px
) return m_px
->get(ToKey(key
), flags
& AccessFlags::Error
);
433 CVarRef
Array::rvalAtRef(double key
, ACCESSPARAMS_IMPL
) const {
434 if (m_px
) return m_px
->get(ToKey(key
), flags
& AccessFlags::Error
);
438 CVarRef
Array::rvalAtRef(CStrRef key
, ACCESSPARAMS_IMPL
) const {
440 bool error
= flags
& AccessFlags::Error
;
441 if (flags
& AccessFlags::Key
) return m_px
->get(key
, error
);
442 if (key
.isNull()) return m_px
->get(empty_string
, error
);
444 if (!key
->isStrictlyInteger(n
)) {
445 return m_px
->get(key
, error
);
447 return m_px
->get(n
, error
);
453 Variant
Array::rvalAt(CStrRef key
, ACCESSPARAMS_IMPL
) const {
454 return Array::rvalAtRef(key
, flags
);
457 CVarRef
Array::rvalAtRef(CVarRef key
, ACCESSPARAMS_IMPL
) const {
458 if (!m_px
) return null_variant
;
459 switch (key
.m_type
) {
462 return m_px
->get(empty_string
, flags
& AccessFlags::Error
);
465 return m_px
->get(key
.m_data
.num
, flags
& AccessFlags::Error
);
467 return m_px
->get((int64_t)key
.m_data
.dbl
, flags
& AccessFlags::Error
);
468 case KindOfStaticString
:
471 if (!(flags
& AccessFlags::Key
) &&
472 key
.m_data
.pstr
->isStrictlyInteger(n
)) {
473 return m_px
->get(n
, flags
& AccessFlags::Error
);
475 return m_px
->get(key
.asCStrRef(), flags
& AccessFlags::Error
);
479 throw_bad_type_exception("Invalid type used as key");
482 if (key
.isResource()) {
483 return m_px
->get(key
.toInt64(), flags
& AccessFlags::Error
);
485 throw_bad_type_exception("Invalid type used as key");
488 return rvalAtRef(*(key
.m_data
.pref
->var()), flags
);
496 Variant
Array::rvalAt(CVarRef key
, ACCESSPARAMS_IMPL
) const {
497 return Array::rvalAtRef(key
, flags
);
500 Variant
*Array::lvalPtr(CStrRef key
, bool forWrite
, bool create
) {
502 if (!m_px
) ArrayBase::operator=(ArrayData::Create());
503 return &lvalAt(key
, AccessFlags::Key
);
505 Variant
*ret
= nullptr;
507 ArrayData
*escalated
= m_px
->lvalPtr(key
, ret
,
508 forWrite
&& m_px
->getCount() > 1,
510 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
515 Variant
&Array::lvalAt() {
516 if (!m_px
) ArrayBase::operator=(ArrayData::Create());
517 Variant
*ret
= nullptr;
518 ArrayData
*arr
= m_px
;
519 ArrayData
*escalated
= arr
->lvalNew(ret
, arr
->getCount() > 1);
520 if (escalated
!= arr
) ArrayBase::operator=(escalated
);
525 Variant
&Array::lvalAt(CStrRef key
, ACCESSPARAMS_IMPL
) {
526 if (flags
& AccessFlags::Key
) return lvalAtImpl(key
, flags
);
527 return lvalAtImpl(key
.toKey(), flags
);
530 Variant
&Array::lvalAt(CVarRef key
, ACCESSPARAMS_IMPL
) {
531 if (flags
& AccessFlags::Key
) return lvalAtImpl(key
, flags
);
532 VarNR
k(key
.toKey());
534 return lvalAtImpl(k
, flags
);
536 return Variant::lvalBlackHole();
541 CVarRef
Array::setImpl(const T
&key
, CVarRef v
) {
543 ArrayData
*data
= ArrayData::Create(key
, v
);
544 ArrayBase::operator=(data
);
546 ArrayData
*escalated
= m_px
->set(key
, v
, (m_px
->getCount() > 1));
547 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
554 CVarRef
Array::setRefImpl(const T
&key
, CVarRef v
) {
556 ArrayData
*data
= ArrayData::CreateRef(key
, v
);
557 ArrayBase::operator=(data
);
560 ArrayData
*escalated
= m_px
->setRef(key
, v
, (m_px
->getCount() > 1));
561 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
568 CVarRef
Array::addImpl(const T
&key
, CVarRef v
) {
570 ArrayData
*data
= ArrayData::Create(key
, v
);
571 ArrayBase::operator=(data
);
573 ArrayData
*escalated
= m_px
->add(key
, v
, (m_px
->getCount() > 1));
574 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
579 CVarRef
Array::set(int64_t key
, CVarRef v
) {
580 return setImpl(key
, v
);
583 CVarRef
Array::set(CStrRef key
, CVarRef v
, bool isKey
/* = false */) {
584 if (isKey
) return setImpl(key
, v
);
585 return setImpl(key
.toKey(), v
);
588 CVarRef
Array::set(CVarRef key
, CVarRef v
, bool isKey
/* = false */) {
589 if (key
.getRawType() == KindOfInt64
) {
590 return setImpl(key
.getNumData(), v
);
592 if (isKey
) return setImpl(key
, v
);
593 VarNR
k(key
.toKey());
595 return setImpl(k
, v
);
597 return Variant::lvalBlackHole();
600 CVarRef
Array::setRef(int64_t key
, CVarRef v
) {
601 return setRefImpl(key
, v
);
604 CVarRef
Array::setRef(CStrRef key
, CVarRef v
, bool isKey
/* = false */) {
605 if (isKey
) return setRefImpl(key
, v
);
606 return setRefImpl(key
.toKey(), v
);
609 CVarRef
Array::setRef(CVarRef key
, CVarRef v
, bool isKey
/* = false */) {
610 if (key
.getRawType() == KindOfInt64
) {
611 return setRefImpl(key
.getNumData(), v
);
613 if (isKey
) return setRefImpl(key
, v
);
614 VarNR
k(key
.toKey());
616 return setRefImpl
<Variant
>(k
, v
);
618 return Variant::lvalBlackHole();
621 CVarRef
Array::add(int64_t key
, CVarRef v
) {
622 return addImpl(key
, v
);
625 CVarRef
Array::add(CStrRef key
, CVarRef v
, bool isKey
/* = false */) {
626 if (isKey
) return addImpl(key
, v
);
627 return addImpl(key
.toKey(), v
);
630 CVarRef
Array::add(CVarRef key
, CVarRef v
, bool isKey
/* = false */) {
631 if (key
.getRawType() == KindOfInt64
) {
632 return addImpl(key
.getNumData(), v
);
634 if (isKey
) return addImpl(key
, v
);
635 VarNR
k(key
.toKey());
637 return addImpl(k
, v
);
639 return Variant::lvalBlackHole();
642 Variant
&Array::addLval(CStrRef key
, bool isKey
/* = false */) {
643 if (isKey
) return addLvalImpl(key
);
644 return addLvalImpl(key
.toKey());
647 Variant
&Array::addLval(CVarRef key
, bool isKey
/* = false */) {
648 if (key
.getRawType() == KindOfInt64
) {
649 return addLvalImpl(key
.getNumData());
651 if (isKey
) return addLvalImpl(key
);
652 VarNR
k(key
.toKey());
654 return addLvalImpl(k
);
656 return Variant::lvalBlackHole();
659 ///////////////////////////////////////////////////////////////////////////////
660 // membership functions
662 bool Array::valueExists(CVarRef search_value
,
663 bool strict
/* = false */) const {
664 for (ArrayIter
iter(*this); iter
; ++iter
) {
665 if ((strict
&& iter
.secondRef().same(search_value
)) ||
666 (!strict
&& iter
.secondRef().equal(search_value
))) {
673 Variant
Array::key(CVarRef search_value
, bool strict
/* = false */) const {
674 for (ArrayIter
iter(*this); iter
; ++iter
) {
675 if ((strict
&& iter
.secondRef().same(search_value
)) ||
676 (!strict
&& iter
.secondRef().equal(search_value
))) {
680 return false; // PHP uses "false" over null in many places
683 Array
Array::keys(CVarRef search_value
/* = null_variant */,
684 bool strict
/* = false */) const {
685 if (!search_value
.isInitialized()) {
686 ArrayInit
ai(size(), ArrayInit::vectorInit
);
687 for (ArrayIter
iter(*this); iter
; ++iter
) {
688 ai
.set(iter
.first());
692 Array ret
= Array::Create();
693 for (ArrayIter
iter(*this); iter
; ++iter
) {
694 if ((strict
&& iter
.secondRef().same(search_value
)) ||
695 (!strict
&& iter
.secondRef().equal(search_value
))) {
696 ret
.append(iter
.first());
703 Array
Array::values() const {
704 ArrayInit
ai(size(), ArrayInit::vectorInit
);
705 for (ArrayIter
iter(*this); iter
; ++iter
) {
706 ai
.set(withRefBind(iter
.secondRef()));
711 bool Array::exists(CStrRef key
, bool isKey
/* = false */) const {
712 if (isKey
) return existsImpl(key
);
713 return existsImpl(key
.toKey());
716 bool Array::exists(CVarRef key
, bool isKey
/* = false */) const {
717 switch(key
.getType()) {
720 return existsImpl(key
.toInt64());
724 if (isKey
) return existsImpl(key
);
725 VarNR
k(key
.toKey());
727 return existsImpl(k
);
732 void Array::remove(CStrRef key
, bool isString
/* = false */) {
736 removeImpl(key
.toKey());
740 void Array::remove(CVarRef key
) {
741 switch(key
.getType()) {
744 removeImpl(key
.toInt64());
749 VarNR
k(key
.toKey());
755 void Array::removeAll() {
759 CVarRef
Array::append(CVarRef v
) {
761 ArrayBase::operator=(ArrayData::Create(v
));
763 ArrayData
*escalated
= m_px
->append(v
, (m_px
->getCount() > 1));
764 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
769 CVarRef
Array::appendRef(CVarRef v
) {
771 ArrayBase::operator=(ArrayData::CreateRef(v
));
773 ArrayData
*escalated
= m_px
->appendRef(v
, (m_px
->getCount() > 1));
774 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
779 CVarRef
Array::appendWithRef(CVarRef v
) {
780 if (!m_px
) ArrayBase::operator=(ArrayData::Create());
781 ArrayData
*escalated
= m_px
->appendWithRef(v
, (m_px
->getCount() > 1));
782 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
786 Variant
Array::appendOpEqual(int op
, CVarRef v
) {
787 if (!m_px
) ArrayBase::operator=(ArrayData::Create());
788 Variant
*cv
= nullptr;
789 ArrayData
*escalated
= m_px
->lvalNew(cv
, m_px
->getCount() > 1);
790 if (escalated
!= m_px
) ArrayBase::operator=(escalated
);
793 case T_CONCAT_EQUAL
: return concat_assign((*cv
), v
);
794 case T_PLUS_EQUAL
: return ((*cv
) += v
);
795 case T_MINUS_EQUAL
: return ((*cv
) -= v
);
796 case T_MUL_EQUAL
: return ((*cv
) *= v
);
797 case T_DIV_EQUAL
: return ((*cv
) /= v
);
798 case T_MOD_EQUAL
: return ((*cv
) %= v
);
799 case T_AND_EQUAL
: return ((*cv
) &= v
);
800 case T_OR_EQUAL
: return ((*cv
) |= v
);
801 case T_XOR_EQUAL
: return ((*cv
) ^= v
);
802 case T_SL_EQUAL
: return ((*cv
) <<= v
);
803 case T_SR_EQUAL
: return ((*cv
) >>= v
);
805 throw FatalErrorException(0, "invalid operator %d", op
);
809 Variant
Array::pop() {
812 ArrayData
*newarr
= m_px
->pop(ret
);
813 if (newarr
!= m_px
) ArrayBase::operator=(newarr
);
819 Variant
Array::dequeue() {
822 ArrayData
*newarr
= m_px
->dequeue(ret
);
823 if (newarr
!= m_px
) ArrayBase::operator=(newarr
);
829 void Array::prepend(CVarRef v
) {
830 if (!m_px
) operator=(Create());
832 ArrayData
*newarr
= m_px
->prepend(v
, (m_px
->getCount() > 1));
833 if (newarr
!= m_px
) ArrayBase::operator=(newarr
);
836 ///////////////////////////////////////////////////////////////////////////////
839 void Array::serialize(VariableSerializer
*serializer
,
840 bool isObject
/* = false */) const {
842 m_px
->serialize(serializer
, isObject
);
844 serializer
->writeNull();
848 void Array::unserialize(VariableUnserializer
*uns
) {
849 int64_t size
= uns
->readInt();
850 char sep
= uns
->readChar();
852 throw Exception("Expected ':' but got '%c'", sep
);
854 sep
= uns
->readChar();
856 throw Exception("Expected '{' but got '%c'", sep
);
862 // Pre-allocate an ArrayData of the given size, to avoid escalation in
863 // the middle, which breaks references.
864 operator=(ArrayInit(size
).create());
865 bool isAPC
= (uns
->getType() == VariableUnserializer::APCSerialize
);
866 for (int64_t i
= 0; i
< size
; i
++) {
867 Variant
key(uns
->unserializeKey());
868 if (!key
.isString() && !key
.isInteger()) {
869 throw Exception("Invalid key");
871 Variant
&value
= isAPC
? addLval(key
, true) :
872 lvalAt(key
, AccessFlags::Key
);
873 value
.unserialize(uns
);
877 sep
= uns
->readChar();
879 throw Exception("Expected '}' but got '%c'", sep
);
891 ///////////////////////////////////////////////////////////////////////////////
894 static int array_compare_func(const void *n1
, const void *n2
, const void *op
) {
895 int index1
= *(int*)n1
;
896 int index2
= *(int*)n2
;
897 Array::SortData
*opaque
= (Array::SortData
*)op
;
898 ssize_t pos1
= opaque
->positions
[index1
];
899 ssize_t pos2
= opaque
->positions
[index2
];
900 if (opaque
->by_key
) {
901 return opaque
->cmp_func((*opaque
->array
)->getKey(pos1
),
902 (*opaque
->array
)->getKey(pos2
),
905 return opaque
->cmp_func((*opaque
->array
)->getValueRef(pos1
),
906 (*opaque
->array
)->getValueRef(pos2
),
910 static int multi_compare_func(const void *n1
, const void *n2
, const void *op
) {
911 int index1
= *(int*)n1
;
912 int index2
= *(int*)n2
;
913 const std::vector
<Array::SortData
> *opaques
=
914 (const std::vector
<Array::SortData
> *)op
;
915 for (unsigned int i
= 0; i
< opaques
->size(); i
++) {
916 const Array::SortData
*opaque
= &opaques
->at(i
);
917 ssize_t pos1
= opaque
->positions
[index1
];
918 ssize_t pos2
= opaque
->positions
[index2
];
920 if (opaque
->by_key
) {
921 result
= opaque
->cmp_func((*opaque
->array
)->getKey(pos1
),
922 (*opaque
->array
)->getKey(pos2
),
925 result
= opaque
->cmp_func((*opaque
->array
)->getValueRef(pos1
),
926 (*opaque
->array
)->getValueRef(pos2
),
929 if (result
!= 0) return result
;
934 void Array::SortImpl(vector
<int> &indices
, CArrRef source
,
935 Array::SortData
&opaque
, Array::PFUNC_CMP cmp_func
,
936 bool by_key
, const void *data
/* = NULL */) {
939 int count
= source
.size();
943 indices
.reserve(count
);
944 for (int i
= 0; i
< count
; i
++) {
945 indices
.push_back(i
);
948 opaque
.array
= &source
;
949 opaque
.by_key
= by_key
;
950 opaque
.cmp_func
= cmp_func
;
952 opaque
.positions
.reserve(count
);
953 for (ssize_t pos
= source
->iter_begin(); pos
!= ArrayData::invalid_index
;
954 pos
= source
->iter_advance(pos
)) {
955 opaque
.positions
.push_back(pos
);
957 zend_qsort(&indices
[0], count
, sizeof(int), array_compare_func
, &opaque
);
960 void Array::sort(PFUNC_CMP cmp_func
, bool by_key
, bool renumber
,
961 const void *data
/* = NULL */) {
962 Array sorted
= Array::Create();
965 SortImpl(indices
, *this, opaque
, cmp_func
, by_key
, data
);
967 for (int i
= 0; i
< count
; i
++) {
968 ssize_t pos
= opaque
.positions
[indices
[i
]];
970 sorted
.appendWithRef(m_px
->getValueRef(pos
));
972 sorted
.addLval(m_px
->getKey(pos
), true).
973 setWithRef(m_px
->getValueRef(pos
));
979 bool Array::MultiSort(std::vector
<SortData
> &data
, bool renumber
) {
980 assert(!data
.empty());
983 for (unsigned int k
= 0; k
< data
.size(); k
++) {
984 SortData
&opaque
= data
[k
];
986 assert(opaque
.array
);
987 assert(opaque
.cmp_func
);
988 int size
= opaque
.array
->size();
991 } else if (count
!= size
) {
992 throw_invalid_argument("arrays: (inconsistent sizes)");
996 opaque
.positions
.reserve(size
);
997 CArrRef arr
= *opaque
.array
;
999 for (ssize_t pos
= arr
->iter_begin(); pos
!= ArrayData::invalid_index
;
1000 pos
= arr
->iter_advance(pos
)) {
1001 opaque
.positions
.push_back(pos
);
1009 int *indices
= (int *)malloc(sizeof(int) * count
);
1010 for (int i
= 0; i
< count
; i
++) {
1014 zend_qsort(indices
, count
, sizeof(int), multi_compare_func
, (void *)&data
);
1016 for (unsigned int k
= 0; k
< data
.size(); k
++) {
1017 SortData
&opaque
= data
[k
];
1018 CArrRef arr
= *opaque
.array
;
1021 for (int i
= 0; i
< count
; i
++) {
1022 ssize_t pos
= opaque
.positions
[indices
[i
]];
1023 Variant
k(arr
->getKey(pos
));
1024 if (renumber
&& k
.isInteger()) {
1025 sorted
.append(arr
->getValueRef(pos
));
1027 sorted
.set(k
, arr
->getValueRef(pos
));
1030 *opaque
.original
= sorted
;
1037 int Array::SortRegularAscending(CVarRef v1
, CVarRef v2
, const void *data
) {
1038 if (v1
.less(v2
)) return -1;
1039 if (v1
.equal(v2
)) return 0;
1042 int Array::SortRegularDescending(CVarRef v1
, CVarRef v2
, const void *data
) {
1043 if (v1
.less(v2
)) return 1;
1044 if (v1
.equal(v2
)) return 0;
1048 int Array::SortNumericAscending(CVarRef v1
, CVarRef v2
, const void *data
) {
1049 double d1
= v1
.toDouble();
1050 double d2
= v2
.toDouble();
1051 if (d1
< d2
) return -1;
1052 if (d1
== d2
) return 0;
1055 int Array::SortNumericDescending(CVarRef v1
, CVarRef v2
, const void *data
) {
1056 double d1
= v1
.toDouble();
1057 double d2
= v2
.toDouble();
1058 if (d1
< d2
) return 1;
1059 if (d1
== d2
) return 0;
1063 int Array::SortStringAscending(CVarRef v1
, CVarRef v2
, const void *data
) {
1064 String s1
= v1
.toString();
1065 String s2
= v2
.toString();
1066 return strcmp(s1
.data(), s2
.data());
1068 int Array::SortStringDescending(CVarRef v1
, CVarRef v2
, const void *data
) {
1069 String s1
= v1
.toString();
1070 String s2
= v2
.toString();
1071 return strcmp(s2
.data(), s1
.data());
1074 int Array::SortLocaleStringAscending(CVarRef v1
, CVarRef v2
,
1076 String s1
= v1
.toString();
1077 String s2
= v2
.toString();
1079 return strcoll(s1
.data(), s2
.data());
1082 int Array::SortLocaleStringDescending(CVarRef v1
, CVarRef v2
,
1084 String s1
= v1
.toString();
1085 String s2
= v2
.toString();
1087 return strcoll(s2
.data(), s1
.data());
1090 int Array::SortNatural(CVarRef v1
, CVarRef v2
, const void *data
) {
1091 String s1
= v1
.toString();
1092 String s2
= v2
.toString();
1093 return string_natural_cmp(s1
.data(), s1
.size(), s2
.data(), s2
.size(), 0);
1096 int Array::SortNaturalCase(CVarRef v1
, CVarRef v2
, const void *data
) {
1097 String s1
= v1
.toString();
1098 String s2
= v2
.toString();
1099 return string_natural_cmp(s1
.data(), s1
.size(), s2
.data(), s2
.size(), 1);
1102 ///////////////////////////////////////////////////////////////////////////////