123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274 |
- // © 2016 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- /*
- *******************************************************************************
- *
- * Copyright (C) 2003-2013, International Business Machines
- * Corporation and others. All Rights Reserved.
- *
- *******************************************************************************
- * file name: uarrsort.c
- * encoding: UTF-8
- * tab size: 8 (not used)
- * indentation:4
- *
- * created on: 2003aug04
- * created by: Markus W. Scherer
- *
- * Internal function for sorting arrays.
- */
- #include <cstddef>
- #include "unicode/utypes.h"
- #include "cmemory.h"
- #include "uarrsort.h"
- enum {
- /**
- * "from Knuth"
- *
- * A binary search over 8 items performs 4 comparisons:
- * log2(8)=3 to subdivide, +1 to check for equality.
- * A linear search over 8 items on average also performs 4 comparisons.
- */
- MIN_QSORT=9,
- STACK_ITEM_SIZE=200
- };
- static constexpr int32_t sizeInMaxAlignTs(int32_t sizeInBytes) {
- return (sizeInBytes + sizeof(std::max_align_t) - 1) / sizeof(std::max_align_t);
- }
- /* UComparator convenience implementations ---------------------------------- */
- U_CAPI int32_t U_EXPORT2
- uprv_uint16Comparator(const void *context, const void *left, const void *right) {
- (void)context;
- return (int32_t)*(const uint16_t *)left - (int32_t)*(const uint16_t *)right;
- }
- U_CAPI int32_t U_EXPORT2
- uprv_int32Comparator(const void *context, const void *left, const void *right) {
- (void)context;
- return *(const int32_t *)left - *(const int32_t *)right;
- }
- U_CAPI int32_t U_EXPORT2
- uprv_uint32Comparator(const void *context, const void *left, const void *right) {
- (void)context;
- uint32_t l=*(const uint32_t *)left, r=*(const uint32_t *)right;
- /* compare directly because (l-r) would overflow the int32_t result */
- if(l<r) {
- return -1;
- } else if(l==r) {
- return 0;
- } else /* l>r */ {
- return 1;
- }
- }
- /* Insertion sort using binary search --------------------------------------- */
- U_CAPI int32_t U_EXPORT2
- uprv_stableBinarySearch(char *array, int32_t limit, void *item, int32_t itemSize,
- UComparator *cmp, const void *context) {
- int32_t start=0;
- UBool found=false;
- /* Binary search until we get down to a tiny sub-array. */
- while((limit-start)>=MIN_QSORT) {
- int32_t i=(start+limit)/2;
- int32_t diff=cmp(context, item, array+i*itemSize);
- if(diff==0) {
- /*
- * Found the item. We look for the *last* occurrence of such
- * an item, for stable sorting.
- * If we knew that there will be only few equal items,
- * we could break now and enter the linear search.
- * However, if there are many equal items, then it should be
- * faster to continue with the binary search.
- * It seems likely that we either have all unique items
- * (where found will never become true in the insertion sort)
- * or potentially many duplicates.
- */
- found=true;
- start=i+1;
- } else if(diff<0) {
- limit=i;
- } else {
- start=i;
- }
- }
- /* Linear search over the remaining tiny sub-array. */
- while(start<limit) {
- int32_t diff=cmp(context, item, array+start*itemSize);
- if(diff==0) {
- found=true;
- } else if(diff<0) {
- break;
- }
- ++start;
- }
- return found ? (start-1) : ~start;
- }
- static void
- doInsertionSort(char *array, int32_t length, int32_t itemSize,
- UComparator *cmp, const void *context, void *pv) {
- int32_t j;
- for(j=1; j<length; ++j) {
- char *item=array+j*itemSize;
- int32_t insertionPoint=uprv_stableBinarySearch(array, j, item, itemSize, cmp, context);
- if(insertionPoint<0) {
- insertionPoint=~insertionPoint;
- } else {
- ++insertionPoint; /* one past the last equal item */
- }
- if(insertionPoint<j) {
- char *dest=array+insertionPoint*itemSize;
- uprv_memcpy(pv, item, itemSize); /* v=array[j] */
- uprv_memmove(dest+itemSize, dest, (j-insertionPoint)*(size_t)itemSize);
- uprv_memcpy(dest, pv, itemSize); /* array[insertionPoint]=v */
- }
- }
- }
- static void
- insertionSort(char *array, int32_t length, int32_t itemSize,
- UComparator *cmp, const void *context, UErrorCode *pErrorCode) {
- icu::MaybeStackArray<std::max_align_t, sizeInMaxAlignTs(STACK_ITEM_SIZE)> v;
- if (sizeInMaxAlignTs(itemSize) > v.getCapacity() &&
- v.resize(sizeInMaxAlignTs(itemSize)) == nullptr) {
- *pErrorCode = U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- doInsertionSort(array, length, itemSize, cmp, context, v.getAlias());
- }
- /* QuickSort ---------------------------------------------------------------- */
- /*
- * This implementation is semi-recursive:
- * It recurses for the smaller sub-array to shorten the recursion depth,
- * and loops for the larger sub-array.
- *
- * Loosely after QuickSort algorithms in
- * Niklaus Wirth
- * Algorithmen und Datenstrukturen mit Modula-2
- * B.G. Teubner Stuttgart
- * 4. Auflage 1986
- * ISBN 3-519-02260-5
- */
- static void
- subQuickSort(char *array, int32_t start, int32_t limit, int32_t itemSize,
- UComparator *cmp, const void *context,
- void *px, void *pw) {
- int32_t left, right;
- /* start and left are inclusive, limit and right are exclusive */
- do {
- if((start+MIN_QSORT)>=limit) {
- doInsertionSort(array+start*itemSize, limit-start, itemSize, cmp, context, px);
- break;
- }
- left=start;
- right=limit;
- /* x=array[middle] */
- uprv_memcpy(px, array+(size_t)((start+limit)/2)*itemSize, itemSize);
- do {
- while(/* array[left]<x */
- cmp(context, array+left*itemSize, px)<0
- ) {
- ++left;
- }
- while(/* x<array[right-1] */
- cmp(context, px, array+(right-1)*itemSize)<0
- ) {
- --right;
- }
- /* swap array[left] and array[right-1] via w; ++left; --right */
- if(left<right) {
- --right;
- if(left<right) {
- uprv_memcpy(pw, array+(size_t)left*itemSize, itemSize);
- uprv_memcpy(array+(size_t)left*itemSize, array+(size_t)right*itemSize, itemSize);
- uprv_memcpy(array+(size_t)right*itemSize, pw, itemSize);
- }
- ++left;
- }
- } while(left<right);
- /* sort sub-arrays */
- if((right-start)<(limit-left)) {
- /* sort [start..right[ */
- if(start<(right-1)) {
- subQuickSort(array, start, right, itemSize, cmp, context, px, pw);
- }
- /* sort [left..limit[ */
- start=left;
- } else {
- /* sort [left..limit[ */
- if(left<(limit-1)) {
- subQuickSort(array, left, limit, itemSize, cmp, context, px, pw);
- }
- /* sort [start..right[ */
- limit=right;
- }
- } while(start<(limit-1));
- }
- static void
- quickSort(char *array, int32_t length, int32_t itemSize,
- UComparator *cmp, const void *context, UErrorCode *pErrorCode) {
- /* allocate two intermediate item variables (x and w) */
- icu::MaybeStackArray<std::max_align_t, sizeInMaxAlignTs(STACK_ITEM_SIZE) * 2> xw;
- if(sizeInMaxAlignTs(itemSize)*2 > xw.getCapacity() &&
- xw.resize(sizeInMaxAlignTs(itemSize) * 2) == nullptr) {
- *pErrorCode=U_MEMORY_ALLOCATION_ERROR;
- return;
- }
- subQuickSort(array, 0, length, itemSize, cmp, context,
- xw.getAlias(), xw.getAlias() + sizeInMaxAlignTs(itemSize));
- }
- /* uprv_sortArray() API ----------------------------------------------------- */
- /*
- * Check arguments, select an appropriate implementation,
- * cast the array to char * so that array+i*itemSize works.
- */
- U_CAPI void U_EXPORT2
- uprv_sortArray(void *array, int32_t length, int32_t itemSize,
- UComparator *cmp, const void *context,
- UBool sortStable, UErrorCode *pErrorCode) {
- if(pErrorCode==nullptr || U_FAILURE(*pErrorCode)) {
- return;
- }
- if((length>0 && array==nullptr) || length<0 || itemSize<=0 || cmp==nullptr) {
- *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
- return;
- }
- if(length<=1) {
- return;
- } else if(length<MIN_QSORT || sortStable) {
- insertionSort((char *)array, length, itemSize, cmp, context, pErrorCode);
- } else {
- quickSort((char *)array, length, itemSize, cmp, context, pErrorCode);
- }
- }
|