diff --git a/headers/compatibility/gnu/stdlib.h b/headers/compatibility/gnu/stdlib.h deleted file mode 100644 index 33057db5d6..0000000000 --- a/headers/compatibility/gnu/stdlib.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2020 Haiku, Inc. All rights reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Isaac Turner, turner.isaac@gmail.com - * Jacob Secunda, secundaja@gmail.com - */ -#ifndef _GNU_STDLIB_H_ -#define _GNU_STDLIB_H_ - - -#include_next -#include - - -#ifdef _DEFAULT_SOURCE - - -#ifdef __cplusplus -extern "C" { -#endif - -typedef int (*_compare_function_qsort_r)(const void*, const void*, void*); - -extern void qsort_r(void* base, size_t numElements, size_t sizeOfElement, - _compare_function_qsort_r, void* cookie); - -#ifdef __cplusplus -} -#endif - - -#endif /* _DEFAULT_SOURCE */ - - -#endif /* _GNU_STDLIB_H_ */ diff --git a/headers/posix/stdlib.h b/headers/posix/stdlib.h index 955bb69858..f187d1aed3 100644 --- a/headers/posix/stdlib.h +++ b/headers/posix/stdlib.h @@ -120,19 +120,25 @@ extern void lcong48(unsigned short int param[7]); /* search and sort functions */ typedef int (*_compare_function)(const void *, const void *); +typedef int (*_compare_function_qsort_r)(const void*, const void*, void*); extern void *bsearch(const void *key, const void *base, size_t numElements, size_t sizeOfElement, _compare_function); +extern void qsort(void *base, size_t numElements, size_t sizeOfElement, + _compare_function); +extern void qsort_r(void* base, size_t numElements, size_t sizeOfElement, + _compare_function_qsort_r, void* cookie); + +#ifdef _DEFAULT_SOURCE extern int heapsort(void *base, size_t numElements, size_t sizeOfElement, _compare_function); extern int mergesort(void *base, size_t numElements, size_t sizeOfElement, _compare_function); -extern void qsort(void *base, size_t numElements, size_t sizeOfElement, - _compare_function); extern int radixsort(u_char const **base, int numElements, u_char const *table, u_int endByte); extern int sradixsort(u_char const **base, int numElements, u_char const *table, u_int endByte); +#endif /* misc math functions */ extern int abs(int number); diff --git a/src/libs/gnu/Jamfile b/src/libs/gnu/Jamfile index 386de68895..230562e6ed 100644 --- a/src/libs/gnu/Jamfile +++ b/src/libs/gnu/Jamfile @@ -17,7 +17,6 @@ for architectureObject in [ MultiArchSubDirSetup ] { SharedLibrary [ MultiArchDefaultGristFiles libgnu.so ] : crypt.cpp - qsort.c sched_affinity.cpp sched_getcpu.cpp xattr.cpp diff --git a/src/libs/gnu/qsort.c b/src/libs/gnu/qsort.c deleted file mode 100644 index 154008cacf..0000000000 --- a/src/libs/gnu/qsort.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2020 Haiku, Inc. All rights reserved. - * Distributed under the terms of the MIT License. - * - * Authors: - * Isaac Turner, turner.isaac@gmail.com - * Jacob Secunda, secundaja@gmail.com - */ - - -#include -#include - - -#define SORT_R_SWAP(a, b, tmp) ((tmp) = (a), (a) = (b), (b) = (tmp)) - - -/* swap itemA and itemB */ -/* itemA and itemB must not be equal! */ -static inline void -sort_r_swap(char* __restrict itemA, char* __restrict itemB, - size_t sizeOfElement) -{ - char tmp; - char* end = itemA + sizeOfElement; - for (; itemA < end; itemA++, itemB++) - SORT_R_SWAP(* itemA, * itemB, tmp); -} - - -/* swap itemA and itemB if itemA > itemB */ -/* itemA and itemB must not be equal! */ -static inline int -sort_r_cmpswap(char* __restrict itemA, char* __restrict itemB, - size_t sizeOfElement, _compare_function_qsort_r cmpFunc, void* cookie) -{ - if (cmpFunc(itemA, itemB, cookie) > 0) { - sort_r_swap(itemA, itemB, sizeOfElement); - return 1; - } - - return 0; -} - - -/* - Swap consecutive blocks of bytes of size na and nb starting at memory addr - ptr, with the smallest swap so that the blocks are in the opposite order. - Blocks may be internally re-ordered e.g. - - 12345ab -> ab34512 - 123abc -> abc123 - 12abcde -> deabc12 -*/ -static inline void -sort_r_swap_blocks(char* ptr, size_t numBytesA, size_t numBytesB) -{ - if (numBytesA > 0 && numBytesB > 0) { - if (numBytesA > numBytesB) - sort_r_swap(ptr, ptr + numBytesA, numBytesB); - else - sort_r_swap(ptr, ptr + numBytesB, numBytesA); - } -} - - -/* Note: quicksort is not stable, equivalent values may be swapped */ -static inline void -sort_r_simple(char* base, size_t numElements, size_t sizeOfElement, - _compare_function_qsort_r cmpFunc, void* cookie) -{ - char* end = base + (numElements * sizeOfElement); - - if (numElements < 10) { - // Insertion sort for arbitrarily small inputs - - char* pivIndexA; - char* pivIndexB; - for (pivIndexA = base + sizeOfElement; pivIndexA < end; - pivIndexA += sizeOfElement) { - pivIndexB = pivIndexA; - while (pivIndexB > base - && sort_r_cmpswap(pivIndexB - sizeOfElement , pivIndexB, - sizeOfElement, cmpFunc, cookie)) { - pivIndexB -= sizeOfElement; - } - } - } else { - // Quicksort when numElements >= 10 - - int cmp; - char* nextPivCmpItem; // pl - char* nextPivEqualsPos; // ple - char* lastPivCmpItem; // pr - char* lastPivEqualsPos; // pre - char* pivot; - char* last = base + sizeOfElement * (numElements - 1); - char* tmp; - - // Use median of second, middle and second-last items as pivot. - // First and last may have been swapped with pivot and therefore be - // extreme. - char* pivList[3]; - pivList[0] = base + sizeOfElement; - pivList[1] = base + sizeOfElement * (numElements / 2); - pivList[2] = last - sizeOfElement; - - if (cmpFunc(pivList[0], pivList[1], cookie) > 0) - SORT_R_SWAP(pivList[0], pivList[1], tmp); - if (cmpFunc(pivList[1], pivList[2], cookie) > 0) { - SORT_R_SWAP(pivList[1], pivList[2], tmp); - if (cmpFunc(pivList[0], pivList[1], cookie) > 0) - SORT_R_SWAP(pivList[0], pivList[1], tmp); - } - - // Swap mid value (pivList[1]), and last element to put pivot as last - // element. - if (pivList[1] != last) - sort_r_swap(pivList[1], last, sizeOfElement); - - // v- end (beyond the array) - // EEEEEELLLLLLLLuuuuuuuuGGGGGGGEEEEEEEE. - // ^- base ^- ple ^- pl ^- pr ^- pre ^- last (where the pivot is) - - // Pivot comparison key: - // E = equal, L = less than, u = unknown, G = greater than, E = equal - pivot = last; - nextPivEqualsPos = nextPivCmpItem = base; - lastPivEqualsPos = lastPivCmpItem = last; - - // Strategy: - // Loop into the list from the left and right at the same time to find: - // - an item on the left that is greater than the pivot - // - an item on the right that is less than the pivot - // Once found, they are swapped and the loop continues. - // Meanwhile items that are equal to the pivot are moved to the edges - // of the array. - while (nextPivCmpItem < lastPivCmpItem) { - // Move left hand items which are equal to the pivot to the far - // left. Break when we find an item that is greater than the pivot. - for (; nextPivCmpItem < lastPivCmpItem; - nextPivCmpItem += sizeOfElement) { - cmp = cmpFunc(nextPivCmpItem, pivot, cookie); - if (cmp > 0) - break; - else if (cmp == 0) { - if (nextPivEqualsPos < nextPivCmpItem) { - sort_r_swap(nextPivEqualsPos, nextPivCmpItem, - sizeOfElement); - } - nextPivEqualsPos += sizeOfElement; - } - } - - // break if last batch of left hand items were equal to pivot - if (nextPivCmpItem >= lastPivCmpItem) - break; - - // Move right hand items which are equal to the pivot to the far - // right. Break when we find an item that is less than the pivot. - for (; nextPivCmpItem < lastPivCmpItem;) { - lastPivCmpItem -= sizeOfElement; - // Move right pointer onto an unprocessed item - cmp = cmpFunc(lastPivCmpItem, pivot, cookie); - if (cmp == 0) { - lastPivEqualsPos -= sizeOfElement; - if (lastPivCmpItem < lastPivEqualsPos) { - sort_r_swap(lastPivCmpItem, lastPivEqualsPos, - sizeOfElement); - } - } else if (cmp < 0) { - if (nextPivCmpItem < lastPivCmpItem) { - sort_r_swap(nextPivCmpItem, lastPivCmpItem, - sizeOfElement); - } - nextPivCmpItem += sizeOfElement; - break; - } - } - } - - nextPivCmpItem = lastPivCmpItem; - // lastPivCmpItem may have gone below nextPivCmpItem - - // Now we need to go from: EEELLLGGGGEEEE - // to: LLLEEEEEEEGGGG - - // Pivot comparison key: - // E = equal, L = less than, u = unknown, G = greater than, E = equal - sort_r_swap_blocks(base, nextPivEqualsPos - base, - nextPivCmpItem - nextPivEqualsPos); - sort_r_swap_blocks(lastPivCmpItem, lastPivEqualsPos - lastPivCmpItem, - end - lastPivEqualsPos); - - sort_r_simple(base, (nextPivCmpItem - nextPivEqualsPos) / sizeOfElement, - sizeOfElement, cmpFunc, cookie); - sort_r_simple(end - (lastPivEqualsPos - lastPivCmpItem), - (lastPivEqualsPos - lastPivCmpItem) / sizeOfElement, sizeOfElement, - cmpFunc, cookie); - } -} - - -inline void -qsort_r(void* base, size_t numElements, size_t sizeOfElement, - _compare_function_qsort_r cmpFunc, void* cookie) -{ - sort_r_simple((char*)base, numElements, sizeOfElement, - cmpFunc, cookie); -} diff --git a/src/system/libroot/posix/stdlib/Jamfile b/src/system/libroot/posix/stdlib/Jamfile index 77aa08c154..e9bdf7fe2d 100644 --- a/src/system/libroot/posix/stdlib/Jamfile +++ b/src/system/libroot/posix/stdlib/Jamfile @@ -26,6 +26,7 @@ for architectureObject in [ MultiArchSubDirSetup ] { mktemp.c pty.cpp qsort.c + qsort_r.c radixsort.c random.c reallocarray.cpp diff --git a/src/system/libroot/posix/stdlib/qsort.c b/src/system/libroot/posix/stdlib/qsort.c index 86e27cbbd0..53c1356b2b 100644 --- a/src/system/libroot/posix/stdlib/qsort.c +++ b/src/system/libroot/posix/stdlib/qsort.c @@ -29,16 +29,22 @@ * SUCH DAMAGE. */ -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ - #include #include #include #include +#define __unused + +#if defined(I_AM_QSORT_R) +typedef int cmp_t(const void *, const void *, void *); +#elif defined(I_AM_QSORT_R_COMPAT) +typedef int cmp_t(void *, const void *, const void *); +#elif defined(I_AM_QSORT_S) +typedef int cmp_t(const void *, const void *, void *); +#else typedef int cmp_t(const void *, const void *); +#endif static inline char *med3(char *, char *, char *, cmp_t *, void *); #define MIN(a, b) ((a) < (b) ? a : b) @@ -62,20 +68,39 @@ swapfunc(char *a, char *b, size_t es) #define vecswap(a, b, n) \ if ((n) > 0) swapfunc(a, b, n) +#if defined(I_AM_QSORT_R) +#define CMP(t, x, y) (cmp((x), (y), (t))) +#elif defined(I_AM_QSORT_R_COMPAT) +#define CMP(t, x, y) (cmp((t), (x), (y))) +#elif defined(I_AM_QSORT_S) +#define CMP(t, x, y) (cmp((x), (y), (t))) +#else #define CMP(t, x, y) (cmp((x), (y))) +#endif static inline char * -med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk) +med3(char *a, char *b, char *c, cmp_t *cmp, void *thunk +#if !defined(I_AM_QSORT_R) && !defined(I_AM_QSORT_R_COMPAT) && !defined(I_AM_QSORT_S) +__unused +#endif +) { 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 )); + (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 )); } /* * The actual qsort() implementation is static to avoid preemptible calls when * recursing. Also give them different names for improved debugging. */ +#if defined(I_AM_QSORT_R) +#define local_qsort local_qsort_r +#elif defined(I_AM_QSORT_R_COMPAT) +#define local_qsort local_qsort_r_compat +#elif defined(I_AM_QSORT_S) +#define local_qsort local_qsort_s +#endif static void local_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) { @@ -92,8 +117,8 @@ loop: 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) + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) swapfunc(pl, pl - es, es); return; } @@ -141,8 +166,8 @@ loop: 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) + pl > (char *)a && CMP(thunk, pl - es, pl) > 0; + pl -= es) swapfunc(pl, pl - es, es); return; } @@ -186,8 +211,52 @@ loop: } } +#if defined(I_AM_QSORT_R) +void +(qsort_r)(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) +{ + local_qsort_r(a, n, es, cmp, thunk); +} +#elif defined(I_AM_QSORT_R_COMPAT) +void +__qsort_r_compat(void *a, size_t n, size_t es, void *thunk, cmp_t *cmp) +{ + local_qsort_r_compat(a, n, es, cmp, thunk); +} +#elif defined(I_AM_QSORT_S) +errno_t +qsort_s(void *a, rsize_t n, rsize_t es, cmp_t *cmp, void *thunk) +{ + if (n > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : n > RSIZE_MAX", EINVAL); + return (EINVAL); + } else if (es > RSIZE_MAX) { + __throw_constraint_handler_s("qsort_s : es > RSIZE_MAX", + EINVAL); + return (EINVAL); + } else if (n != 0) { + if (a == NULL) { + __throw_constraint_handler_s("qsort_s : a == NULL", + EINVAL); + return (EINVAL); + } else if (cmp == NULL) { + __throw_constraint_handler_s("qsort_s : cmp == NULL", + EINVAL); + return (EINVAL); + } else if (es <= 0) { + __throw_constraint_handler_s("qsort_s : es <= 0", + EINVAL); + return (EINVAL); + } + } + + local_qsort_s(a, n, es, cmp, thunk); + return (0); +} +#else void qsort(void *a, size_t n, size_t es, cmp_t *cmp) { local_qsort(a, n, es, cmp, NULL); } +#endif diff --git a/src/system/libroot/posix/stdlib/qsort_r.c b/src/system/libroot/posix/stdlib/qsort_r.c new file mode 100644 index 0000000000..b382b40e91 --- /dev/null +++ b/src/system/libroot/posix/stdlib/qsort_r.c @@ -0,0 +1,6 @@ +/* + * This file is in the public domain. Originally written by Garrett + * A. Wollman. + */ +#define I_AM_QSORT_R +#include "qsort.c"