2 * Copyright (c) 2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 Copyright (c) 1999-2009, Apple Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFBase.h>
29 #include "CFInternal.h"
30 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
31 #include <dispatch/dispatch.h>
32 #include <sys/sysctl.h>
37 kCFSortConcurrent
= (1 << 0),
38 kCFSortStable
= (1 << 4),
41 typedef CFIndex VALUE_TYPE
;
42 typedef CFIndex INDEX_TYPE
;
43 typedef CFComparisonResult CMP_RESULT_TYPE
;
44 typedef CMP_RESULT_TYPE (^COMPARATOR_BLOCK
)(VALUE_TYPE
, VALUE_TYPE
);
47 Number of elements in a list and expected number of compares,
48 when the initial short-circuiting compare is not done.
63 static void __CFSimpleMerge(VALUE_TYPE listp
[], INDEX_TYPE cnt1
, INDEX_TYPE cnt2
, VALUE_TYPE tmp
[], COMPARATOR_BLOCK cmp
) {
64 if (cnt1
<= 0 || cnt2
<= 0) return;
65 // if the last element of listp1 <= the first of listp2, lists are already ordered
66 if (16 < cnt1
+ cnt2
&& cmp(listp
[cnt1
- 1], listp
[cnt1
]) <= 0) return;
68 INDEX_TYPE idx
= 0, idx1
= 0, idx2
= cnt1
;
72 listp
[idx
] = tmp
[idx
];
76 if (cnt1
+ cnt2
<= idx2
) {
77 for (INDEX_TYPE t
= cnt1
+ cnt2
- 1; idx
<= t
; t
--) {
78 listp
[t
] = listp
[t
- cnt2
];
81 listp
[idx
] = tmp
[idx
];
85 VALUE_TYPE v1
= listp
[idx1
], v2
= listp
[idx2
];
86 if (cmp(v1
, v2
) <= 0) {
97 static void __CFSimpleMergeSort(VALUE_TYPE listp
[], INDEX_TYPE cnt
, VALUE_TYPE tmp
[], COMPARATOR_BLOCK cmp
) {
100 } else if (2 == cnt
) {
101 VALUE_TYPE v0
= listp
[0], v1
= listp
[1];
102 if (0 < cmp(v0
, v1
)) {
106 } else if (3 == cnt
) {
107 VALUE_TYPE v0
= listp
[0], v1
= listp
[1], v2
= listp
[2], vt
;
108 if (0 < cmp(v0
, v1
)) {
113 if (0 < cmp(v1
, v2
)) {
117 if (0 < cmp(v0
, v1
)) {
127 INDEX_TYPE half_cnt
= cnt
/ 2;
128 __CFSimpleMergeSort(listp
, half_cnt
, tmp
, cmp
);
129 __CFSimpleMergeSort(listp
+ half_cnt
, cnt
- half_cnt
, tmp
, cmp
);
130 __CFSimpleMerge(listp
, half_cnt
, cnt
- half_cnt
, tmp
, cmp
);
134 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
135 // if !right, put the cnt1 smallest values in tmp, else put the cnt2 largest values in tmp
136 static void __CFSortIndexesNMerge(VALUE_TYPE listp1
[], INDEX_TYPE cnt1
, VALUE_TYPE listp2
[], INDEX_TYPE cnt2
, VALUE_TYPE tmp
[], size_t right
, COMPARATOR_BLOCK cmp
) {
137 // if the last element of listp1 <= the first of listp2, lists are already ordered
138 if (16 < cnt1
+ cnt2
&& cmp(listp1
[cnt1
- 1], listp2
[0]) <= 0) {
139 memmove(tmp
, (right
? listp2
: listp1
), (right
? cnt2
: cnt1
) * sizeof(VALUE_TYPE
));
144 VALUE_TYPE
*listp1_end
= listp1
;
145 VALUE_TYPE
*listp2_end
= listp2
;
146 VALUE_TYPE
*tmp_end
= tmp
;
150 while (tmp_end
< tmp
) {
152 if (listp2
< listp2_end
) {
155 } else if (listp1
< listp1_end
) {
159 VALUE_TYPE v1
= *listp1
, v2
= *listp2
;
160 CMP_RESULT_TYPE res
= cmp(v1
, v2
);
171 VALUE_TYPE
*listp1_end
= listp1
+ cnt1
;
172 VALUE_TYPE
*listp2_end
= listp2
+ cnt2
;
173 VALUE_TYPE
*tmp_end
= tmp
+ cnt1
;
174 while (tmp
< tmp_end
) {
175 if (listp2_end
<= listp2
) {
178 } else if (listp1_end
<= listp1
) {
182 VALUE_TYPE v1
= *listp1
, v2
= *listp2
;
183 CMP_RESULT_TYPE res
= cmp(v1
, v2
);
197 /* Merging algorithm based on
198 "A New Parallel Sorting Algorithm based on Odd-Even Mergesort", Ezequiel Herruzo, et al
200 static void __CFSortIndexesN(VALUE_TYPE listp
[], INDEX_TYPE count
, int32_t ncores
, CMP_RESULT_TYPE (^cmp
)(INDEX_TYPE
, INDEX_TYPE
)) {
201 /* Divide the array up into up to ncores, multiple-of-16-sized, chunks */
202 INDEX_TYPE sz
= ((((count
+ ncores
- 1) / ncores
) + 15) / 16) * 16;
203 INDEX_TYPE num_sect
= (count
+ sz
- 1) / sz
;
204 INDEX_TYPE last_sect_len
= count
+ sz
- sz
* num_sect
;
206 STACK_BUFFER_DECL(VALUE_TYPE
*, stack_tmps
, num_sect
);
207 for (INDEX_TYPE idx
= 0; idx
< num_sect
; idx
++) {
208 stack_tmps
[idx
] = malloc(sz
* sizeof(VALUE_TYPE
));
210 VALUE_TYPE
**tmps
= stack_tmps
;
212 dispatch_queue_t q
= dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
);
213 dispatch_apply(num_sect
, q
, ^(size_t sect
) {
214 INDEX_TYPE sect_len
= (sect
< num_sect
- 1) ? sz
: last_sect_len
;
215 __CFSimpleMergeSort(listp
+ sect
* sz
, sect_len
, tmps
[sect
], cmp
); // naturally stable
218 INDEX_TYPE even_phase_cnt
= ((num_sect
/ 2) * 2);
219 INDEX_TYPE odd_phase_cnt
= (((num_sect
- 1) / 2) * 2);
220 for (INDEX_TYPE idx
= 0; idx
< (num_sect
+ 1) / 2; idx
++) {
221 dispatch_apply(even_phase_cnt
, q
, ^(size_t sect
) { // merge even
222 size_t right
= sect
& (size_t)0x1;
223 VALUE_TYPE
*left_base
= listp
+ sect
* sz
- (right
? sz
: 0);
224 VALUE_TYPE
*right_base
= listp
+ sect
* sz
+ (right
? 0 : sz
);
225 INDEX_TYPE sect2_len
= (sect
+ 1 + (right
? 0 : 1) == num_sect
) ? last_sect_len
: sz
;
226 __CFSortIndexesNMerge(left_base
, sz
, right_base
, sect2_len
, tmps
[sect
], right
, cmp
);
228 if (num_sect
& 0x1) {
229 memmove(tmps
[num_sect
- 1], listp
+ (num_sect
- 1) * sz
, last_sect_len
* sizeof(VALUE_TYPE
));
231 dispatch_apply(odd_phase_cnt
, q
, ^(size_t sect
) { // merge odd
232 size_t right
= sect
& (size_t)0x1;
233 VALUE_TYPE
*left_base
= tmps
[sect
+ (right
? 0 : 1)];
234 VALUE_TYPE
*right_base
= tmps
[sect
+ (right
? 1 : 2)];
235 INDEX_TYPE sect2_len
= (sect
+ 1 + (right
? 1 : 2) == num_sect
) ? last_sect_len
: sz
;
236 __CFSortIndexesNMerge(left_base
, sz
, right_base
, sect2_len
, listp
+ sect
* sz
+ sz
, right
, cmp
);
238 memmove(listp
+ 0 * sz
, tmps
[0], sz
* sizeof(VALUE_TYPE
));
239 if (!(num_sect
& 0x1)) {
240 memmove(listp
+ (num_sect
- 1) * sz
, tmps
[num_sect
- 1], last_sect_len
* sizeof(VALUE_TYPE
));
244 for (INDEX_TYPE idx
= 0; idx
< num_sect
; idx
++) {
245 free(stack_tmps
[idx
]);
250 // returns an array of indexes (of length count) giving the indexes 0 - count-1, as sorted by the comparator block
251 CFIndex
*CFSortIndexes(CFIndex count
, CFOptionFlags opts
, CFComparisonResult (^cmp
)(CFIndex
, CFIndex
)) {
252 if (count
< 1) return NULL
;
253 if (INTPTR_MAX
/ sizeof(CFIndex
) < count
) return NULL
;
254 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
256 if (opts
& kCFSortConcurrent
) {
257 int32_t mib
[2] = {CTL_HW
, HW_AVAILCPU
};
258 size_t len
= sizeof(ncores
);
259 sysctl(mib
, 2, &ncores
, &len
, NULL
, 0);
260 if (count
< 160 || ncores
< 2) {
261 opts
= (opts
& ~kCFSortConcurrent
);
262 } else if (count
< 640 && 2 < ncores
) {
264 } else if (count
< 3200 && 4 < ncores
) {
266 } else if (count
< 16000 && 8 < ncores
) {
273 CFIndex
*idxs
= malloc_zone_memalign(malloc_default_zone(), 64, count
* sizeof(CFIndex
));
274 if (!idxs
) return NULL
;
275 if (count
<= 65536) {
276 for (CFIndex idx
= 0; idx
< count
; idx
++) idxs
[idx
] = idx
;
278 /* Specifically hard-coded to 8; the count has to be very large before more chunks and/or cores is worthwhile. */
279 CFIndex sz
= ((((size_t)count
+ 15) / 16) * 16) / 8;
280 dispatch_apply(8, dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT
), ^(size_t n
) {
281 CFIndex idx
= n
* sz
, lim
= __CFMin(idx
+ sz
, count
);
282 for (; idx
< lim
; idx
++) idxs
[idx
] = idx
;
285 if (opts
& kCFSortConcurrent
) {
286 __CFSortIndexesN(idxs
, count
, ncores
, cmp
); // naturally stable
290 CFIndex
*idxs
= (CFIndex
*)malloc(count
* sizeof(CFIndex
));
291 if (!idxs
) return NULL
;
292 for (CFIndex idx
= 0; idx
< count
; idx
++) idxs
[idx
] = idx
;
294 STACK_BUFFER_DECL(VALUE_TYPE
, local
, count
<= 16384 ? count
: 1);
295 VALUE_TYPE
*tmp
= (count
<= 16384) ? local
: (VALUE_TYPE
*)malloc(count
* sizeof(VALUE_TYPE
));
296 __CFSimpleMergeSort(idxs
, count
, tmp
, cmp
); // naturally stable
297 if (local
!= tmp
) free(tmp
);
301 /* Comparator is passed the address of the values. */
302 void CFQSortArray(void *list
, CFIndex count
, CFIndex elementSize
, CFComparatorFunction comparator
, void *context
) {
303 if (count
< 1 || elementSize
< 1) return;
304 CFIndex
*indexes
= CFSortIndexes(count
, 0, ^(CFIndex a
, CFIndex b
) { return comparator((char *)list
+ a
* elementSize
, (char *)list
+ b
* elementSize
, context
); }); // naturally stable
305 void *store
= malloc(count
* elementSize
);
306 for (CFIndex idx
= 0; idx
< count
; idx
++) {
307 if (sizeof(uintptr_t) == elementSize
) {
308 uintptr_t *a
= (uintptr_t *)list
+ indexes
[idx
];
309 uintptr_t *b
= (uintptr_t *)store
+ idx
;
312 memmove((char *)store
+ idx
* elementSize
, (char *)list
+ indexes
[idx
] * elementSize
, elementSize
);
315 // no swapping or modification of the original list has occurred until this point
316 objc_memmove_collectable(list
, store
, count
* elementSize
);
321 /* Comparator is passed the address of the values. */
322 void CFMergeSortArray(void *list
, CFIndex count
, CFIndex elementSize
, CFComparatorFunction comparator
, void *context
) {
323 if (count
< 1 || elementSize
< 1) return;
324 CFIndex
*indexes
= CFSortIndexes(count
, kCFSortStable
, ^(CFIndex a
, CFIndex b
) { return comparator((char *)list
+ a
* elementSize
, (char *)list
+ b
* elementSize
, context
); }); // naturally stable
325 void *store
= malloc(count
* elementSize
);
326 for (CFIndex idx
= 0; idx
< count
; idx
++) {
327 if (sizeof(uintptr_t) == elementSize
) {
328 uintptr_t *a
= (uintptr_t *)list
+ indexes
[idx
];
329 uintptr_t *b
= (uintptr_t *)store
+ idx
;
332 memmove((char *)store
+ idx
* elementSize
, (char *)list
+ indexes
[idx
] * elementSize
, elementSize
);
335 // no swapping or modification of the original list has occurred until this point
336 objc_memmove_collectable(list
, store
, count
* elementSize
);