PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/hphp/runtime/base/sort-helpers.h

https://gitlab.com/Blueprint-Marketing/hhvm
C Header | 359 lines | 313 code | 18 blank | 28 comment | 87 complexity | 5c0a18531ede94cb010fe29a6c1b135c MD5 | raw file
  1. /*
  2. +----------------------------------------------------------------------+
  3. | HipHop for PHP |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 2010-2014 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. +----------------------------------------------------------------------+
  15. */
  16. #ifndef incl_HPHP_SORT_HELPERS_H_
  17. #define incl_HPHP_SORT_HELPERS_H_
  18. #include "hphp/runtime/base/builtin-functions.h"
  19. #include "hphp/runtime/base/comparisons.h"
  20. #include "hphp/runtime/base/execution-context.h"
  21. #include "hphp/runtime/base/type-string.h"
  22. #include "hphp/runtime/base/type-variant.h"
  23. #include "hphp/runtime/base/zend-functions.h"
  24. #include "hphp/runtime/base/zend-string.h"
  25. #include "hphp/runtime/base/sort-flags.h"
  26. #include "hphp/util/safesort.h"
  27. namespace HPHP {
  28. ///////////////////////////////////////////////////////////////////////////////
  29. // For sorting PackedArray and Vector
  30. struct TVAccessor {
  31. typedef const TypedValue& ElmT;
  32. bool isInt(ElmT elm) const { return elm.m_type == KindOfInt64; }
  33. bool isStr(ElmT elm) const { return IS_STRING_TYPE(elm.m_type); }
  34. int64_t getInt(ElmT elm) const { return elm.m_data.num; }
  35. StringData* getStr(ElmT elm) const { return elm.m_data.pstr; }
  36. Variant getValue(ElmT elm) const { return tvAsCVarRef(&elm); }
  37. };
  38. template<typename E> struct AssocKeyAccessor {
  39. typedef const E& ElmT;
  40. bool isInt(ElmT elm) const { return elm.hasIntKey(); }
  41. bool isStr(ElmT elm) const { return elm.hasStrKey(); }
  42. int64_t getInt(ElmT elm) const { return elm.ikey; }
  43. StringData* getStr(ElmT elm) const { return elm.skey; }
  44. Variant getValue(ElmT elm) const {
  45. if (isInt(elm)) {
  46. return getInt(elm);
  47. }
  48. assert(isStr(elm));
  49. return getStr(elm);
  50. }
  51. };
  52. template<typename E> struct AssocValAccessor {
  53. typedef const E& ElmT;
  54. bool isInt(ElmT elm) const { return elm.data.m_type == KindOfInt64; }
  55. bool isStr(ElmT elm) const { return IS_STRING_TYPE(elm.data.m_type); }
  56. int64_t getInt(ElmT elm) const { return elm.data.m_data.num; }
  57. StringData* getStr(ElmT elm) const { return elm.data.m_data.pstr; }
  58. Variant getValue(ElmT elm) const { return tvAsCVarRef(&elm.data); }
  59. };
  60. /**
  61. * These comparators below are used together with safesort(), and safesort
  62. * requires that comparators return true iff left is GREATER than right.
  63. */
  64. template <typename AccessorT, int sort_flags, bool ascending>
  65. struct IntElmCompare {
  66. typedef typename AccessorT::ElmT ElmT;
  67. AccessorT acc;
  68. bool operator()(ElmT left, ElmT right) const {
  69. int64_t iLeft = acc.getInt(left);
  70. int64_t iRight = acc.getInt(right);
  71. if (sort_flags == SORT_REGULAR || sort_flags == SORT_NUMERIC) {
  72. return ascending ? (iLeft > iRight) : (iLeft < iRight);
  73. }
  74. char bufLeft[21];
  75. char bufRight[21];
  76. const char* sLeft;
  77. const char* sRight;
  78. int lenLeft;
  79. int lenRight;
  80. // Take advantage of precomputed StringDatas if they are available
  81. const StringData* sdLeft = String::GetIntegerStringData(iLeft);
  82. if (sdLeft) {
  83. sLeft = sdLeft->data();
  84. lenLeft = sdLeft->size();
  85. } else {
  86. bufLeft[20] = '\0';
  87. auto sl = conv_10(iLeft, &bufLeft[20]);
  88. sLeft = sl.ptr;
  89. lenLeft = sl.len;
  90. }
  91. const StringData* sdRight = String::GetIntegerStringData(iRight);
  92. if (sdRight) {
  93. sRight = sdRight->data();
  94. lenRight = sdRight->size();
  95. } else {
  96. bufRight[20] = '\0';
  97. auto sl = conv_10(iRight, &bufRight[20]);
  98. sRight = sl.ptr;
  99. lenRight = sl.len;
  100. }
  101. if (sort_flags == SORT_STRING) {
  102. return ascending ?
  103. (string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  104. (string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0);
  105. }
  106. if (sort_flags == SORT_STRING_CASE) {
  107. return ascending ?
  108. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  109. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) < 0);
  110. }
  111. if (sort_flags == SORT_LOCALE_STRING) {
  112. return ascending ? (strcoll(sLeft, sRight) > 0) :
  113. (strcoll(sLeft, sRight) < 0);
  114. }
  115. if (sort_flags == SORT_NATURAL) {
  116. return ascending ?
  117. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0) :
  118. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0);
  119. }
  120. if (sort_flags == SORT_NATURAL_CASE) {
  121. return ascending ?
  122. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0) :
  123. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0);
  124. }
  125. assert(false);
  126. return true;
  127. }
  128. };
  129. template <typename AccessorT, int sort_flags, bool ascending>
  130. struct StrElmCompare {
  131. typedef typename AccessorT::ElmT ElmT;
  132. AccessorT acc;
  133. bool operator()(ElmT left, ElmT right) const {
  134. StringData* sdLeft = acc.getStr(left);
  135. StringData* sdRight = acc.getStr(right);
  136. if (sort_flags == SORT_REGULAR) {
  137. return ascending ? (sdLeft->compare(sdRight) > 0) :
  138. (sdLeft->compare(sdRight) < 0);
  139. }
  140. if (sort_flags == SORT_NUMERIC) {
  141. double dLeft = sdLeft->toDouble();
  142. double dRight = sdRight->toDouble();
  143. return ascending ? (dLeft > dRight) : (dLeft < dRight);
  144. }
  145. const char* sLeft = sdLeft->data();
  146. int lenLeft = sdLeft->size();
  147. const char* sRight = sdRight->data();
  148. int lenRight = sdRight->size();
  149. if (sort_flags == SORT_STRING) {
  150. return ascending ?
  151. (string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  152. (string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0);
  153. }
  154. if (sort_flags == SORT_STRING_CASE) {
  155. return ascending ?
  156. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  157. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) < 0);
  158. }
  159. if (sort_flags == SORT_LOCALE_STRING) {
  160. return ascending ? (strcoll(sLeft, sRight) > 0) :
  161. (strcoll(sLeft, sRight) < 0);
  162. }
  163. if (sort_flags == SORT_NATURAL) {
  164. return ascending ?
  165. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0) :
  166. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0);
  167. }
  168. if (sort_flags == SORT_NATURAL_CASE) {
  169. return ascending ?
  170. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0) :
  171. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0);
  172. }
  173. assert(false);
  174. return true;
  175. }
  176. };
  177. template <typename AccessorT, int sort_flags, bool ascending>
  178. struct ElmCompare {
  179. typedef typename AccessorT::ElmT ElmT;
  180. AccessorT acc;
  181. bool operator()(ElmT left, ElmT right) const {
  182. // Fast paths
  183. if (sort_flags == SORT_REGULAR) {
  184. if (acc.isStr(left)) {
  185. if (LIKELY(acc.isStr(right))) {
  186. StringData* sLeft = acc.getStr(left);
  187. StringData* sRight = acc.getStr(right);
  188. return ascending ? (sLeft->compare(sRight) > 0) :
  189. (sLeft->compare(sRight) < 0);
  190. }
  191. } else if (acc.isInt(left)) {
  192. if (LIKELY(acc.isInt(right))) {
  193. int64_t iLeft = acc.getInt(left);
  194. int64_t iRight = acc.getInt(right);
  195. return ascending ? (iLeft > iRight) : (iLeft < iRight);
  196. }
  197. }
  198. }
  199. if (sort_flags == SORT_NUMERIC) {
  200. if (acc.isInt(left)) {
  201. if (LIKELY(acc.isInt(right))) {
  202. int64_t iLeft = acc.getInt(left);
  203. int64_t iRight = acc.getInt(right);
  204. return ascending ? (iLeft > iRight) : (iLeft < iRight);
  205. }
  206. }
  207. }
  208. if (sort_flags == SORT_STRING || sort_flags == SORT_LOCALE_STRING ||
  209. sort_flags == SORT_NATURAL || sort_flags == SORT_NATURAL_CASE) {
  210. if (acc.isStr(left)) {
  211. if (LIKELY(acc.isStr(right))) {
  212. StringData* sdLeft = acc.getStr(left);
  213. StringData* sdRight = acc.getStr(right);
  214. const char* sLeft = sdLeft->data();
  215. int lenLeft = sdLeft->size();
  216. const char* sRight = sdRight->data();
  217. int lenRight = sdRight->size();
  218. if (sort_flags == SORT_STRING) {
  219. return ascending ?
  220. (string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  221. (string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0);
  222. }
  223. if (sort_flags == SORT_STRING_CASE) {
  224. return ascending ?
  225. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  226. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) < 0);
  227. }
  228. if (sort_flags == SORT_LOCALE_STRING) {
  229. return ascending ? (strcoll(sLeft, sRight) > 0) :
  230. (strcoll(sLeft, sRight) < 0);
  231. }
  232. if (sort_flags == SORT_NATURAL) {
  233. return ascending ?
  234. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0) :
  235. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0);
  236. }
  237. if (sort_flags == SORT_NATURAL_CASE) {
  238. return ascending ?
  239. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0) :
  240. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0);
  241. }
  242. }
  243. }
  244. }
  245. // Slow paths
  246. Variant vLeft = acc.getValue(left);
  247. Variant vRight = acc.getValue(right);
  248. if (sort_flags == SORT_REGULAR) {
  249. return ascending ? HPHP::more(vLeft, vRight) : HPHP::less(vLeft, vRight);
  250. }
  251. if (sort_flags == SORT_NUMERIC) {
  252. double dLeft = vLeft.toDouble();
  253. double dRight = vRight.toDouble();
  254. return ascending ? dLeft > dRight : dLeft < dRight;
  255. }
  256. String strLeft = vLeft.toString();
  257. String strRight = vRight.toString();
  258. const char* sLeft = strLeft.data();
  259. int lenLeft = strLeft.size();
  260. const char* sRight = strRight.data();
  261. int lenRight = strRight.size();
  262. if (sort_flags == SORT_STRING) {
  263. return ascending ?
  264. (string_strcmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  265. (string_strcmp(sLeft, lenLeft, sRight, lenRight) < 0);
  266. }
  267. if (sort_flags == SORT_STRING_CASE) {
  268. return ascending ?
  269. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) > 0) :
  270. (bstrcasecmp(sLeft, lenLeft, sRight, lenRight) < 0);
  271. }
  272. if (sort_flags == SORT_LOCALE_STRING) {
  273. return ascending ?
  274. (strcoll(sLeft, sRight) > 0) :
  275. (strcoll(sLeft, sRight) < 0);
  276. }
  277. if (sort_flags == SORT_NATURAL) {
  278. return ascending ?
  279. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) > 0) :
  280. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 0) < 0);
  281. }
  282. if (sort_flags == SORT_NATURAL_CASE) {
  283. return ascending ?
  284. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) > 0) :
  285. (string_natural_cmp(sLeft, lenLeft, sRight, lenRight, 1) < 0);
  286. }
  287. assert(false);
  288. return true;
  289. }
  290. };
  291. template <typename AccessorT>
  292. struct ElmUCompare {
  293. typedef typename AccessorT::ElmT ElmT;
  294. AccessorT acc;
  295. const CallCtx* ctx;
  296. // only warn with HH syntax enabled
  297. ElmUCompare() : warned(!RuntimeOption::EnableHipHopSyntax) {}
  298. bool operator()(ElmT left, ElmT right) const {
  299. Variant ret;
  300. {
  301. TypedValue args[2] = {
  302. *acc.getValue(left).asCell(),
  303. *acc.getValue(right).asCell()
  304. };
  305. g_context->invokeFuncFew(ret.asTypedValue(), *ctx, 2, args);
  306. }
  307. if (LIKELY(ret.isInteger())) {
  308. return ret.toInt64() > 0;
  309. }
  310. if (ret.isDouble()) {
  311. return ret.toDouble() > 0.0;
  312. }
  313. if (ret.isString()) {
  314. int64_t lval; double dval;
  315. auto dt = ret.getStringData()->isNumericWithVal(lval, dval, 0);
  316. if (IS_INT_TYPE(dt)) {
  317. return lval > 0;
  318. } else if (IS_DOUBLE_TYPE(dt)) {
  319. return dval > 0;
  320. }
  321. }
  322. if (ret.isBoolean()) {
  323. if (!warned) {
  324. raise_warning("Sort comparators should not return a boolean because "
  325. "it may result in a different sort order than expected. "
  326. "The comparator should return an integer (negative if "
  327. "left is less than right, positive if left is greater "
  328. "than right, zero if they are equal).");
  329. warned = true;
  330. }
  331. }
  332. // If the comparator returned something other than int, double, or a
  333. // numeric string, we fall back to casting it to an integer.
  334. return ret.toInt64() > 0;
  335. }
  336. private:
  337. mutable bool warned;
  338. };
  339. ///////////////////////////////////////////////////////////////////////////////
  340. }
  341. #endif // incl_HPHP_SORT_HELPERS_H_