js/src/ds/Sort.h

changeset 0
6474c204b198
     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 */

mercurial