michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef ds_Sort_h michael@0: #define ds_Sort_h michael@0: michael@0: #include "jstypes.h" michael@0: michael@0: namespace js { michael@0: michael@0: namespace detail { michael@0: michael@0: template michael@0: MOZ_ALWAYS_INLINE void michael@0: CopyNonEmptyArray(T *dst, const T *src, size_t nelems) michael@0: { michael@0: JS_ASSERT(nelems != 0); michael@0: const T *end = src + nelems; michael@0: do { michael@0: *dst++ = *src++; michael@0: } while (src != end); michael@0: } michael@0: michael@0: /* Helper function for MergeSort. */ michael@0: template michael@0: MOZ_ALWAYS_INLINE bool michael@0: MergeArrayRuns(T *dst, const T *src, size_t run1, size_t run2, Comparator c) michael@0: { michael@0: JS_ASSERT(run1 >= 1); michael@0: JS_ASSERT(run2 >= 1); michael@0: michael@0: /* Copy runs already in sorted order. */ michael@0: const T *b = src + run1; michael@0: bool lessOrEqual; michael@0: if (!c(b[-1], b[0], &lessOrEqual)) michael@0: return false; michael@0: michael@0: if (!lessOrEqual) { michael@0: /* Runs are not already sorted, merge them. */ michael@0: for (const T *a = src;;) { michael@0: if (!c(*a, *b, &lessOrEqual)) michael@0: return false; michael@0: if (lessOrEqual) { michael@0: *dst++ = *a++; michael@0: if (!--run1) { michael@0: src = b; michael@0: break; michael@0: } michael@0: } else { michael@0: *dst++ = *b++; michael@0: if (!--run2) { michael@0: src = a; michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: CopyNonEmptyArray(dst, src, run1 + run2); michael@0: return true; michael@0: } michael@0: michael@0: } /* namespace detail */ michael@0: michael@0: /* michael@0: * Sort the array using the merge sort algorithm. The scratch should point to michael@0: * a temporary storage that can hold nelems elements. michael@0: * michael@0: * The comparator must provide the () operator with the following signature: michael@0: * michael@0: * bool operator()(const T& a, const T& a, bool *lessOrEqualp); michael@0: * michael@0: * It should return true on success and set *lessOrEqualp to the result of michael@0: * a <= b operation. If it returns false, the sort terminates immediately with michael@0: * the false result. In this case the content of the array and scratch is michael@0: * arbitrary. michael@0: */ michael@0: template michael@0: bool michael@0: MergeSort(T *array, size_t nelems, T *scratch, Comparator c) michael@0: { michael@0: const size_t INS_SORT_LIMIT = 3; michael@0: michael@0: if (nelems <= 1) michael@0: return true; michael@0: michael@0: /* michael@0: * Apply insertion sort to small chunks to reduce the number of merge michael@0: * passes needed. michael@0: */ michael@0: for (size_t lo = 0; lo < nelems; lo += INS_SORT_LIMIT) { michael@0: size_t hi = lo + INS_SORT_LIMIT; michael@0: if (hi >= nelems) michael@0: hi = nelems; michael@0: for (size_t i = lo + 1; i != hi; i++) { michael@0: for (size_t j = i; ;) { michael@0: bool lessOrEqual; michael@0: if (!c(array[j - 1], array[j], &lessOrEqual)) michael@0: return false; michael@0: if (lessOrEqual) michael@0: break; michael@0: T tmp = array[j - 1]; michael@0: array[j - 1] = array[j]; michael@0: array[j] = tmp; michael@0: if (--j == lo) michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: T *vec1 = array; michael@0: T *vec2 = scratch; michael@0: for (size_t run = INS_SORT_LIMIT; run < nelems; run *= 2) { michael@0: for (size_t lo = 0; lo < nelems; lo += 2 * run) { michael@0: size_t hi = lo + run; michael@0: if (hi >= nelems) { michael@0: detail::CopyNonEmptyArray(vec2 + lo, vec1 + lo, nelems - lo); michael@0: break; michael@0: } michael@0: size_t run2 = (run <= nelems - hi) ? run : nelems - hi; michael@0: if (!detail::MergeArrayRuns(vec2 + lo, vec1 + lo, run, run2, c)) michael@0: return false; michael@0: } michael@0: T *swap = vec1; michael@0: vec1 = vec2; michael@0: vec2 = swap; michael@0: } michael@0: if (vec1 == scratch) michael@0: detail::CopyNonEmptyArray(array, scratch, nelems); michael@0: return true; michael@0: } michael@0: michael@0: } /* namespace js */ michael@0: michael@0: #endif /* ds_Sort_h */