88ec1476e2
copy paste error crept in a few weeks ago, breaking the case of a user-defined function that returns boolean.
292 linhas
11 KiB
C++
292 linhas
11 KiB
C++
/*
|
|
+----------------------------------------------------------------------+
|
|
| HipHop for PHP |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| http://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifndef incl_HPHP_SORT_HELPERS_H_
|
|
#define incl_HPHP_SORT_HELPERS_H_
|
|
|
|
#include "hphp/runtime/base/complex_types.h"
|
|
#include "hphp/runtime/base/builtin_functions.h"
|
|
#include "hphp/runtime/base/comparisons.h"
|
|
#include "hphp/runtime/base/zend/zend_functions.h"
|
|
#include "hphp/runtime/base/sort_flags.h"
|
|
#include "hphp/util/safesort.h"
|
|
|
|
namespace HPHP {
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename AccessorT, int sort_flags, bool ascending>
|
|
struct IntElmCompare {
|
|
typedef typename AccessorT::ElmT ElmT;
|
|
AccessorT acc;
|
|
bool operator()(ElmT left, ElmT right) const {
|
|
int64_t iLeft = acc.getInt(left);
|
|
int64_t iRight = acc.getInt(right);
|
|
if (sort_flags == SORT_REGULAR || sort_flags == SORT_NUMERIC) {
|
|
return ascending ? (iLeft < iRight) : (iLeft > iRight);
|
|
}
|
|
int isNegative;
|
|
char bufLeft[21];
|
|
char bufRight[21];
|
|
const char* sLeft;
|
|
const char* sRight;
|
|
int lenLeft;
|
|
int lenRight;
|
|
// Take advantage of precomputed StringDatas if they are available
|
|
const StringData* sdLeft = String::GetIntegerStringData(iLeft);
|
|
if (sdLeft) {
|
|
sLeft = sdLeft->data();
|
|
lenLeft = sdLeft->size();
|
|
} else {
|
|
bufLeft[20] = '\0';
|
|
sLeft = conv_10(iLeft, &isNegative, &bufLeft[20], &lenLeft);
|
|
}
|
|
const StringData* sdRight = String::GetIntegerStringData(iRight);
|
|
if (sdRight) {
|
|
sRight = sdRight->data();
|
|
lenRight = sdRight->size();
|
|
} else {
|
|
bufRight[20] = '\0';
|
|
sRight = conv_10(iRight, &isNegative, &bufRight[20], &lenRight);
|
|
}
|
|
if (sort_flags == SORT_STRING) {
|
|
return ascending ?
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_LOCALE_STRING) {
|
|
return ascending ? (strcoll(sLeft, sRight) < 0) :
|
|
(strcoll(sLeft, sRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL_CASE) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
|
|
}
|
|
assert(false);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename AccessorT, int sort_flags, bool ascending>
|
|
struct StrElmCompare {
|
|
typedef typename AccessorT::ElmT ElmT;
|
|
AccessorT acc;
|
|
bool operator()(ElmT left, ElmT right) const {
|
|
StringData* sdLeft = acc.getStr(left);
|
|
StringData* sdRight = acc.getStr(right);
|
|
if (sort_flags == SORT_REGULAR) {
|
|
return ascending ? (sdLeft->compare(sdRight) < 0) :
|
|
(sdLeft->compare(sdRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_NUMERIC) {
|
|
double dLeft = sdLeft->toDouble();
|
|
double dRight = sdRight->toDouble();
|
|
return ascending ? (dLeft < dRight) : (dLeft > dRight);
|
|
}
|
|
const char* sLeft = sdLeft->data();
|
|
int lenLeft = sdLeft->size();
|
|
const char* sRight = sdRight->data();
|
|
int lenRight = sdRight->size();
|
|
if (sort_flags == SORT_STRING) {
|
|
return ascending ?
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_LOCALE_STRING) {
|
|
return ascending ? (strcoll(sLeft, sRight) < 0) :
|
|
(strcoll(sLeft, sRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL_CASE) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
|
|
}
|
|
assert(false);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename AccessorT, int sort_flags, bool ascending>
|
|
struct ElmCompare {
|
|
typedef typename AccessorT::ElmT ElmT;
|
|
AccessorT acc;
|
|
bool operator()(ElmT left, ElmT right) const {
|
|
// Fast paths
|
|
if (sort_flags == SORT_REGULAR) {
|
|
if (acc.isStr(left)) {
|
|
if (LIKELY(acc.isStr(right))) {
|
|
StringData* sLeft = acc.getStr(left);
|
|
StringData* sRight = acc.getStr(right);
|
|
return ascending ? (sLeft->compare(sRight) < 0) :
|
|
(sLeft->compare(sRight) > 0);
|
|
}
|
|
} else if (acc.isInt(left)) {
|
|
if (LIKELY(acc.isInt(right))) {
|
|
int64_t iLeft = acc.getInt(left);
|
|
int64_t iRight = acc.getInt(right);
|
|
return ascending ? (iLeft < iRight) : (iLeft > iRight);
|
|
}
|
|
}
|
|
}
|
|
if (sort_flags == SORT_NUMERIC) {
|
|
if (acc.isInt(left)) {
|
|
if (LIKELY(acc.isInt(right))) {
|
|
int64_t iLeft = acc.getInt(left);
|
|
int64_t iRight = acc.getInt(right);
|
|
return ascending ? (iLeft < iRight) : (iLeft > iRight);
|
|
}
|
|
}
|
|
}
|
|
if (sort_flags == SORT_STRING || sort_flags == SORT_LOCALE_STRING ||
|
|
sort_flags == SORT_NATURAL || sort_flags == SORT_NATURAL_CASE) {
|
|
if (acc.isStr(left)) {
|
|
if (LIKELY(acc.isStr(right))) {
|
|
StringData* sdLeft = acc.getStr(left);
|
|
StringData* sdRight = acc.getStr(right);
|
|
const char* sLeft = sdLeft->data();
|
|
int lenLeft = sdLeft->size();
|
|
const char* sRight = sdRight->data();
|
|
int lenRight = sdRight->size();
|
|
if (sort_flags == SORT_STRING) {
|
|
return ascending ?
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_LOCALE_STRING) {
|
|
return ascending ? (strcoll(sLeft, sRight) < 0) :
|
|
(strcoll(sLeft, sRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL_CASE) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Slow paths
|
|
Variant vLeft = acc.getValue(left);
|
|
Variant vRight = acc.getValue(right);
|
|
if (sort_flags == SORT_REGULAR) {
|
|
return ascending ? HPHP::less(vLeft, vRight) : HPHP::more(vLeft, vRight);
|
|
}
|
|
if (sort_flags == SORT_NUMERIC) {
|
|
double dLeft = vLeft.toDouble();
|
|
double dRight = vRight.toDouble();
|
|
return ascending ? dLeft < dRight : dLeft > dRight;
|
|
}
|
|
String strLeft = vLeft.toString();
|
|
String strRight = vRight.toString();
|
|
const char* sLeft = strLeft.data();
|
|
int lenLeft = strLeft.size();
|
|
const char* sRight = strRight.data();
|
|
int lenRight = strRight.size();
|
|
if (sort_flags == SORT_STRING) {
|
|
return ascending ?
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0) :
|
|
(string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_LOCALE_STRING) {
|
|
return ascending ?
|
|
(strcoll(sLeft, sRight) < 0) :
|
|
(strcoll(sLeft, sRight) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0);
|
|
}
|
|
if (sort_flags == SORT_NATURAL_CASE) {
|
|
return ascending ?
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0) :
|
|
(string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0);
|
|
}
|
|
assert(false);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename AccessorT>
|
|
struct ElmUCompare {
|
|
typedef typename AccessorT::ElmT ElmT;
|
|
AccessorT acc;
|
|
const CallCtx* ctx;
|
|
bool operator()(ElmT left, ElmT right) const {
|
|
Variant ret;
|
|
TypedValue args[2];
|
|
tvDup(acc.getValue(left).asTypedValue(), args+0);
|
|
tvDup(acc.getValue(right).asTypedValue(), args+1);
|
|
g_vmContext->invokeFuncFew(ret.asTypedValue(), *ctx,
|
|
2, args);
|
|
if (ret.isInteger()) {
|
|
return ret.toInt64() < 0;
|
|
}
|
|
if (ret.isDouble()) {
|
|
return ret.toDouble() < 0.0;
|
|
}
|
|
if (ret.isString()) {
|
|
int64_t lval; double dval;
|
|
switch (ret.getStringData()->isNumericWithVal(lval, dval, 0)) {
|
|
case KindOfInt64: return lval < 0;
|
|
case KindOfDouble: return dval < 0;
|
|
default: /* fall through */ break;
|
|
}
|
|
}
|
|
// Task #1839416: Raise a warning indicating that the comparator
|
|
// returned something other than an integer, double, or numeric
|
|
// string
|
|
if (ret.isBoolean()) {
|
|
// Match the behavior of Zend PHP for comparators that return
|
|
// boolean values
|
|
bool b = ret.toBoolean();
|
|
if (b) {
|
|
return false;
|
|
}
|
|
Variant ret2;
|
|
tvDup(acc.getValue(right).asTypedValue(), args+0);
|
|
tvDup(acc.getValue(left).asTypedValue(), args+1);
|
|
g_vmContext->invokeFuncFew(ret2.asTypedValue(), *ctx,
|
|
2, args);
|
|
if (ret2.isBoolean()) {
|
|
return ret2.toBoolean();
|
|
}
|
|
// We have a wild comparator that returns boolean and non-boolean
|
|
// values; give up and fall through to the logic below
|
|
}
|
|
return ret.toInt64() < 0;
|
|
}
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
#endif // incl_HPHP_SORT_HELPERS_H_
|