From 1025248cc7a67e9cf86637f2d2c31b96ad5adec7 Mon Sep 17 00:00:00 2001 From: apio Date: Sun, 6 Nov 2022 17:16:08 +0100 Subject: [PATCH] libc: Add qsort (with a test!!) --- libs/libc/include/stdlib.h | 3 ++- libs/libc/src/stdlib.cpp | 49 ++++++++++++++++++++++++++++++++++++-- tests/libc/Test.c | 2 ++ tests/libc/stdlib.c | 45 +++++++++++++++++++++++++++++++++- 4 files changed, 95 insertions(+), 4 deletions(-) diff --git a/libs/libc/include/stdlib.h b/libs/libc/include/stdlib.h index 5a67e0c6..17088d2c 100644 --- a/libs/libc/include/stdlib.h +++ b/libs/libc/include/stdlib.h @@ -113,7 +113,8 @@ extern "C" /* Runs a shell command. */ int system(const char* command); - void qsort(void*, size_t, size_t, int (*)(const void*, const void*)); // Not implemented. + /* Sorts the array pointed to by base of nmemb items of the specified size using the comparison function compar. */ + void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)); void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*)); // Not implemented. diff --git a/libs/libc/src/stdlib.cpp b/libs/libc/src/stdlib.cpp index 4a1ac2f5..ffcf60c3 100644 --- a/libs/libc/src/stdlib.cpp +++ b/libs/libc/src/stdlib.cpp @@ -77,6 +77,51 @@ template static inline Struct common_div(Arg a, return result; } +static void qswap(void* ptr1, void* ptr2, size_t size) +{ + char* x = (char*)ptr1; + char* y = (char*)ptr2; + + while (size--) + { + char t = *x; + *x = *y; + *y = t; + x += 1; + y += 1; + } +} + +static size_t partition(void* base, size_t start, size_t end, size_t size, int (*compar)(const void*, const void*)) +{ + auto atindex = [&base, &size](size_t index) { return (void*)((char*)base + (index * size)); }; + + void* pivot = atindex(end); + size_t i = (start - 1); + + for (size_t j = start; j <= end - 1; j++) + { + if (compar(atindex(j), pivot) < 0) + { + i++; + qswap(atindex(i), atindex(j), size); + } + } + + qswap(atindex(i + 1), pivot, size); + return i + 1; +} + +static void quicksort(void* base, size_t start, size_t end, size_t size, int (*compar)(const void*, const void*)) +{ + if (start < end) + { + size_t pivot = partition(base, start, end, size, compar); + quicksort(base, start, pivot - 1, size, compar); + quicksort(base, pivot + 1, end, size, compar); + } +} + extern "C" { __lc_noreturn void abort() @@ -175,9 +220,9 @@ extern "C" return status; } - void qsort(void*, size_t, size_t, int (*)(const void*, const void*)) + void qsort(void* base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)) { - NOT_IMPLEMENTED("qsort"); + quicksort(base, 0, nmemb - 1, size, compar); } void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*)) diff --git a/tests/libc/Test.c b/tests/libc/Test.c index 5e188ef5..be0b132d 100644 --- a/tests/libc/Test.c +++ b/tests/libc/Test.c @@ -25,6 +25,7 @@ DEFINE_TEST(srand); DEFINE_TEST(malloc); DEFINE_TEST(calloc); DEFINE_TEST(mktemp); +DEFINE_TEST(qsort); int main() { @@ -53,4 +54,5 @@ int main() RUN_TEST(malloc); RUN_TEST(calloc); RUN_TEST(mktemp); + RUN_TEST(qsort); } \ No newline at end of file diff --git a/tests/libc/stdlib.c b/tests/libc/stdlib.c index 0e754810..ed9a5e26 100644 --- a/tests/libc/stdlib.c +++ b/tests/libc/stdlib.c @@ -155,12 +155,55 @@ DEFINE_TEST(mktemp) int rc = access(ptr, F_OK); - EXPECT_NOT_EQ(rc, 0); // FIXME: This could actually happen, since that's why mktemp is deprecated. + EXPECT_NOT_EQ(rc, 0); // FIXME: This could actually happen, since that's why mktemp is deprecated. // Another process could create the file between generating the name and actually using it. rc = strcmp(ptr, template2); EXPECT_NOT_EQ(rc, 0); + TEST_SUCCESS(); +} + +static int compare_char(const void* p1, const void* p2) +{ + const char* c1 = (const char*)p1; + const char* c2 = (const char*)p2; + if (*c1 < *c2) return -1; + if (*c1 > *c2) return 1; + return 0; +} + +static int compare_int(const void* p1, const void* p2) +{ + const int* c1 = (const int*)p1; + const int* c2 = (const int*)p2; + if (*c1 < *c2) return -1; + if (*c1 > *c2) return 1; + return 0; +} + +DEFINE_TEST(qsort) +{ + START_TEST(qsort); + + char unsorted[] = "elacbkdjf"; + const char sorted[] = "abcdefjkl"; + + qsort(unsorted, 9, sizeof(char), compare_char); + + int rc = memcmp(sorted, unsorted, 9); + + EXPECT_EQ(rc, 0); + + int unsorted2[] = {1500, 45, 3, 11, 41, 90, 6, 32, 5, 76}; + const int sorted2[] = {3, 5, 6, 11, 32, 41, 45, 76, 90, 1500}; + + qsort(unsorted2, 10, sizeof(int), compare_int); + + rc = memcmp(unsorted2, sorted2, 10); + + EXPECT_EQ(rc, 0); + TEST_SUCCESS(); } \ No newline at end of file