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:
Augustin Cavalier
2025-03-13 19:30:39 -04:00
parent 194f7cff83
commit 95eee47cdc
7 changed files with 95 additions and 261 deletions
-37
View File
@@ -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_ */
+8 -2
View File
@@ -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);
-1
View File
@@ -17,7 +17,6 @@ for architectureObject in [ MultiArchSubDirSetup ] {
SharedLibrary [ MultiArchDefaultGristFiles libgnu.so ] :
crypt.cpp
qsort.c
sched_affinity.cpp
sched_getcpu.cpp
xattr.cpp
-210
View File
@@ -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);
}
+1
View File
@@ -26,6 +26,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
mktemp.c
pty.cpp
qsort.c
qsort_r.c
radixsort.c
random.c
reallocarray.cpp
+80 -11
View File
@@ -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"