From e801ca642e6c8103983e8a8be6097056d8916e3d Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Sat, 2 Mar 2024 21:31:49 +0900 Subject: [PATCH] Implement list.sort with a 3-way quicksort This is a temporary measure until a better (stable) sort is implemented, like Timsort or powersort. --- src/obj_list.c | 69 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/src/obj_list.c b/src/obj_list.c index 65fc744..3b3e36e 100644 --- a/src/obj_list.c +++ b/src/obj_list.c @@ -428,12 +428,7 @@ KRK_Method(list,reverse) { return NONE_VAL(); } -static int _list_sorter(const void * _a, const void * _b) { - KrkValue a = *(KrkValue*)_a; - KrkValue b = *(KrkValue*)_b; - - /* Avoid actually calling the sort function if there's an active exception */ - if (unlikely(krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION)) return -1; +static int _list_sorter(KrkValue a, KrkValue b) { KrkValue ltComp = krk_operator_lt(a,b); if (IS_NONE(ltComp) || (IS_BOOLEAN(ltComp) && AS_BOOLEAN(ltComp))) return -1; KrkValue gtComp = krk_operator_gt(a,b); @@ -441,11 +436,69 @@ static int _list_sorter(const void * _a, const void * _b) { return 0; } +static void list_swap(KrkList *list, size_t i, size_t j) { + krk_push(list->values.values[i]); + list->values.values[i] = list->values.values[j]; + list->values.values[j] = krk_pop(); +} + +static int partition(KrkList *list, KrkValue key, int reverse, ssize_t lo, ssize_t hi, ssize_t *lt, ssize_t *gt) { + /* Create key from pivot */ + if (!IS_NONE(key)) krk_push(key); + krk_push(list->values.values[(lo+hi)/2]); + if (!IS_NONE(key)) krk_push(krk_callStack(1)); + + ssize_t _lt = lo; + ssize_t _eq = lo; + ssize_t _gt = hi; + + while (_eq <= _gt) { + if (!IS_NONE(key)) krk_push(key); + krk_push(list->values.values[_eq]); + if (!IS_NONE(key)) krk_push(krk_callStack(1)); + + int res = _list_sorter(krk_peek(reverse),krk_peek(1-reverse)); + if (krk_currentThread.flags & KRK_THREAD_HAS_EXCEPTION) return 1; + + if (res < 0) { + list_swap(list,_eq,_lt); + _lt++; + _eq++; + } else if (res > 0) { + list_swap(list,_eq,_gt); + _gt--; + } else { + _eq++; + } + + krk_pop(); + } + + krk_pop(); /* Pop pivot key. */ + + *lt = _lt; + *gt = _gt; + return 0; +} + +static void quicksort(KrkList * list, KrkValue key, int reverse, ssize_t lo, ssize_t hi) { + if (lo >= 0 && lo < hi) { + ssize_t lt, gt; + if (partition(list, key, reverse, lo, hi, <, >)) return; + quicksort(list, key, reverse, lo, lt - 1); + quicksort(list, key, reverse, gt + 1, hi); + } +} + KRK_Method(list,sort) { - METHOD_TAKES_NONE(); + KrkValue key = NONE_VAL(); + int reverse = 0; + if (!krk_parseArgs(".|$Vp", (const char*[]){"key","reverse"}, &key, &reverse)) return NONE_VAL(); + + if (self->values.count < 2) return NONE_VAL(); pthread_rwlock_wrlock(&self->rwlock); - qsort(self->values.values, self->values.count, sizeof(KrkValue), _list_sorter); + quicksort(self, key, reverse, 0, self->values.count - 1); pthread_rwlock_unlock(&self->rwlock); return NONE_VAL();