blob: 172860a6c4b8469f31cd7b6562b6b827458ceb7d [file] [log] [blame]
/* CFSortFunctions.c
Copyright (c) 1999-2016, Apple Inc. and the Swift project authors
Portions Copyright (c) 2014-2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
Responsibility: Christopher Kane
*/
#include <CoreFoundation/CFBase.h>
#include "CFInternal.h"
#if __HAS_DISPATCH__
#include <dispatch/dispatch.h>
#if (DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED) && __has_include(<dispatch/private.h>)
#include <dispatch/private.h>
#endif
#endif
#include "CFLogUtilities.h"
#include "CFInternal.h"
#if __has_include(<checkint.h>)
#include <checkint.h>
#else
enum {
CHECKINT_NO_ERROR = 0,
CHECKINT_OVERFLOW_ERROR = (1 << 0),
CHECKINT_TYPE_ERROR = (1 << 1)
};
#define __check_int32_add(x, y, err) (x + y)
#define __check_uint32_add(x, y, err) (x + y)
#define __check_int64_add(x, y, err) (x + y)
#define __check_uint64_add(x, y, err) (x + y)
#define __check_int32_sub(x, y, err) (x - y)
#define __check_uint32_sub(x, y, err) (x - y)
#define __check_int64_sub(x, y, err) (x - y)
#define __check_uint64_sub(x, y, err) (x - y)
#define __check_int32_mul(x, y, err) (x * y)
#define __check_uint32_mul(x, y, err) (x * y)
#define __check_int64_mul(x, y, err) (x * y)
#define __check_uint64_mul(x, y, err) (x * y)
#define __check_int32_div(x, y, err) (x / y)
#define __check_uint32_div(x, y, err) (x / y)
#define __check_int64_div(x, y, err) (x / y)
#define __check_uint64_div(x, y, err) (x / y)
#define __checkint_int64_mul(x, y, err) __check_int64_mul(x, y, err)
#define __checkint_int32_mul(x, y, err) __check_int32_mul(x, y, err)
#define __checkint_uint64_add(x, y, err) __check_uint64_add(x, y, err)
#define __checkint_uint32_add(x, y, err) __check_uint32_add(x, y, err)
#endif
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);
/*
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
*/
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 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);
}
}
#if __HAS_DISPATCH__
// 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;
}
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++;
}
}
}
/* 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] = (VALUE_TYPE *)malloc(sz * sizeof(VALUE_TYPE));
}
VALUE_TYPE **tmps = stack_tmps;
dispatch_queue_t q = __CFDispatchQueueGetGenericMatchingCurrent();
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));
}
}
for (INDEX_TYPE idx = 0; idx < num_sect; idx++) {
free(stack_tmps[idx]);
}
}
#endif
// fills an array of indexes (of length count) giving the indexes 0 - count-1, as sorted by the comparator block
void CFSortIndexes(CFIndex *indexBuffer, CFIndex count, CFOptionFlags opts, CFComparisonResult (^cmp)(CFIndex, CFIndex)) {
if (count < 1) return;
if (INTPTR_MAX / sizeof(CFIndex) < count) return;
int32_t ncores = 0;
if (opts & kCFSortConcurrent) {
ncores = __CFActiveProcessorCount();
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;
}
}
#if __HAS_DISPATCH__
if (count <= 65536) {
for (CFIndex idx = 0; idx < count; idx++) indexBuffer[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, __CFDispatchQueueGetGenericMatchingCurrent(), ^(size_t n) {
CFIndex idx = n * sz, lim = __CFMin(idx + sz, count);
for (; idx < lim; idx++) indexBuffer[idx] = idx;
});
}
#else
for (CFIndex idx = 0; idx < count; idx++) indexBuffer[idx] = idx;
#endif
#if __HAS_DISPATCH__
if (opts & kCFSortConcurrent) {
__CFSortIndexesN(indexBuffer, count, ncores, cmp); // naturally stable
return;
}
#endif
STACK_BUFFER_DECL(VALUE_TYPE, local, count <= 4096 ? count : 1);
VALUE_TYPE *tmp = (count <= 4096) ? local : (VALUE_TYPE *)malloc(count * sizeof(VALUE_TYPE));
__CFSimpleMergeSort(indexBuffer, count, tmp, cmp); // naturally stable
if (local != tmp) free(tmp);
}
typedef CF_ENUM(uint8_t, _CFOverflowResult) {
_CFOverflowResultOK = 0,
_CFOverflowResultNegativeParameters,
_CFOverflowResultOverflows,
};
// Overflow utilities for positive integers
CF_INLINE _CFOverflowResult _CFIntegerProductWouldOverflow(CFIndex si_a, CFIndex si_b) {
_CFOverflowResult result = _CFOverflowResultOK;
if (si_a < 0 || si_b < 0) {
// we explicitly only implement a subset of the overflow checking, so report failure if out of domain
result = _CFOverflowResultNegativeParameters;
} else {
int32_t err = CHECKINT_NO_ERROR;
#if __LP64__
__checkint_int64_mul(si_a, si_b, &err);
#else
__checkint_int32_mul(si_a, si_b, &err);
#endif
if (err != CHECKINT_NO_ERROR) {
result = _CFOverflowResultOverflows;
}
}
return result;
}
CF_INLINE _CFOverflowResult _CFPointerSumWouldOverflow(void *p, size_t n) {
_CFOverflowResult result = _CFOverflowResultOK;
int32_t err = CHECKINT_NO_ERROR;
#if __LP64__
__checkint_uint64_add((uint64_t)p, (uint64_t)n, &err);
#else
__checkint_uint32_add((uint32_t)p, (uint32_t)n, &err);
#endif
if (err != CHECKINT_NO_ERROR) {
result = _CFOverflowResultOverflows;
}
return result;
}
/* Comparator is passed the address of the values. */
void CFQSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context) {
if (count < 2 || elementSize < 1) return;
_CFOverflowResult overflowResult = _CFIntegerProductWouldOverflow(count, elementSize);
if (overflowResult != _CFOverflowResultOK) {
CFLog(kCFLogLevelError, CFSTR("Unable to qsort array - count: %ld elementSize: %ld product overflows"), (long)count, (long)elementSize);
CRSetCrashLogMessage("qsort - count/elementSize overflow");
HALT;
}
overflowResult = _CFPointerSumWouldOverflow(list, count * elementSize);
if (overflowResult != _CFOverflowResultOK) {
CFLog(kCFLogLevelError, CFSTR("Unable to qsort array - list: %lu count: %ld elementSize: %ld - array access overflows"), (unsigned long)list, (long)count, (long)elementSize);
CRSetCrashLogMessage("qsort - array access overflow");
HALT;
}
STACK_BUFFER_DECL(CFIndex, locali, count <= 4096 ? count : 1);
CFIndex *indexes = (count <= 4096) ? locali : (CFIndex *)malloc(count * sizeof(CFIndex));
if (indexes == NULL) {
CFLog(kCFLogLevelError, CFSTR("unable to qsort array - malloc failed"));
CRSetCrashLogMessage("qsort - malloc failed");
HALT;
}
CFSortIndexes(indexes, count, 0, ^(CFIndex a, CFIndex b) { return comparator((char *)list + a * elementSize, (char *)list + b * elementSize, context); });
STACK_BUFFER_DECL(uint8_t, locals, count <= (16 * 1024 / elementSize) ? count * elementSize : 1);
void *store = (count <= (16 * 1024 / elementSize)) ? locals : malloc(count * elementSize);
overflowResult = _CFPointerSumWouldOverflow(store, count * elementSize);
if (overflowResult != _CFOverflowResultOK) {
CFLog(kCFLogLevelError, CFSTR("Unable to qsort array - list: %lu count: %ld elementSize: %ld array - store overflows"), (unsigned long)list, (long)count, (long)elementSize);
CRSetCrashLogMessage("qsort - array storage overflow");
HALT;
}
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
memmove(list, store, count * elementSize);
if (locals != store) free(store);
if (locali != indexes) free(indexes);
}
/* Comparator is passed the address of the values. */
void CFMergeSortArray(void *list, CFIndex count, CFIndex elementSize, CFComparatorFunction comparator, void *context) {
if (count < 2 || elementSize < 1) return;
_CFOverflowResult overflowResult = _CFIntegerProductWouldOverflow(count, elementSize);
if (overflowResult != _CFOverflowResultOK) {
CFLog(kCFLogLevelError, CFSTR("Unable to mergesort array - count: %ld elementSize: %ld overflows"), (long)count, (long)elementSize);
CRSetCrashLogMessage("merge sort - count/elementSize overflow");
HALT;
}
overflowResult = _CFPointerSumWouldOverflow(list, count * elementSize);
if (overflowResult != _CFOverflowResultOK) {
CFLog(kCFLogLevelError, CFSTR("Unable to mergesort array - list: %lu count: %ld elementSize: %ld - array access overflows"), (unsigned long)list, (long)count, (long)elementSize);
CRSetCrashLogMessage("merge sort - array access overflow");
HALT;
}
STACK_BUFFER_DECL(CFIndex, locali, count <= 4096 ? count : 1);
CFIndex *indexes = (count <= 4096) ? locali : (CFIndex *)malloc(count * sizeof(CFIndex));
if (indexes == NULL) {
CFLog(kCFLogLevelError, CFSTR("unable to mergesort array - malloc failed"));
CRSetCrashLogMessage("merge sort - malloc failure");
HALT;
}
CFSortIndexes(indexes, count, kCFSortStable, ^(CFIndex a, CFIndex b) { return comparator((char *)list + a * elementSize, (char *)list + b * elementSize, context); });
STACK_BUFFER_DECL(uint8_t, locals, count <= (16 * 1024 / elementSize) ? count * elementSize : 1);
void *store = (count <= (16 * 1024 / elementSize)) ? locals : malloc(count * elementSize);
overflowResult = _CFPointerSumWouldOverflow(store, count * elementSize);
if (overflowResult != _CFOverflowResultOK) {
CFLog(kCFLogLevelError, CFSTR("Unable to mergesort array - list: %lu count: %ld elementSize: %ld - array store overflows"), (unsigned long)list, (long)count, (long)elementSize);
CRSetCrashLogMessage("merge sort - overflow array storage");
HALT;
}
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
memmove(list, store, count * elementSize);
if (locals != store) free(store);
if (locali != indexes) free(indexes);
}