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 #ifndef incl_HPHP_SORT_HELPERS_H_
18 #define incl_HPHP_SORT_HELPERS_H_
20 #include "hphp/runtime/base/builtin-functions.h"
21 #include "hphp/runtime/base/comparisons.h"
22 #include "hphp/runtime/base/execution-context.h"
23 #include "hphp/runtime/base/type-string.h"
24 #include "hphp/runtime/base/type-variant.h"
25 #include "hphp/runtime/base/zend-functions.h"
26 #include "hphp/runtime/base/zend-string.h"
27 #include "hphp/runtime/base/sort-flags.h"
28 #include "hphp/runtime/base/mixed-array.h"
29 #include "hphp/runtime/base/set-array.h"
31 #include "hphp/util/safesort.h"
34 ///////////////////////////////////////////////////////////////////////////////
36 // For sorting PackedArray and Vector
38 typedef const TypedValue
& ElmT
;
39 bool isInt(ElmT elm
) const { return elm
.m_type
== KindOfInt64
; }
40 bool isStr(ElmT elm
) const { return isStringType(elm
.m_type
); }
41 int64_t getInt(ElmT elm
) const { return elm
.m_data
.num
; }
42 StringData
* getStr(ElmT elm
) const { return elm
.m_data
.pstr
; }
43 Variant
getValue(ElmT elm
) const { return tvAsCVarRef(&elm
); }
46 template<class Self
, typename E
>
47 struct AssocKeyAccessorImpl
{
48 typedef const E
& ElmT
;
49 bool isInt(ElmT elm
) const { return elm
.hasIntKey(); }
50 bool isStr(ElmT elm
) const { return elm
.hasStrKey(); }
51 Variant
getValue(ElmT elm
) const {
53 return Self::getInt(elm
);
56 return Variant
{Self::getStr(elm
)};
60 template<typename E
> struct AssocKeyAccessor
;
63 struct AssocKeyAccessor
<MixedArray::Elm
> :
64 AssocKeyAccessorImpl
<AssocKeyAccessor
<MixedArray::Elm
>, MixedArray::Elm
> {
65 static int64_t getInt(ElmT elm
) { return elm
.ikey
; }
66 static StringData
* getStr(ElmT elm
) { return elm
.skey
; }
70 struct AssocKeyAccessor
<SetArray::Elm
> :
71 AssocKeyAccessorImpl
<AssocKeyAccessor
<SetArray::Elm
>, SetArray::Elm
> {
72 static int64_t getInt(ElmT elm
) { return elm
.intKey(); }
73 static StringData
* getStr(ElmT elm
) { return elm
.strKey(); }
76 template<typename E
> struct AssocValAccessor
{
77 typedef const E
& ElmT
;
78 bool isInt(ElmT elm
) const { return elm
.data
.m_type
== KindOfInt64
; }
79 bool isStr(ElmT elm
) const { return isStringType(elm
.data
.m_type
); }
80 int64_t getInt(ElmT elm
) const { return elm
.data
.m_data
.num
; }
81 StringData
* getStr(ElmT elm
) const { return elm
.data
.m_data
.pstr
; }
82 Variant
getValue(ElmT elm
) const { return tvAsCVarRef(&elm
.data
); }
87 * These comparators below are used together with safesort(), and safesort
88 * requires that comparators return true iff left is GREATER than right.
91 template <typename AccessorT
, int sort_flags
, bool ascending
>
92 struct IntElmCompare
{
93 typedef typename
AccessorT::ElmT ElmT
;
95 bool operator()(ElmT left
, ElmT right
) const {
96 int64_t iLeft
= acc
.getInt(left
);
97 int64_t iRight
= acc
.getInt(right
);
98 if (sort_flags
== SORT_REGULAR
|| sort_flags
== SORT_NUMERIC
) {
99 return ascending
? (iLeft
> iRight
) : (iLeft
< iRight
);
107 // Take advantage of precomputed StringDatas if they are available
108 const StringData
* sdLeft
= String::GetIntegerStringData(iLeft
);
110 sLeft
= sdLeft
->data();
111 lenLeft
= sdLeft
->size();
114 auto sl
= conv_10(iLeft
, &bufLeft
[20]);
118 const StringData
* sdRight
= String::GetIntegerStringData(iRight
);
120 sRight
= sdRight
->data();
121 lenRight
= sdRight
->size();
124 auto sl
= conv_10(iRight
, &bufRight
[20]);
126 lenRight
= sl
.size();
128 if (sort_flags
== SORT_STRING
) {
130 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
131 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
133 if (sort_flags
== SORT_STRING_CASE
) {
135 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
136 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
138 if (sort_flags
== SORT_LOCALE_STRING
) {
139 return ascending
? (strcoll(sLeft
, sRight
) > 0) :
140 (strcoll(sLeft
, sRight
) < 0);
142 if (sort_flags
== SORT_NATURAL
) {
144 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) > 0) :
145 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) < 0);
147 if (sort_flags
== SORT_NATURAL_CASE
) {
149 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) > 0) :
150 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) < 0);
157 template <typename AccessorT
, int sort_flags
, bool ascending
>
158 struct StrElmCompare
{
159 typedef typename
AccessorT::ElmT ElmT
;
161 bool operator()(ElmT left
, ElmT right
) const {
162 StringData
* sdLeft
= acc
.getStr(left
);
163 StringData
* sdRight
= acc
.getStr(right
);
164 if (sort_flags
== SORT_REGULAR
) {
165 return ascending
? (sdLeft
->compare(sdRight
) > 0) :
166 (sdLeft
->compare(sdRight
) < 0);
168 if (sort_flags
== SORT_NUMERIC
) {
169 double dLeft
= sdLeft
->toDouble();
170 double dRight
= sdRight
->toDouble();
171 return ascending
? (dLeft
> dRight
) : (dLeft
< dRight
);
173 const char* sLeft
= sdLeft
->data();
174 int lenLeft
= sdLeft
->size();
175 const char* sRight
= sdRight
->data();
176 int lenRight
= sdRight
->size();
177 if (sort_flags
== SORT_STRING
) {
179 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
180 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
182 if (sort_flags
== SORT_STRING_CASE
) {
184 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
185 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
187 if (sort_flags
== SORT_LOCALE_STRING
) {
188 return ascending
? (strcoll(sLeft
, sRight
) > 0) :
189 (strcoll(sLeft
, sRight
) < 0);
191 if (sort_flags
== SORT_NATURAL
) {
193 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) > 0) :
194 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) < 0);
196 if (sort_flags
== SORT_NATURAL_CASE
) {
198 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) > 0) :
199 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) < 0);
206 template <typename AccessorT
, int sort_flags
, bool ascending
>
208 typedef typename
AccessorT::ElmT ElmT
;
210 bool operator()(ElmT left
, ElmT right
) const {
212 if (sort_flags
== SORT_REGULAR
) {
213 if (acc
.isStr(left
)) {
214 if (LIKELY(acc
.isStr(right
))) {
215 StringData
* sLeft
= acc
.getStr(left
);
216 StringData
* sRight
= acc
.getStr(right
);
217 return ascending
? (sLeft
->compare(sRight
) > 0) :
218 (sLeft
->compare(sRight
) < 0);
220 } else if (acc
.isInt(left
)) {
221 if (LIKELY(acc
.isInt(right
))) {
222 int64_t iLeft
= acc
.getInt(left
);
223 int64_t iRight
= acc
.getInt(right
);
224 return ascending
? (iLeft
> iRight
) : (iLeft
< iRight
);
228 if (sort_flags
== SORT_NUMERIC
) {
229 if (acc
.isInt(left
)) {
230 if (LIKELY(acc
.isInt(right
))) {
231 int64_t iLeft
= acc
.getInt(left
);
232 int64_t iRight
= acc
.getInt(right
);
233 return ascending
? (iLeft
> iRight
) : (iLeft
< iRight
);
237 if (sort_flags
== SORT_STRING
|| sort_flags
== SORT_LOCALE_STRING
||
238 sort_flags
== SORT_NATURAL
|| sort_flags
== SORT_NATURAL_CASE
) {
239 if (acc
.isStr(left
)) {
240 if (LIKELY(acc
.isStr(right
))) {
241 StringData
* sdLeft
= acc
.getStr(left
);
242 StringData
* sdRight
= acc
.getStr(right
);
243 const char* sLeft
= sdLeft
->data();
244 int lenLeft
= sdLeft
->size();
245 const char* sRight
= sdRight
->data();
246 int lenRight
= sdRight
->size();
247 if (sort_flags
== SORT_STRING
) {
249 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
250 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
252 if (sort_flags
== SORT_STRING_CASE
) {
254 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
255 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
257 if (sort_flags
== SORT_LOCALE_STRING
) {
258 return ascending
? (strcoll(sLeft
, sRight
) > 0) :
259 (strcoll(sLeft
, sRight
) < 0);
261 if (sort_flags
== SORT_NATURAL
) {
263 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) > 0) :
264 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) < 0);
266 if (sort_flags
== SORT_NATURAL_CASE
) {
268 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) > 0) :
269 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) < 0);
275 Variant vLeft
= acc
.getValue(left
);
276 Variant vRight
= acc
.getValue(right
);
277 if (sort_flags
== SORT_REGULAR
) {
278 return ascending
? HPHP::more(vLeft
, vRight
) : HPHP::less(vLeft
, vRight
);
280 if (sort_flags
== SORT_NUMERIC
) {
281 double dLeft
= vLeft
.toDouble();
282 double dRight
= vRight
.toDouble();
283 return ascending
? dLeft
> dRight
: dLeft
< dRight
;
285 String strLeft
= vLeft
.toString();
286 String strRight
= vRight
.toString();
287 const char* sLeft
= strLeft
.data();
288 int lenLeft
= strLeft
.size();
289 const char* sRight
= strRight
.data();
290 int lenRight
= strRight
.size();
291 if (sort_flags
== SORT_STRING
) {
293 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
294 (string_strcmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
296 if (sort_flags
== SORT_STRING_CASE
) {
298 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) > 0) :
299 (bstrcasecmp(sLeft
, lenLeft
, sRight
, lenRight
) < 0);
301 if (sort_flags
== SORT_LOCALE_STRING
) {
303 (strcoll(sLeft
, sRight
) > 0) :
304 (strcoll(sLeft
, sRight
) < 0);
306 if (sort_flags
== SORT_NATURAL
) {
308 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) > 0) :
309 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 0) < 0);
311 if (sort_flags
== SORT_NATURAL_CASE
) {
313 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) > 0) :
314 (string_natural_cmp(sLeft
, lenLeft
, sRight
, lenRight
, 1) < 0);
321 template <typename AccessorT
>
323 typedef typename
AccessorT::ElmT ElmT
;
327 // only warn with HH syntax enabled
328 ElmUCompare() : warned(!RuntimeOption::EnableHipHopSyntax
) {}
330 bool operator()(ElmT left
, ElmT right
) const {
331 TypedValue args
[2] = {
332 *acc
.getValue(left
).asCell(),
333 *acc
.getValue(right
).asCell()
335 auto ret
= Variant::attach(
336 g_context
->invokeFuncFew(*ctx
, 2, args
)
338 if (LIKELY(ret
.isInteger())) {
339 return ret
.toInt64() > 0;
341 if (ret
.isDouble()) {
342 return ret
.toDouble() > 0.0;
344 if (ret
.isString()) {
345 int64_t lval
; double dval
;
346 auto dt
= ret
.getStringData()->isNumericWithVal(lval
, dval
, 0);
350 } else if (isDoubleType(dt
)) {
354 if (ret
.isBoolean()) {
356 raise_warning("Sort comparators should not return a boolean because "
357 "it may result in a different sort order than expected. "
358 "The comparator should return an integer (negative if "
359 "left is less than right, positive if left is greater "
360 "than right, zero if they are equal).");
364 // If the comparator returned something other than int, double, or a
365 // numeric string, we fall back to casting it to an integer.
366 return ret
.toInt64() > 0;
372 ///////////////////////////////////////////////////////////////////////////////
375 #endif // incl_HPHP_SORT_HELPERS_H_