GNU Compatibility: Implement qsort_r
This is a version of the qsort function allowing you to pass a "cookie" to the function. Change-Id: I60c645213b9c9590e38b112634fcac1d7969b6d9 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2449 Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
This commit is contained in:
parent
1c889d23fe
commit
5c32512076
36
headers/compatibility/gnu/stdlib.h
Normal file
36
headers/compatibility/gnu/stdlib.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 <stdlib.h>
|
||||
|
||||
|
||||
#ifdef _GNU_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 /* _GNU_SOURCE */
|
||||
|
||||
|
||||
#endif /* _GNU_STDLIB_H_ */
|
@ -11,6 +11,7 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
||||
on $(architectureObject) {
|
||||
SharedLibrary [ MultiArchDefaultGristFiles libgnu.so ] :
|
||||
memmem.c
|
||||
qsort.c
|
||||
xattr.cpp
|
||||
;
|
||||
}
|
||||
|
210
src/libs/gnu/qsort.c
Normal file
210
src/libs/gnu/qsort.c
Normal file
@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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 <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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user