libroot/stdlib: Add qsort_r.
It's in POSIX-2024. Remove the libgnu implementation and just import the changes from latest FreeBSD (dc36d6f9bb1753f3) instead. While at it, put the non-standard sort functions behind _DEFAULT_SOURCE.
This commit is contained in:
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Isaac Turner, [email protected]
|
||||
* Jacob Secunda, [email protected]
|
||||
*/
|
||||
#ifndef _GNU_STDLIB_H_
|
||||
#define _GNU_STDLIB_H_
|
||||
|
||||
|
||||
#include_next <stdlib.h>
|
||||
#include <features.h>
|
||||
|
||||
|
||||
#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_ */
|
||||
@@ -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);
|
||||
|
||||
@@ -17,7 +17,6 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
||||
|
||||
SharedLibrary [ MultiArchDefaultGristFiles libgnu.so ] :
|
||||
crypt.cpp
|
||||
qsort.c
|
||||
sched_affinity.cpp
|
||||
sched_getcpu.cpp
|
||||
xattr.cpp
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Isaac Turner, [email protected]
|
||||
* Jacob Secunda, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -26,6 +26,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
||||
mktemp.c
|
||||
pty.cpp
|
||||
qsort.c
|
||||
qsort_r.c
|
||||
radixsort.c
|
||||
random.c
|
||||
reallocarray.cpp
|
||||
|
||||
@@ -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 <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -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"
|
||||
Reference in New Issue
Block a user