X-Git-Url: https://git.saurik.com/apple/cf.git/blobdiff_plain/b0e0750aaee7a1a2cd8e3aa918bd9fc756e9dfcf..cf7d2af96685aba74e84652f9ed1098e9253902e:/CFSortFunctions.c diff --git a/CFSortFunctions.c b/CFSortFunctions.c index 6a1ce6b..9275059 100644 --- a/CFSortFunctions.c +++ b/CFSortFunctions.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008 Apple Inc. All rights reserved. + * Copyright (c) 2009 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * @@ -21,1001 +21,321 @@ * @APPLE_LICENSE_HEADER_END@ */ /* CFSortFunctions.c - Copyright 1999-2007, Apple, Inc. All rights reserved. + Copyright (c) 1999-2009, Apple Inc. All rights reserved. Responsibility: Christopher Kane */ -/* This file contains code copied from the Libc project's files - qsort.c and merge.c, and modified to suit the - needs of CF, which needs the comparison callback to have an - additional parameter. The code is collected into this one - file so that the individual BSD sort routines can remain - private and static. -*/ - #include -#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX || DEPLOYMENT_TARGET_FREEBSD -#include -#else -#define EINVAL 22 -#endif #include "CFInternal.h" - - -/* stdlib.subproj/qsort.c ============================================== */ - -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ -#include -__FBSDID("$FreeBSD: src/lib/libc/stdlib/qsort.c,v 1.12 2002/09/10 02:04:49 wollman Exp $"); +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED +#include +#include #endif -#include -#ifdef I_AM_QSORT_R -typedef int cmp_t(void *, const void *, const void *); -#else -typedef CFComparisonResult cmp_t(const void *, const void *, void *); -#endif -static inline char *med3(char *, char *, char *, cmp_t *, void *); -static inline void swapfunc(char *, char *, long, long); +enum { + kCFSortConcurrent = (1 << 0), + kCFSortStable = (1 << 4), +}; + +typedef CFIndex VALUE_TYPE; +typedef CFIndex INDEX_TYPE; +typedef CFComparisonResult CMP_RESULT_TYPE; +typedef CMP_RESULT_TYPE (^COMPARATOR_BLOCK)(VALUE_TYPE, VALUE_TYPE); -#if !defined(min) -#define min(a, b) (a) < (b) ? a : b -#endif /* - * Qsort routine from Bentley & McIlroy's "Engineering a Sort Function". - */ -#define swapcode(TYPE, parmi, parmj, n) { \ - long i = (n) / sizeof (TYPE); \ - TYPE *pi = (TYPE *) (parmi); \ - TYPE *pj = (TYPE *) (parmj); \ - do { \ - TYPE t = *pi; \ - *pi++ = *pj; \ - *pj++ = t; \ - } while (--i > 0); \ -} +Number of elements in a list and expected number of compares, +when the initial short-circuiting compare is not done. +1 0 +2 1 +3 2.667 +4 4.667 +5 7.167 +6 9.833 +7 12.733 +8 15.733 +9 19.167 +10 22.667 +11 26.2857 +12 29.9524 +*/ -#define SWAPINIT(a, es) swaptype = ((char *)a - (char *)0) % sizeof(long) || \ - es % sizeof(long) ? 2 : es == sizeof(long)? 0 : 1; +static void __CFSimpleMerge(VALUE_TYPE listp[], INDEX_TYPE cnt1, INDEX_TYPE cnt2, VALUE_TYPE tmp[], COMPARATOR_BLOCK cmp) { + if (cnt1 <= 0 || cnt2 <= 0) return; + // if the last element of listp1 <= the first of listp2, lists are already ordered + if (16 < cnt1 + cnt2 && cmp(listp[cnt1 - 1], listp[cnt1]) <= 0) return; + + INDEX_TYPE idx = 0, idx1 = 0, idx2 = cnt1; + for (;;) { + if (cnt1 <= idx1) { + while (idx--) { + listp[idx] = tmp[idx]; + } + return; + } + if (cnt1 + cnt2 <= idx2) { + for (INDEX_TYPE t = cnt1 + cnt2 - 1; idx <= t; t--) { + listp[t] = listp[t - cnt2]; + } + while (idx--) { + listp[idx] = tmp[idx]; + } + return; + } + VALUE_TYPE v1 = listp[idx1], v2 = listp[idx2]; + if (cmp(v1, v2) <= 0) { + tmp[idx] = v1; + idx1++; + } else { + tmp[idx] = v2; + idx2++; + } + idx++; + } +} -static inline void -swapfunc(char *a, char *b, long n, long swaptype) -{ - if(swaptype <= 1) - swapcode(long, a, b, n) - else - swapcode(char, a, b, n) +static void __CFSimpleMergeSort(VALUE_TYPE listp[], INDEX_TYPE cnt, VALUE_TYPE tmp[], COMPARATOR_BLOCK cmp) { + if (cnt < 2) { + /* do nothing */ + } else if (2 == cnt) { + VALUE_TYPE v0 = listp[0], v1 = listp[1]; + if (0 < cmp(v0, v1)) { + listp[0] = v1; + listp[1] = v0; + } + } else if (3 == cnt) { + VALUE_TYPE v0 = listp[0], v1 = listp[1], v2 = listp[2], vt; + if (0 < cmp(v0, v1)) { + vt = v0; + v0 = v1; + v1 = vt; + } + if (0 < cmp(v1, v2)) { + vt = v1; + v1 = v2; + v2 = vt; + if (0 < cmp(v0, v1)) { + vt = v0; + v0 = v1; + v1 = vt; + } + } + listp[0] = v0; + listp[1] = v1; + listp[2] = v2; + } else { + INDEX_TYPE half_cnt = cnt / 2; + __CFSimpleMergeSort(listp, half_cnt, tmp, cmp); + __CFSimpleMergeSort(listp + half_cnt, cnt - half_cnt, tmp, cmp); + __CFSimpleMerge(listp, half_cnt, cnt - half_cnt, tmp, cmp); + } } -#define swap(a, b) \ - if (swaptype == 0) { \ - long t = *(long *)(a); \ - *(long *)(a) = *(long *)(b); \ - *(long *)(b) = t; \ - } else \ - swapfunc(a, b, es, swaptype) +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED +// if !right, put the cnt1 smallest values in tmp, else put the cnt2 largest values in tmp +static void __CFSortIndexesNMerge(VALUE_TYPE listp1[], INDEX_TYPE cnt1, VALUE_TYPE listp2[], INDEX_TYPE cnt2, VALUE_TYPE tmp[], size_t right, COMPARATOR_BLOCK cmp) { + // if the last element of listp1 <= the first of listp2, lists are already ordered + if (16 < cnt1 + cnt2 && cmp(listp1[cnt1 - 1], listp2[0]) <= 0) { + memmove(tmp, (right ? listp2 : listp1), (right ? cnt2 : cnt1) * sizeof(VALUE_TYPE)); + return; + } -#define vecswap(a, b, n) if ((n) > 0) swapfunc(a, b, n, swaptype) + if (right) { + VALUE_TYPE *listp1_end = listp1; + VALUE_TYPE *listp2_end = listp2; + VALUE_TYPE *tmp_end = tmp; + listp1 += cnt1 - 1; + listp2 += cnt2 - 1; + tmp += cnt2; + while (tmp_end < tmp) { + tmp--; + if (listp2 < listp2_end) { + listp1--; + *tmp = *listp1; + } else if (listp1 < listp1_end) { + listp2--; + *tmp = *listp2; + } else { + VALUE_TYPE v1 = *listp1, v2 = *listp2; + CMP_RESULT_TYPE res = cmp(v1, v2); + if (res <= 0) { + *tmp = v2; + listp2--; + } else { + *tmp = v1; + listp1--; + } + } + } + } else { + VALUE_TYPE *listp1_end = listp1 + cnt1; + VALUE_TYPE *listp2_end = listp2 + cnt2; + VALUE_TYPE *tmp_end = tmp + cnt1; + while (tmp < tmp_end) { + if (listp2_end <= listp2) { + *tmp = *listp1; + listp1++; + } else if (listp1_end <= listp1) { + *tmp = *listp2; + listp2++; + } else { + VALUE_TYPE v1 = *listp1, v2 = *listp2; + CMP_RESULT_TYPE res = cmp(v1, v2); + if (res <= 0) { + *tmp = v1; + listp1++; + } else { + *tmp = v2; + listp2++; + } + } + tmp++; + } + } +} -#ifdef I_AM_QSORT_R -#define CMP(t, x, y) (cmp((t), (x), (y))) -#else -#define CMP(t, x, y) (cmp((x), (y), (t))) -#endif +/* Merging algorithm based on + "A New Parallel Sorting Algorithm based on Odd-Even Mergesort", Ezequiel Herruzo, et al +*/ +static void __CFSortIndexesN(VALUE_TYPE listp[], INDEX_TYPE count, int32_t ncores, CMP_RESULT_TYPE (^cmp)(INDEX_TYPE, INDEX_TYPE)) { + /* Divide the array up into up to ncores, multiple-of-16-sized, chunks */ + INDEX_TYPE sz = ((((count + ncores - 1) / ncores) + 15) / 16) * 16; + INDEX_TYPE num_sect = (count + sz - 1) / sz; + INDEX_TYPE last_sect_len = count + sz - sz * num_sect; + + STACK_BUFFER_DECL(VALUE_TYPE *, stack_tmps, num_sect); + for (INDEX_TYPE idx = 0; idx < num_sect; idx++) { + stack_tmps[idx] = malloc(sz * sizeof(VALUE_TYPE)); + } + VALUE_TYPE **tmps = stack_tmps; + + dispatch_queue_t q = dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT); + dispatch_apply(num_sect, q, ^(size_t sect) { + INDEX_TYPE sect_len = (sect < num_sect - 1) ? sz : last_sect_len; + __CFSimpleMergeSort(listp + sect * sz, sect_len, tmps[sect], cmp); // naturally stable + }); + + INDEX_TYPE even_phase_cnt = ((num_sect / 2) * 2); + INDEX_TYPE odd_phase_cnt = (((num_sect - 1) / 2) * 2); + for (INDEX_TYPE idx = 0; idx < (num_sect + 1) / 2; idx++) { + dispatch_apply(even_phase_cnt, q, ^(size_t sect) { // merge even + size_t right = sect & (size_t)0x1; + VALUE_TYPE *left_base = listp + sect * sz - (right ? sz : 0); + VALUE_TYPE *right_base = listp + sect * sz + (right ? 0 : sz); + INDEX_TYPE sect2_len = (sect + 1 + (right ? 0 : 1) == num_sect) ? last_sect_len : sz; + __CFSortIndexesNMerge(left_base, sz, right_base, sect2_len, tmps[sect], right, cmp); + }); + if (num_sect & 0x1) { + memmove(tmps[num_sect - 1], listp + (num_sect - 1) * sz, last_sect_len * sizeof(VALUE_TYPE)); + } + dispatch_apply(odd_phase_cnt, q, ^(size_t sect) { // merge odd + size_t right = sect & (size_t)0x1; + VALUE_TYPE *left_base = tmps[sect + (right ? 0 : 1)]; + VALUE_TYPE *right_base = tmps[sect + (right ? 1 : 2)]; + INDEX_TYPE sect2_len = (sect + 1 + (right ? 1 : 2) == num_sect) ? last_sect_len : sz; + __CFSortIndexesNMerge(left_base, sz, right_base, sect2_len, listp + sect * sz + sz, right, cmp); + }); + memmove(listp + 0 * sz, tmps[0], sz * sizeof(VALUE_TYPE)); + if (!(num_sect & 0x1)) { + memmove(listp + (num_sect - 1) * sz, tmps[num_sect - 1], last_sect_len * sizeof(VALUE_TYPE)); + } + } -static inline char * -med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk) -{ - return CMP(thunk, a, b) < 0 ? - (CMP(thunk, b, c) < 0 ? b : (CMP(thunk, a, c) < 0 ? c : a )) - :(CMP(thunk, b, c) > 0 ? b : (CMP(thunk, a, c) < 0 ? a : c )); + for (INDEX_TYPE idx = 0; idx < num_sect; idx++) { + free(stack_tmps[idx]); + } } - -#ifdef I_AM_QSORT_R -void -qsort_r(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) -#else -static void -bsd_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) #endif -{ - char *pa, *pb, *pc, *pd, *pl, *pm, *pn; - long d, r, swaptype, swap_cnt; - -loop: SWAPINIT(a, es); - swap_cnt = 0; - if (n < 7) { - for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) - for (pl = pm; - pl > (char *)a && CMP(thunk, pl - es, pl) > 0; - pl -= es) - swap(pl, pl - es); - return; - } - pm = (char *)a + (n / 2) * es; - if (n > 7) { - pl = a; - pn = (char *)a + (n - 1) * es; - if (n > 40) { - d = (n / 8) * es; - pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk); - pm = med3(pm - d, pm, pm + d, cmp, thunk); - pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk); - } - pm = med3(pl, pm, pn, cmp, thunk); - } - swap(a, pm); - pa = pb = (char *)a + es; - - pc = pd = (char *)a + (n - 1) * es; - for (;;) { - while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) { - if (r == 0) { - swap_cnt = 1; - swap(pa, pb); - pa += es; - } - pb += es; - } - while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) { - if (r == 0) { - swap_cnt = 1; - swap(pc, pd); - pd -= es; - } - pc -= es; - } - if (pb > pc) - break; - swap(pb, pc); - swap_cnt = 1; - pb += es; - pc -= es; - } - if (swap_cnt == 0) { /* Switch to insertion sort */ - for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) - for (pl = pm; - pl > (char *)a && CMP(thunk, pl - es, pl) > 0; - pl -= es) - swap(pl, pl - es); - return; - } - pn = (char *)a + n * es; - r = min(pa - (char *)a, pb - pa); - vecswap(a, pb - r, r); - r = min(pd - pc, pn - pd - es); - vecswap(pb, pn - r, r); - if ((r = pb - pa) > es) -#ifdef I_AM_QSORT_R - qsort_r(a, r / es, es, thunk, cmp); +// returns an array of indexes (of length count) giving the indexes 0 - count-1, as sorted by the comparator block +CFIndex *CFSortIndexes(CFIndex count, CFOptionFlags opts, CFComparisonResult (^cmp)(CFIndex, CFIndex)) { + if (count < 1) return NULL; + if (INTPTR_MAX / sizeof(CFIndex) < count) return NULL; +#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED + int32_t ncores = 0; + if (opts & kCFSortConcurrent) { + int32_t mib[2] = {CTL_HW, HW_AVAILCPU}; + size_t len = sizeof(ncores); + sysctl(mib, 2, &ncores, &len, NULL, 0); + if (count < 160 || ncores < 2) { + opts = (opts & ~kCFSortConcurrent); + } else if (count < 640 && 2 < ncores) { + ncores = 2; + } else if (count < 3200 && 4 < ncores) { + ncores = 4; + } else if (count < 16000 && 8 < ncores) { + ncores = 8; + } + if (16 < ncores) { + ncores = 16; + } + } + CFIndex *idxs = malloc_zone_memalign(malloc_default_zone(), 64, count * sizeof(CFIndex)); + if (!idxs) return NULL; + if (count <= 65536) { + for (CFIndex idx = 0; idx < count; idx++) idxs[idx] = idx; + } else { + /* Specifically hard-coded to 8; the count has to be very large before more chunks and/or cores is worthwhile. */ + CFIndex sz = ((((size_t)count + 15) / 16) * 16) / 8; + dispatch_apply(8, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT), ^(size_t n) { + CFIndex idx = n * sz, lim = __CFMin(idx + sz, count); + for (; idx < lim; idx++) idxs[idx] = idx; + }); + } + if (opts & kCFSortConcurrent) { + __CFSortIndexesN(idxs, count, ncores, cmp); // naturally stable + return idxs; + } #else - bsd_qsort(a, r / es, es, cmp, thunk); + CFIndex *idxs = (CFIndex *)malloc(count * sizeof(CFIndex)); + if (!idxs) return NULL; + for (CFIndex idx = 0; idx < count; idx++) idxs[idx] = idx; #endif - if ((r = pd - pc) > es) { - /* Iterate rather than recurse to save stack space */ - a = pn - r; - n = r / es; - goto loop; - } -/* qsort(pn - r, r / es, es, cmp);*/ -} - - -/* And now for something not so completely different, a copy/paste version that uses write-barriers so as to notify GC as necessary of changes */ -#define ASSIGN __CFObjCStrongAssign -//#define ASSIGN objc_assign_strongCast - -#define swapcode_wb(TYPE, parmi, parmj, n) { \ - long i = (n) / sizeof (TYPE); \ - TYPE *pi = (TYPE *) (parmi); \ - TYPE *pj = (TYPE *) (parmj); \ - do { \ - TYPE t = *pi; \ - ASSIGN(*pj, pi++); \ - ASSIGN(t, pj++); \ - } while (--i > 0); \ -} - - -static inline void -swapfunc_wb(char *a, char *b, long n, long swaptype) -{ - if(swaptype <= 1) - swapcode_wb(void *, a, b, n) - else - swapcode(char, a, b, n) -} - -#define swap_wb(a, b) \ - if (swaptype == 0) { \ - const void *t = *(const void **)(a); \ - ASSIGN(*(void **)(b), (const void **)a); \ - ASSIGN(t, (const void **)(b)); \ - } else \ - printf("bad things happening\n"); - //swapfunc_wb(a, b, es, swaptype) - -#define vecswap_wb(a, b, n) if ((n) > 0) swapfunc_wb(a, b, n, swaptype) - -static void -bsd_qsort_wb(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) -{ - char *pa, *pb, *pc, *pd, *pl, *pm, *pn; - long d, r, swaptype, swap_cnt; - -loop: SWAPINIT(a, es); - swap_cnt = 0; - if (n < 7) { - for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) - for (pl = pm; - pl > (char *)a && CMP(thunk, pl - es, pl) > 0; - pl -= es) - swap_wb(pl, pl - es); - return; - } - pm = (char *)a + (n / 2) * es; - if (n > 7) { - pl = a; - pn = (char *)a + (n - 1) * es; - if (n > 40) { - d = (n / 8) * es; - pl = med3(pl, pl + d, pl + 2 * d, cmp, thunk); - pm = med3(pm - d, pm, pm + d, cmp, thunk); - pn = med3(pn - 2 * d, pn - d, pn, cmp, thunk); - } - pm = med3(pl, pm, pn, cmp, thunk); - } - swap_wb(a, pm); - pa = pb = (char *)a + es; - - pc = pd = (char *)a + (n - 1) * es; - for (;;) { - while (pb <= pc && (r = CMP(thunk, pb, a)) <= 0) { - if (r == 0) { - swap_cnt = 1; - swap_wb(pa, pb); - pa += es; - } - pb += es; - } - while (pb <= pc && (r = CMP(thunk, pc, a)) >= 0) { - if (r == 0) { - swap_cnt = 1; - swap_wb(pc, pd); - pd -= es; - } - pc -= es; - } - if (pb > pc) - break; - swap_wb(pb, pc); - swap_cnt = 1; - pb += es; - pc -= es; - } - if (swap_cnt == 0) { /* Switch to insertion sort */ - for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) - for (pl = pm; - pl > (char *)a && CMP(thunk, pl - es, pl) > 0; - pl -= es) - swap_wb(pl, pl - es); - return; - } - - pn = (char *)a + n * es; - r = min(pa - (char *)a, pb - pa); - vecswap_wb(a, pb - r, r); - r = min(pd - pc, pn - pd - es); - vecswap_wb(pb, pn - r, r); - if ((r = pb - pa) > es) - bsd_qsort_wb(a, r / es, es, cmp, thunk); - if ((r = pd - pc) > es) { - /* Iterate rather than recurse to save stack space */ - a = pn - r; - n = r / es; - goto loop; - } -/* qsort(pn - r, r / es, es, cmp);*/ + STACK_BUFFER_DECL(VALUE_TYPE, local, count <= 16384 ? count : 1); + VALUE_TYPE *tmp = (count <= 16384) ? local : (VALUE_TYPE *)malloc(count * sizeof(VALUE_TYPE)); + __CFSimpleMergeSort(idxs, count, tmp, cmp); // naturally stable + if (local != tmp) free(tmp); + return idxs; } /* Comparator is passed the address of the values. */ void CFQSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context) { - if (CF_USING_COLLECTABLE_MEMORY && (auto_zone_get_layout_type(__CFCollectableZone, list) & AUTO_UNSCANNED) == 0) - bsd_qsort_wb(list, count, elementSize, comparator, context); - else - bsd_qsort(list, count, elementSize, comparator, context); -} - -#undef thunk -#undef CMP -#undef vecswap -//#undef swap -#undef SWAPINIT -#undef swapcode -#undef min - -/* stdlib.subproj/mergesort.c ========================================== */ - -/*- - * Copyright (c) 1992, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Peter McIlroy. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#if 0 -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)merge.c 8.2 (Berkeley) 2/14/94"; -#endif /* LIBC_SCCS and not lint */ -#include -__FBSDID("$FreeBSD: src/lib/libc/stdlib/merge.c,v 1.6 2002/03/21 22:48:42 obrien Exp $"); -#endif - -/* - * Hybrid exponential search/linear search merge sort with hybrid - * natural/pairwise first pass. Requires about .3% more comparisons - * for random data than LSMS with pairwise first pass alone. - * It works for objects as small as two bytes. - */ - -#define NATURAL -#define THRESHOLD 16 /* Best choice for natural merge cut-off. */ - -/* #define NATURAL to get hybrid natural merge. - * (The default is pairwise merging.) - */ - -#include - -#include -#include -#include - -static void setup(u_char *, u_char *, size_t, size_t, CFComparisonResult (*)(), void *); -static void insertionsort(u_char *, size_t, size_t, CFComparisonResult (*)(), void *); - -#define ISIZE sizeof(long) -#define PSIZE sizeof(u_char *) -#define ICOPY_LIST(src, dst, last) \ - do \ - *(long*)dst = *(long*)src, src += ISIZE, dst += ISIZE; \ - while(src < last) -#define ICOPY_ELT(src, dst, i) \ - do \ - *(long*) dst = *(long*) src, src += ISIZE, dst += ISIZE; \ - while (i -= ISIZE) - -#define CCOPY_LIST(src, dst, last) \ - do \ - *dst++ = *src++; \ - while (src < last) -#define CCOPY_ELT(src, dst, i) \ - do \ - *dst++ = *src++; \ - while (i -= 1) - -/* - * Find the next possible pointer head. (Trickery for forcing an array - * to do double duty as a linked list when objects do not align with word - * boundaries. - */ -/* Assumption: PSIZE is a power of 2. */ -#define EVAL(p) (u_char **) \ - ((u_char *)0 + \ - (((u_char *)p + PSIZE - 1 - (u_char *) 0) & ~(PSIZE - 1))) - -/* - * Arguments are as for qsort. - */ -static int -bsd_mergesort(void *base, size_t nmemb, size_t size, CFComparisonResult (*cmp)(const void *, const void *, void *), void *context) -{ - long i, sense; - long big, iflag; - u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; - u_char *list2, *list1, *p2, *p, *last, **p1; - - if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ - errno = EINVAL; - return (-1); - } - - if (nmemb == 0) - return (0); - - /* - * XXX - * Stupid subtraction for the Cray. - */ - iflag = 0; - if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) - iflag = 1; - - if ((list2 = malloc(nmemb * size + PSIZE)) == NULL) - return (-1); - - list1 = base; - setup(list1, list2, nmemb, size, cmp, context); - last = list2 + nmemb * size; - i = big = 0; - while (*EVAL(list2) != last) { - l2 = list1; - p1 = EVAL(list1); - for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) { - p2 = *EVAL(p2); - f1 = l2; - f2 = l1 = list1 + (p2 - list2); - if (p2 != last) - p2 = *EVAL(p2); - l2 = list1 + (p2 - list2); - while (f1 < l1 && f2 < l2) { - if ((*cmp)(f1, f2, context) <= 0) { - q = f2; - b = f1, t = l1; - sense = -1; - } else { - q = f1; - b = f2, t = l2; - sense = 0; - } - if (!big) { /* here i = 0 */ - while ((b += size) < t && cmp(q, b, context) >sense) - if (++i == 6) { - big = 1; - goto EXPONENTIAL; - } - } else { -EXPONENTIAL: for (i = size; ; i <<= 1) - if ((p = (b + i)) >= t) { - if ((p = t - size) > b && - (*cmp)(q, p, context) <= sense) - t = p; - else - b = p; - break; - } else if ((*cmp)(q, p, context) <= sense) { - t = p; - if (i == size) - big = 0; - goto FASTCASE; - } else - b = p; - while (t > b+size) { - i = (((t - b) / size) >> 1) * size; - if ((*cmp)(q, p = b + i, context) <= sense) - t = p; - else - b = p; - } - goto COPY; -FASTCASE: while (i > size) - if ((*cmp)(q, - p = b + (i >>= 1), context) <= sense) - t = p; - else - b = p; -COPY: b = t; - } - i = size; - if (q == f1) { - if (iflag) { - ICOPY_LIST(f2, tp2, b); - ICOPY_ELT(f1, tp2, i); - } else { - CCOPY_LIST(f2, tp2, b); - CCOPY_ELT(f1, tp2, i); - } - } else { - if (iflag) { - ICOPY_LIST(f1, tp2, b); - ICOPY_ELT(f2, tp2, i); - } else { - CCOPY_LIST(f1, tp2, b); - CCOPY_ELT(f2, tp2, i); - } - } - } - if (f2 < l2) { - if (iflag) - ICOPY_LIST(f2, tp2, l2); - else - CCOPY_LIST(f2, tp2, l2); - } else if (f1 < l1) { - if (iflag) - ICOPY_LIST(f1, tp2, l1); - else - CCOPY_LIST(f1, tp2, l1); - } - *p1 = l2; - } - tp2 = list1; /* swap list1, list2 */ - list1 = list2; - list2 = tp2; - last = list2 + nmemb*size; - } - if (base == list2) { - memmove(list2, list1, nmemb*size); - list2 = list1; - } - free(list2); - return (0); -} - -#define swap(a, b) { \ - s = b; \ - i = size; \ - do { \ - tmp = *a; *a++ = *s; *s++ = tmp; \ - } while (--i); \ - a -= size; \ - } -#define reverse(bot, top) { \ - s = top; \ - do { \ - i = size; \ - do { \ - tmp = *bot; *bot++ = *s; *s++ = tmp; \ - } while (--i); \ - s -= size2; \ - } while(bot < s); \ -} - -/* - * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of - * increasing order, list2 in a corresponding linked list. Checks for runs - * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL - * is defined. Otherwise simple pairwise merging is used.) - */ -static void -setup(u_char *list1, u_char *list2, size_t n, size_t size, CFComparisonResult (*cmp)(const void *, const void *, void *), void *context) -{ - long i, length, size2, tmp, sense; - u_char *f1, *f2, *s, *l2, *last, *p2; - - size2 = size*2; - if (n <= 5) { - insertionsort(list1, n, size, cmp, context); - *EVAL(list2) = (u_char*) list2 + n*size; - return; - } - /* - * Avoid running pointers out of bounds; limit n to evens - * for simplicity. - */ - i = 4 + (n & 1); - insertionsort(list1 + (n - i) * size, i, size, cmp, context); - last = list1 + size * (n - i); - *EVAL(list2 + (last - list1)) = list2 + n * size; - -#ifdef NATURAL - p2 = list2; - f1 = list1; - sense = (cmp(f1, f1 + size, context) > 0); - for (; f1 < last; sense = !sense) { - length = 2; - /* Find pairs with same sense. */ - for (f2 = f1 + size2; f2 < last; f2 += size2) { - if ((cmp(f2, f2+ size, context) > 0) != sense) - break; - length += 2; - } - if (length < THRESHOLD) { /* Pairwise merge */ - do { - p2 = *EVAL(p2) = f1 + size2 - list1 + list2; - if (sense > 0) - swap (f1, f1 + size); - } while ((f1 += size2) < f2); - } else { /* Natural merge */ - l2 = f2; - for (f2 = f1 + size2; f2 < l2; f2 += size2) { - if ((cmp(f2-size, f2, context) > 0) != sense) { - p2 = *EVAL(p2) = f2 - list1 + list2; - if (sense > 0) - reverse(f1, f2-size); - f1 = f2; - } - } - if (sense > 0) - reverse (f1, f2-size); - f1 = f2; - if (f2 < last || cmp(f2 - size, f2, context) > 0) - p2 = *EVAL(p2) = f2 - list1 + list2; - else - p2 = *EVAL(p2) = list2 + n*size; - } - } -#else /* pairwise merge only. */ - for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { - p2 = *EVAL(p2) = p2 + size2; - if (cmp (f1, f1 + size, context) > 0) - swap(f1, f1 + size); - } -#endif /* NATURAL */ -} - -/* - * This is to avoid out-of-bounds addresses in sorting the - * last 4 elements. - */ -static void -insertionsort(u_char *a, size_t n, size_t size, CFComparisonResult (*cmp)(const void *, const void *, void *), void *context) -{ - u_char *ai, *s, *t, *u, tmp; - long i; - - for (ai = a+size; --n >= 1; ai += size) - for (t = ai; t > a; t -= size) { - u = t - size; - if (cmp(u, t, context) <= 0) - break; - swap(u, t); - } -} - -/* Another version, also not so completely different, in order to handle necessary write-barriers in the face of GC */ - -#undef ASSIGN -#define ASSIGN __CFObjCStrongAssign -//#define ASSIGN log_assign - -static void setup_wb(u_char *, u_char *, size_t, size_t, CFComparisonResult (*)(), void *); -static void insertionsort_wb(u_char *, size_t, size_t, CFComparisonResult (*)(), void *); - -#undef ICOPY_ELT - -#define ICOPY_ELT(src, dst, i) \ - do \ - ASSIGN(*(const void**)src, (const void *)dst), src += ISIZE, dst += ISIZE; \ - while (i -= ISIZE) - -#undef ICOPY_LIST - -#define ICOPY_LIST(src, dst, last) \ - do \ - ASSIGN(*(const void **)src, (const void *)dst), src += ISIZE, dst += ISIZE; \ - while (src < last) - - -/* - * Arguments are as for qsort. - */ -static int -bsd_mergesort_wb(void *base, size_t nmemb, size_t size, CFComparisonResult (*cmp)(const void *, const void *, void *), void *context) -{ - long i, sense; - long big, iflag; - u_char *f1, *f2, *t, *b, *tp2, *q, *l1, *l2; - u_char *list2, *list1, *p2, *p, *last, **p1; - - if (size < PSIZE / 2) { /* Pointers must fit into 2 * size. */ - errno = EINVAL; - return (-1); - } - - if (nmemb == 0) - return (0); - - /* - * XXX - * Stupid subtraction for the Cray. - */ - iflag = 0; - if (!(size % ISIZE) && !(((char *)base - (char *)0) % ISIZE)) - iflag = 1; - - if (!iflag) - return -1; // only set up for "integer" swaps, e.g. long integer - - if ((list2 = CFAllocatorAllocate(NULL, (nmemb * size + PSIZE), __kCFAllocatorGCScannedMemory)) == NULL) - return (-1); - - list1 = base; - setup_wb(list1, list2, nmemb, size, cmp, context); - last = list2 + nmemb * size; - i = big = 0; - while (*EVAL(list2) != last) { - l2 = list1; - p1 = EVAL(list1); - for (tp2 = p2 = list2; p2 != last; p1 = EVAL(l2)) { - p2 = *EVAL(p2); - f1 = l2; - f2 = l1 = list1 + (p2 - list2); - if (p2 != last) - p2 = *EVAL(p2); - l2 = list1 + (p2 - list2); - while (f1 < l1 && f2 < l2) { - if ((*cmp)(f1, f2, context) <= 0) { - q = f2; - b = f1, t = l1; - sense = -1; - } else { - q = f1; - b = f2, t = l2; - sense = 0; - } - if (!big) { /* here i = 0 */ - while ((b += size) < t && cmp(q, b, context) >sense) - if (++i == 6) { - big = 1; - goto EXPONENTIAL; - } - } else { -EXPONENTIAL: for (i = size; ; i <<= 1) - if ((p = (b + i)) >= t) { - if ((p = t - size) > b && - (*cmp)(q, p, context) <= sense) - t = p; - else - b = p; - break; - } else if ((*cmp)(q, p, context) <= sense) { - t = p; - if (i == size) - big = 0; - goto FASTCASE; - } else - b = p; - while (t > b+size) { - i = (((t - b) / size) >> 1) * size; - if ((*cmp)(q, p = b + i, context) <= sense) - t = p; - else - b = p; - } - goto COPY; -FASTCASE: while (i > size) - if ((*cmp)(q, - p = b + (i >>= 1), context) <= sense) - t = p; - else - b = p; -COPY: b = t; - } - i = size; - if (q == f1) { - if (iflag) { - ICOPY_LIST(f2, tp2, b); - ICOPY_ELT(f1, tp2, i); - } else { - CCOPY_LIST(f2, tp2, b); - CCOPY_ELT(f1, tp2, i); - } - } else { - if (iflag) { - ICOPY_LIST(f1, tp2, b); - ICOPY_ELT(f2, tp2, i); - } else { - CCOPY_LIST(f1, tp2, b); - CCOPY_ELT(f2, tp2, i); - } - } - } - if (f2 < l2) { - if (iflag) - ICOPY_LIST(f2, tp2, l2); - else - CCOPY_LIST(f2, tp2, l2); - } else if (f1 < l1) { - if (iflag) - ICOPY_LIST(f1, tp2, l1); - else - CCOPY_LIST(f1, tp2, l1); - } - *p1 = l2; - } - tp2 = list1; /* swap list1, list2 */ - list1 = list2; - list2 = tp2; - last = list2 + nmemb*size; - } - if (base == list2) { - CF_WRITE_BARRIER_MEMMOVE(list2, list1, nmemb*size); - list2 = list1; - } - free(list2); - return (0); -} - - -#define swap_wb(a, b) { \ - const void *object = *(void **)a; \ - ASSIGN(*(const void **)b, (const void *)a); \ - ASSIGN(object, (const void *)b); \ + if (count < 1 || elementSize < 1) return; + CFIndex *indexes = CFSortIndexes(count, 0, ^(CFIndex a, CFIndex b) { return comparator((char *)list + a * elementSize, (char *)list + b * elementSize, context); }); // naturally stable + void *store = malloc(count * elementSize); + for (CFIndex idx = 0; idx < count; idx++) { + if (sizeof(uintptr_t) == elementSize) { + uintptr_t *a = (uintptr_t *)list + indexes[idx]; + uintptr_t *b = (uintptr_t *)store + idx; + *b = *a; + } else { + memmove((char *)store + idx * elementSize, (char *)list + indexes[idx] * elementSize, elementSize); + } } - -#define reverse_wb(bot, top) { \ - s = top; \ - do { \ - swap_wb(bot, s); \ - bot += size; \ - s -= size; \ - } while(bot < s); \ -} - -/* - * Optional hybrid natural/pairwise first pass. Eats up list1 in runs of - * increasing order, list2 in a corresponding linked list. Checks for runs - * when THRESHOLD/2 pairs compare with same sense. (Only used when NATURAL - * is defined. Otherwise simple pairwise merging is used.) - */ -static void -setup_wb(u_char *list1, u_char *list2, size_t n, size_t size, CFComparisonResult (*cmp)(const void *, const void *, void *), void *context) -{ - long i, length, size2, tmp, sense; - u_char *f1, *f2, *s, *l2, *last, *p2; - - size2 = size*2; - if (n <= 5) { - insertionsort_wb(list1, n, size, cmp, context); - *EVAL(list2) = (u_char*) list2 + n*size; - return; - } - /* - * Avoid running pointers out of bounds; limit n to evens - * for simplicity. - */ - i = 4 + (n & 1); - insertionsort_wb(list1 + (n - i) * size, i, size, cmp, context); - last = list1 + size * (n - i); - *EVAL(list2 + (last - list1)) = list2 + n * size; - -#ifdef NATURAL - p2 = list2; - f1 = list1; - sense = (cmp(f1, f1 + size, context) > 0); - for (; f1 < last; sense = !sense) { - length = 2; - /* Find pairs with same sense. */ - for (f2 = f1 + size2; f2 < last; f2 += size2) { - if ((cmp(f2, f2+ size, context) > 0) != sense) - break; - length += 2; - } - if (length < THRESHOLD) { /* Pairwise merge */ - do { - p2 = *EVAL(p2) = f1 + size2 - list1 + list2; - if (sense > 0) - swap (f1, f1 + size); - } while ((f1 += size2) < f2); - } else { /* Natural merge */ - l2 = f2; - for (f2 = f1 + size2; f2 < l2; f2 += size2) { - if ((cmp(f2-size, f2, context) > 0) != sense) { - p2 = *EVAL(p2) = f2 - list1 + list2; - if (sense > 0) { - reverse_wb(f1, f2-size); - } - f1 = f2; - } - } - if (sense > 0) { - reverse_wb (f1, f2-size); - } - f1 = f2; - if (f2 < last || cmp(f2 - size, f2, context) > 0) { - p2 = *EVAL(p2) = f2 - list1 + list2; - } - else { - p2 = *EVAL(p2) = list2 + n*size; - } - } - } -#else /* pairwise merge only. */ -#error unchanged - for (f1 = list1, p2 = list2; f1 < last; f1 += size2) { - p2 = *EVAL(p2) = p2 + size2; - if (cmp (f1, f1 + size, context) > 0) - swap_wb(f1, f1 + size); - } -#endif /* NATURAL */ -} - -/* - * This is to avoid out-of-bounds addresses in sorting the - * last 4 elements. - */ -static void -insertionsort_wb(u_char *a, size_t n, size_t size, CFComparisonResult (*cmp)(const void *, const void *, void *), void *context) -{ - u_char *ai, *s, *t, *u, tmp; - long i; - - for (ai = a+size; --n >= 1; ai += size) - for (t = ai; t > a; t -= size) { - u = t - size; - if (cmp(u, t, context) <= 0) - break; - swap(u, t); - } + // no swapping or modification of the original list has occurred until this point + objc_memmove_collectable(list, store, count * elementSize); + free(store); + free(indexes); } +/* Comparator is passed the address of the values. */ void CFMergeSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context) { - if (CF_USING_COLLECTABLE_MEMORY && (auto_zone_get_layout_type(__CFCollectableZone, list) & AUTO_UNSCANNED) == 0) - bsd_mergesort_wb(list, count, elementSize, comparator, context); - else - bsd_mergesort(list, count, elementSize, comparator, context); -} - -#undef NATURAL -#undef THRESHOLD -#undef ISIZE -#undef PSIZE -#undef ICOPY_LIST -#undef ICOPY_ELT -#undef CCOPY_LIST -#undef CCOPY_ELT -#undef EVAL -#undef swap -#undef reverse - -/* ===================================================================== */ + if (count < 1 || elementSize < 1) return; + CFIndex *indexes = CFSortIndexes(count, kCFSortStable, ^(CFIndex a, CFIndex b) { return comparator((char *)list + a * elementSize, (char *)list + b * elementSize, context); }); // naturally stable + void *store = malloc(count * elementSize); + for (CFIndex idx = 0; idx < count; idx++) { + if (sizeof(uintptr_t) == elementSize) { + uintptr_t *a = (uintptr_t *)list + indexes[idx]; + uintptr_t *b = (uintptr_t *)store + idx; + *b = *a; + } else { + memmove((char *)store + idx * elementSize, (char *)list + indexes[idx] * elementSize, elementSize); + } + } + // no swapping or modification of the original list has occurred until this point + objc_memmove_collectable(list, store, count * elementSize); + free(store); + free(indexes); +} -#undef EINVAL