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 +----------------------------------------------------------------------+
17 #include "hphp/runtime/base/array-init.h"
18 #include "hphp/runtime/base/array-iterator.h"
19 #include "hphp/runtime/base/execution-context.h"
20 #include "hphp/runtime/base/sort-helpers.h"
21 #include "hphp/runtime/base/tv-mutate.h"
22 #include "hphp/runtime/base/tv-variant.h"
23 #include "hphp/runtime/base/mixed-array-defs.h"
24 #include "hphp/runtime/base/packed-array-defs.h"
25 #include "hphp/runtime/base/set-array.h"
26 #include "hphp/runtime/base/array-iterator-defs.h"
27 #include "hphp/runtime/vm/jit/translator-inline.h"
29 #include <folly/ScopeGuard.h>
32 ///////////////////////////////////////////////////////////////////////////////
35 * preSort() does an initial pass over the array to do some preparatory work
36 * before the sort algorithm runs. For sorts that use builtin comparators, the
37 * types of values are also observed during this first pass. By observing the
38 * types during this initial pass, we can often use a specialized comparator
39 * and avoid performing type checks during the actual sort.
41 template <typename AccessorT
, class ArrayT
>
42 SortFlavor
genericPreSort(ArrayT
& arr
,
45 assertx(arr
.m_size
> 0);
46 if (!checkTypes
&& arr
.m_size
== arr
.m_used
) {
47 // No need to loop over the elements, we're done
50 typename
ArrayT::Elm
* start
= arr
.data();
51 typename
ArrayT::Elm
* end
= arr
.data() + arr
.m_used
;
52 bool allInts UNUSED
= true;
53 bool allStrs UNUSED
= true;
56 while (!start
->isTombstone()) {
57 allInts
= (allInts
&& acc
.isInt(*start
));
58 allStrs
= (allStrs
&& acc
.isStr(*start
));
65 while (!start
->isTombstone()) {
76 while (end
->isTombstone()) {
82 memcpy(start
, end
, sizeof(typename
ArrayT::Elm
));
85 arr
.m_used
= start
- arr
.data();
86 assertx(arr
.m_size
== arr
.m_used
);
88 return allStrs
? StringSort
: allInts
? IntegerSort
: GenericSort
;
93 template <typename AccessorT
>
94 SortFlavor
MixedArray::preSort(const AccessorT
& acc
, bool checkTypes
) {
95 return genericPreSort(*this, acc
, checkTypes
);
98 template <typename AccessorT
>
99 SortFlavor
SetArray::preSort(const AccessorT
& acc
, bool checkTypes
) {
100 auto const oldUsed UNUSED
= m_used
;
101 auto flav
= genericPreSort(*this, acc
, checkTypes
);
102 assertx(ClearElms(data() + m_used
, oldUsed
- m_used
));
107 * postSort() runs after the sort has been performed. For MixedArray, postSort()
108 * handles rebuilding the hash. Also, if resetKeys is true, postSort() will
109 * renumber the keys 0 thru n-1.
111 void MixedArray::postSort(bool resetKeys
) { // nothrow guarantee
113 auto const ht
= initHash(m_scale
);
114 auto const mask
= this->mask();
116 for (uint32_t pos
= 0; pos
< m_used
; ++pos
) {
117 auto& e
= data()[pos
];
118 if (e
.hasStrKey()) decRefStr(e
.skey
);
119 auto h
= hash_int64(pos
);
121 *findForNewInsert(ht
, mask
, h
) = pos
;
125 auto data
= this->data();
126 for (uint32_t pos
= 0; pos
< m_used
; ++pos
) {
128 *findForNewInsert(ht
, mask
, e
.probe()) = pos
;
134 * postSort() runs after the sort has been performed. For SetArray, postSort()
135 * handles rebuilding the hash.
137 void SetArray::postSort() { // nothrow guarantee
139 auto const ht
= initHash(m_scale
);
140 auto const mask
= this->mask();
142 assertx(m_used
== m_size
);
143 for (uint32_t i
= 0; i
< m_used
; ++i
) {
145 assertx(!elm
.isInvalid());
146 *findForNewInsert(ht
, mask
, elm
.hash()) = i
;
150 ArrayData
* MixedArray::EscalateForSort(ArrayData
* ad
, SortFunction sf
) {
151 auto a
= asMixed(ad
);
152 // We can uncomment later if we want this feature.
153 // if (a->m_size <= 1 && !isSortFamily(sf)) {
156 if (UNLIKELY(hasUserDefinedCmp(sf
) || a
->cowCheck())) {
157 auto ret
= a
->copyMixed();
158 assertx(ret
->hasExactlyOneRef());
164 ArrayData
* SetArray::EscalateForSort(ArrayData
* ad
, SortFunction sf
) {
166 if (UNLIKELY(hasUserDefinedCmp(sf
) || a
->cowCheck())) {
167 auto ret
= a
->copySet();
168 assertx(ret
->hasExactlyOneRef());
174 ArrayData
* PackedArray::EscalateForSort(ArrayData
* ad
, SortFunction sf
) {
175 if (sf
== SORTFUNC_KSORT
) {
176 return ad
; // trivial for packed arrays.
178 if (isSortFamily(sf
)) { // sort/rsort/usort
179 if (UNLIKELY(ad
->cowCheck())) {
180 auto ret
= PackedArray::Copy(ad
);
181 assertx(ret
->hasExactlyOneRef());
186 if (ad
->m_size
<= 1) {
187 if (ad
->isVecArray()) {
188 auto ret
= PackedArray::ToDictVec(ad
, ad
->cowCheck());
189 assertx(ret
->hasExactlyOneRef());
194 assertx(checkInvariants(ad
));
195 auto ret
= ad
->isVecArray()
196 ? PackedArray::ToDictVec(ad
, ad
->cowCheck())
198 assertx(ret
->hasExactlyOneRef());
202 #define SORT_CASE(flag, cmp_type, acc_type) \
205 cmp_type##Compare<acc_type, flag, true> comp; \
206 HPHP::Sort::sort(data_begin, data_end, comp); \
208 cmp_type##Compare<acc_type, flag, false> comp; \
209 HPHP::Sort::sort(data_begin, data_end, comp); \
213 #define SORT_CASE_BLOCK(cmp_type, acc_type) \
214 switch (sort_flags) { \
215 default: /* fall through to SORT_REGULAR case */ \
216 SORT_CASE(SORT_REGULAR, cmp_type, acc_type) \
217 SORT_CASE(SORT_NUMERIC, cmp_type, acc_type) \
218 SORT_CASE(SORT_STRING, cmp_type, acc_type) \
219 SORT_CASE(SORT_STRING_CASE, cmp_type, acc_type) \
220 SORT_CASE(SORT_LOCALE_STRING, cmp_type, acc_type) \
221 SORT_CASE(SORT_NATURAL, cmp_type, acc_type) \
222 SORT_CASE(SORT_NATURAL_CASE, cmp_type, acc_type) \
225 #define CALL_SORT(acc_type) \
226 if (flav == StringSort) { \
227 SORT_CASE_BLOCK(StrElm, acc_type) \
228 } else if (flav == IntegerSort) { \
229 SORT_CASE_BLOCK(IntElm, acc_type) \
231 SORT_CASE_BLOCK(Elm, acc_type) \
234 #define SORT_BODY(acc_type, resetKeys) \
236 if (UNLIKELY(strong_iterators_exist())) { \
237 free_strong_iterators(a); \
245 SortFlavor flav = a->preSort<acc_type>(acc_type(), true); \
246 a->m_pos = ssize_t(0); \
248 CALL_SORT(acc_type); \
250 /* Make sure we leave the array in a consistent state */ \
251 a->postSort(resetKeys); \
254 a->postSort(resetKeys); \
257 void MixedArray::Ksort(ArrayData
* ad
, int sort_flags
, bool ascending
) {
258 auto a
= asMixed(ad
);
259 auto data_begin
= a
->data();
260 auto data_end
= data_begin
+ a
->m_size
;
261 SORT_BODY(AssocKeyAccessor
<MixedArray::Elm
>, false);
264 void MixedArray::Sort(ArrayData
* ad
, int sort_flags
, bool ascending
) {
265 auto a
= asMixed(ad
);
266 auto data_begin
= a
->data();
267 auto data_end
= data_begin
+ a
->m_size
;
268 SORT_BODY(AssocValAccessor
<MixedArray::Elm
>, true);
271 void MixedArray::Asort(ArrayData
* ad
, int sort_flags
, bool ascending
) {
272 auto a
= asMixed(ad
);
273 auto data_begin
= a
->data();
274 auto data_end
= data_begin
+ a
->m_size
;
275 SORT_BODY(AssocValAccessor
<MixedArray::Elm
>, false);
278 void SetArray::Ksort(ArrayData
* ad
, int sort_flags
, bool ascending
) {
280 auto data_begin
= a
->data();
281 auto data_end
= data_begin
+ a
->m_size
;
282 if (!a
->m_size
) return;
283 auto acc
= AssocKeyAccessor
<SetArray::Elm
>();
284 SortFlavor flav
= a
->preSort(acc
, true);
287 /* Make sure we leave the array in a consistent state */
290 CALL_SORT(AssocKeyAccessor
<SetArray::Elm
>);
293 void PackedArray::Sort(ArrayData
* ad
, int sort_flags
, bool ascending
) {
294 assertx(checkInvariants(ad
));
295 if (ad
->m_size
<= 1) {
298 assertx(!ad
->hasMultipleRefs());
300 if (UNLIKELY(strong_iterators_exist())) {
301 free_strong_iterators(a
);
303 SortFlavor flav
= preSort(ad
);
305 auto data_begin
= packedData(ad
);
306 auto data_end
= data_begin
+ a
->m_size
;
307 CALL_SORT(TVAccessor
);
311 #undef SORT_CASE_BLOCK
314 #define USER_SORT_BODY(acc_type, resetKeys) \
316 if (UNLIKELY(strong_iterators_exist())) { \
317 free_strong_iterators(a); \
327 vm_decode_function(cmp_function, cf(), false, ctx); \
331 a->preSort<acc_type>(acc_type(), false); \
332 a->m_pos = ssize_t(0); \
334 /* Make sure we leave the array in a consistent state */ \
335 a->postSort(resetKeys); \
337 ElmUCompare<acc_type> comp; \
339 HPHP::Sort::sort(a->data(), a->data() + a->m_size, comp); \
343 bool MixedArray::Uksort(ArrayData
* ad
, const Variant
& cmp_function
) {
344 auto a
= asMixed(ad
);
345 USER_SORT_BODY(AssocKeyAccessor
<MixedArray::Elm
>, false);
348 bool MixedArray::Usort(ArrayData
* ad
, const Variant
& cmp_function
) {
349 auto a
= asMixed(ad
);
350 USER_SORT_BODY(AssocValAccessor
<MixedArray::Elm
>, true);
353 bool MixedArray::Uasort(ArrayData
* ad
, const Variant
& cmp_function
) {
354 auto a
= asMixed(ad
);
355 USER_SORT_BODY(AssocValAccessor
<MixedArray::Elm
>, false);
358 bool SetArray::Uksort(ArrayData
* ad
, const Variant
& cmp_function
) {
360 if (!a
->m_size
) return true;
363 vm_decode_function(cmp_function
, cf(), false, ctx
);
367 auto acc
= AssocKeyAccessor
<SetArray::Elm
>();
368 a
->preSort(acc
, false);
371 /* Make sure we leave the array in a consistent state */
374 ElmUCompare
<AssocKeyAccessor
<SetArray::Elm
>> comp
;
376 HPHP::Sort::sort(a
->data(), a
->data() + a
->m_size
, comp
);
380 SortFlavor
PackedArray::preSort(ArrayData
* ad
) {
381 assertx(checkInvariants(ad
));
382 auto const data
= packedData(ad
);
384 uint32_t sz
= ad
->m_size
;
387 for (uint32_t i
= 0; i
< sz
; ++i
) {
388 allInts
= (allInts
&& acc
.isInt(data
[i
]));
389 allStrs
= (allStrs
&& acc
.isStr(data
[i
]));
391 return allStrs
? StringSort
: allInts
? IntegerSort
: GenericSort
;
394 bool PackedArray::Usort(ArrayData
* ad
, const Variant
& cmp_function
) {
395 assertx(checkInvariants(ad
));
396 if (ad
->m_size
<= 1) {
399 assertx(!ad
->hasMultipleRefs());
400 if (UNLIKELY(strong_iterators_exist())) {
401 free_strong_iterators(ad
);
403 ElmUCompare
<TVAccessor
> comp
;
406 vm_decode_function(cmp_function
, cf(), false, ctx
);
411 auto const data
= packedData(ad
);
412 Sort::sort(data
, data
+ ad
->m_size
, comp
);
416 #undef USER_SORT_BODY
418 ///////////////////////////////////////////////////////////////////////////////