1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/ds/Sort.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,137 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99: 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef ds_Sort_h 1.11 +#define ds_Sort_h 1.12 + 1.13 +#include "jstypes.h" 1.14 + 1.15 +namespace js { 1.16 + 1.17 +namespace detail { 1.18 + 1.19 +template<typename T> 1.20 +MOZ_ALWAYS_INLINE void 1.21 +CopyNonEmptyArray(T *dst, const T *src, size_t nelems) 1.22 +{ 1.23 + JS_ASSERT(nelems != 0); 1.24 + const T *end = src + nelems; 1.25 + do { 1.26 + *dst++ = *src++; 1.27 + } while (src != end); 1.28 +} 1.29 + 1.30 +/* Helper function for MergeSort. */ 1.31 +template<typename T, typename Comparator> 1.32 +MOZ_ALWAYS_INLINE bool 1.33 +MergeArrayRuns(T *dst, const T *src, size_t run1, size_t run2, Comparator c) 1.34 +{ 1.35 + JS_ASSERT(run1 >= 1); 1.36 + JS_ASSERT(run2 >= 1); 1.37 + 1.38 + /* Copy runs already in sorted order. */ 1.39 + const T *b = src + run1; 1.40 + bool lessOrEqual; 1.41 + if (!c(b[-1], b[0], &lessOrEqual)) 1.42 + return false; 1.43 + 1.44 + if (!lessOrEqual) { 1.45 + /* Runs are not already sorted, merge them. */ 1.46 + for (const T *a = src;;) { 1.47 + if (!c(*a, *b, &lessOrEqual)) 1.48 + return false; 1.49 + if (lessOrEqual) { 1.50 + *dst++ = *a++; 1.51 + if (!--run1) { 1.52 + src = b; 1.53 + break; 1.54 + } 1.55 + } else { 1.56 + *dst++ = *b++; 1.57 + if (!--run2) { 1.58 + src = a; 1.59 + break; 1.60 + } 1.61 + } 1.62 + } 1.63 + } 1.64 + CopyNonEmptyArray(dst, src, run1 + run2); 1.65 + return true; 1.66 +} 1.67 + 1.68 +} /* namespace detail */ 1.69 + 1.70 +/* 1.71 + * Sort the array using the merge sort algorithm. The scratch should point to 1.72 + * a temporary storage that can hold nelems elements. 1.73 + * 1.74 + * The comparator must provide the () operator with the following signature: 1.75 + * 1.76 + * bool operator()(const T& a, const T& a, bool *lessOrEqualp); 1.77 + * 1.78 + * It should return true on success and set *lessOrEqualp to the result of 1.79 + * a <= b operation. If it returns false, the sort terminates immediately with 1.80 + * the false result. In this case the content of the array and scratch is 1.81 + * arbitrary. 1.82 + */ 1.83 +template<typename T, typename Comparator> 1.84 +bool 1.85 +MergeSort(T *array, size_t nelems, T *scratch, Comparator c) 1.86 +{ 1.87 + const size_t INS_SORT_LIMIT = 3; 1.88 + 1.89 + if (nelems <= 1) 1.90 + return true; 1.91 + 1.92 + /* 1.93 + * Apply insertion sort to small chunks to reduce the number of merge 1.94 + * passes needed. 1.95 + */ 1.96 + for (size_t lo = 0; lo < nelems; lo += INS_SORT_LIMIT) { 1.97 + size_t hi = lo + INS_SORT_LIMIT; 1.98 + if (hi >= nelems) 1.99 + hi = nelems; 1.100 + for (size_t i = lo + 1; i != hi; i++) { 1.101 + for (size_t j = i; ;) { 1.102 + bool lessOrEqual; 1.103 + if (!c(array[j - 1], array[j], &lessOrEqual)) 1.104 + return false; 1.105 + if (lessOrEqual) 1.106 + break; 1.107 + T tmp = array[j - 1]; 1.108 + array[j - 1] = array[j]; 1.109 + array[j] = tmp; 1.110 + if (--j == lo) 1.111 + break; 1.112 + } 1.113 + } 1.114 + } 1.115 + 1.116 + T *vec1 = array; 1.117 + T *vec2 = scratch; 1.118 + for (size_t run = INS_SORT_LIMIT; run < nelems; run *= 2) { 1.119 + for (size_t lo = 0; lo < nelems; lo += 2 * run) { 1.120 + size_t hi = lo + run; 1.121 + if (hi >= nelems) { 1.122 + detail::CopyNonEmptyArray(vec2 + lo, vec1 + lo, nelems - lo); 1.123 + break; 1.124 + } 1.125 + size_t run2 = (run <= nelems - hi) ? run : nelems - hi; 1.126 + if (!detail::MergeArrayRuns(vec2 + lo, vec1 + lo, run, run2, c)) 1.127 + return false; 1.128 + } 1.129 + T *swap = vec1; 1.130 + vec1 = vec2; 1.131 + vec2 = swap; 1.132 + } 1.133 + if (vec1 == scratch) 1.134 + detail::CopyNonEmptyArray(array, scratch, nelems); 1.135 + return true; 1.136 +} 1.137 + 1.138 +} /* namespace js */ 1.139 + 1.140 +#endif /* ds_Sort_h */