From 3804c55509f1fa1b04eaf17cea4b9cffa8a76554 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Sun, 2 Nov 2025 00:57:50 +0800 Subject: [PATCH 1/2] New Vector_sort function, replacing insertion and quick sort Introduce the new Vector_sort() function and obsolete the old Vector_quickSortCustomCompare() and Vector_insertionSort() APIs. This new sort function is a natural, in-place merge sort. I.e. it takes advantage of partially sorted data, and it's stable. Space complexity: O(log(n)) worst case (on stack, no malloc()) Time complexity: O(n) best case, O(n*(log(n))^2) average & worst case Signed-off-by: Kang-Che Sung --- Action.c | 2 +- EnvScreen.c | 4 +- OpenFilesScreen.c | 4 +- ProcessLocksScreen.c | 4 +- Table.c | 4 +- Vector.c | 207 ++++++++++++++++++++++++++++++------------- Vector.h | 7 +- 7 files changed, 155 insertions(+), 77 deletions(-) diff --git a/Action.c b/Action.c index 1d3bccc51..5a390b5c1 100644 --- a/Action.c +++ b/Action.c @@ -542,7 +542,7 @@ static Htop_Reaction actionFilterByUser(State* st) { Panel_setHeader(usersPanel, "Show processes of:"); Machine* host = st->host; UsersTable_foreach(host->usersTable, addUserToVector, usersPanel); - Vector_insertionSort(usersPanel->items); + Vector_sort(usersPanel->items, NULL); ListItem* allUsers = ListItem_new("All users", -1); Panel_insert(usersPanel, 0, (Object*) allUsers); const ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 19, false); diff --git a/EnvScreen.c b/EnvScreen.c index 4a36b318a..00530ff50 100644 --- a/EnvScreen.c +++ b/EnvScreen.c @@ -52,8 +52,8 @@ static void EnvScreen_scan(InfoScreen* this) { InfoScreen_addLine(this, "Could not read process environment."); } - Vector_insertionSort(this->lines); - Vector_insertionSort(panel->items); + Vector_sort(this->lines, NULL); + Vector_sort(panel->items, NULL); Panel_setSelected(panel, idx); } diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c index d04cce76e..c879bdaa9 100644 --- a/OpenFilesScreen.c +++ b/OpenFilesScreen.c @@ -300,8 +300,8 @@ static void OpenFilesScreen_scan(InfoScreen* super) { OpenFiles_Data_clear(&pdata->data); } free(pdata); - Vector_insertionSort(super->lines); - Vector_insertionSort(panel->items); + Vector_sort(super->lines, NULL); + Vector_sort(panel->items, NULL); Panel_setSelected(panel, idx); } diff --git a/ProcessLocksScreen.c b/ProcessLocksScreen.c index 36a37f927..550123397 100644 --- a/ProcessLocksScreen.c +++ b/ProcessLocksScreen.c @@ -91,8 +91,8 @@ static void ProcessLocksScreen_scan(InfoScreen* this) { } } free(pdata); - Vector_insertionSort(this->lines); - Vector_insertionSort(panel->items); + Vector_sort(this->lines, NULL); + Vector_sort(panel->items, NULL); Panel_setSelected(panel, idx); } diff --git a/Table.c b/Table.c index 9bedfc705..8fd095877 100644 --- a/Table.c +++ b/Table.c @@ -168,7 +168,7 @@ static void Table_buildTree(Table* this) { } // Sort by known parent (roots first), then row ID - Vector_quickSortCustomCompare(this->rows, compareRowByKnownParentThenNatural); + Vector_sort(this->rows, compareRowByKnownParentThenNatural); // Find all processes whose parent is not visible for (int i = 0; i < vsize; i++) { @@ -199,7 +199,7 @@ void Table_updateDisplayList(Table* this) { Table_buildTree(this); } else { if (this->needsSort) - Vector_insertionSort(this->rows); + Vector_sort(this->rows, NULL); Vector_prune(this->displayList); int size = Vector_size(this->rows); for (int i = 0; i < size; i++) diff --git a/Vector.c b/Vector.c index edb89dfd6..d7a3179e9 100644 --- a/Vector.c +++ b/Vector.c @@ -10,12 +10,19 @@ in the source distribution for its full text. #include "Vector.h" #include +#include #include #include #include "XUtils.h" +typedef int(*CompareWithContext)(const void*, const void*, void*); + +typedef struct VectorSortContext_ { + Object_Compare compare; +} VectorSortContext; + Vector* Vector_new(const ObjectClass* type, bool owner, int size) { Vector* this; @@ -93,91 +100,167 @@ void Vector_prune(Vector* this) { memset(this->array, '\0', this->arraySize * sizeof(Object*)); } -//static int comparisons = 0; - -static void swap(Object** array, int indexA, int indexB) { - assert(indexA >= 0); - assert(indexB >= 0); - Object* tmp = array[indexA]; - array[indexA] = array[indexB]; - array[indexB] = tmp; +ATTR_NONNULL +static void swapByte(char* p1, char* p2) { + char temp = *p1; + *p1 = *p2; + *p2 = temp; } -static int partition(Object** array, int left, int right, int pivotIndex, Object_Compare compare) { - const Object* pivotValue = array[pivotIndex]; - swap(array, pivotIndex, right); - int storeIndex = left; - for (int i = left; i < right; i++) { - //comparisons++; - if (compare(array[i], pivotValue) <= 0) { - swap(array, i, storeIndex); - storeIndex++; +ATTR_NONNULL +static void rotate(void* buffer, size_t leftSize, size_t rightSize) { + if (rightSize == 0) + return; + + char* p1 = buffer; + char* p2 = p1 + leftSize; + char* mid = p2; + const char* const end = mid + rightSize; + + while (true) { + // Ensure there is no arithmetic overflow on input. + assert(p1 <= mid); + assert(mid <= p2); + assert(p2 <= end); + + if (p2 >= end) { + assert(mid < end); + p2 = mid; } + + if (p1 >= p2) + break; + + if (p1 >= mid) + mid = p2; + + swapByte(p1, p2); + p1 += 1; + p2 += 1; } - swap(array, storeIndex, right); - return storeIndex; } -static void quickSort(Object** array, int left, int right, Object_Compare compare) { - if (left >= right) +ATTR_NONNULL_N(1, 5) +static void mergeRuns(void* array, size_t leftLen, size_t rightLen, size_t size, CompareWithContext compare, void* context) { + assert(size > 0); + if (leftLen == 0 || rightLen == 0 || size == 0) return; - int pivotIndex = left + (right - left) / 2; - int pivotNewIndex = partition(array, left, right, pivotIndex, compare); - quickSort(array, left, pivotNewIndex - 1, compare); - quickSort(array, pivotNewIndex + 1, right, compare); -} + assert(leftLen <= SIZE_MAX / size); + assert(rightLen <= SIZE_MAX / size); -// If I were to use only one sorting algorithm for both cases, it would probably be this one: -/* + char* p1 = array; + char* p2 = p1 + leftLen * size; + char* mid = p2; + const char* const end = mid + rightLen * size; -static void combSort(Object** array, int left, int right, Object_Compare compare) { - int gap = right - left; - bool swapped = true; - while ((gap > 1) || swapped) { - if (gap > 1) { - gap = (int)((double)gap / 1.247330950103979); - } - swapped = false; - for (int i = left; gap + i <= right; i++) { - comparisons++; - if (compare(array[i], array[i+gap]) > 0) { - swap(array, i, i+gap); - swapped = true; - } + for (size_t limit = (leftLen + rightLen) / 2; limit > 0; limit--) { + // Ensure there is no arithmetic overflow on input. + assert(p1 <= mid); + assert(mid <= p2); + assert(p2 <= end); + + if (p1 >= mid || p2 >= end) + break; + + if (compare(p1, p2, context) <= 0) { + p1 += size; + } else { + p2 += size; } } -} -*/ + rotate(p1, (size_t)(mid - p1), (size_t)(p2 - mid)); + + leftLen = (size_t)(p1 - (char*)array) / size; + rightLen = (size_t)(p2 - mid) / size; + mergeRuns(array, leftLen, rightLen, size, compare, context); -static void insertionSort(Object** array, int left, int right, Object_Compare compare) { - for (int i = left + 1; i <= right; i++) { - Object* t = array[i]; - int j = i - 1; - while (j >= left) { - //comparisons++; - if (compare(array[j], t) <= 0) + leftLen = (size_t)(mid - p1) / size; + rightLen = (size_t)(end - p2) / size; + mergeRuns(p1 + (p2 - mid), leftLen, rightLen, size, compare, context); +} + +ATTR_NONNULL_N(1, 5) +static size_t mergeSortSubarray(void* array, size_t unsortedLen, size_t limit, size_t size, CompareWithContext compare, void* context) { + assert(size > 0); + if (size == 0) + return 0; + + // The initial level of this function call must set "limit" to 0. Subsequent + // levels of recursion will have "limit" no less than the previous level. + + // A run is a sorted subarray. Each recursive call of this function keeps + // the lengths of two runs. At most O(log(n)) lengths of runs will be + // tracked on the call stack. + size_t runLen[3] = {0}; + while (unsortedLen > 0) { + size_t totalLen = unsortedLen; + assert(totalLen <= SIZE_MAX / size); + while (true) { + --unsortedLen; + + const char* p2 = (const char*)array + unsortedLen * size; + // Ensure there is no arithmetic overflow on input. + assert(p2 > (const char*)array); + + if (unsortedLen < limit) + return 0; + + if (unsortedLen == 0 || compare(p2 - 1 * size, p2, context) > 0) { break; + } + } + runLen[1] = totalLen - unsortedLen; + + bool reachesLimit = false; + + assert(runLen[2] > 0 || runLen[0] == 0); + if (runLen[2] > 0) { + size_t nextLimit = limit; + if (unsortedLen > runLen[2] + limit) { + nextLimit = unsortedLen - runLen[2]; + } else { + reachesLimit = true; + } + + runLen[0] = mergeSortSubarray(array, unsortedLen, nextLimit, size, compare, context); + unsortedLen -= runLen[0]; + + char* p1 = (char*)array + unsortedLen * size; + mergeRuns(p1, runLen[0], runLen[1], size, compare, context); + runLen[1] += runLen[0]; + runLen[0] = 0; - array[j + 1] = array[j]; - j--; + mergeRuns(p1, runLen[1], runLen[2], size, compare, context); + } + runLen[2] += runLen[1]; + runLen[1] = 0; + + if (reachesLimit) { + break; } - array[j + 1] = t; } + return runLen[2]; } -void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare) { - assert(compare); - assert(Vector_isConsistent(this)); - quickSort(this->array, 0, this->items - 1, compare); - assert(Vector_isConsistent(this)); +ATTR_NONNULL +static int Vector_sortCompare(const void* p1, const void* p2, void* context) { + VectorSortContext* vc = (VectorSortContext*) context; + + return vc->compare(*(const void* const*)p1, *(const void* const*)p2); } -void Vector_insertionSort(Vector* this) { - assert(this->type->compare); +ATTR_NONNULL_N(1) +void Vector_sort(Vector* this, Object_Compare compare) { + VectorSortContext vc = { + .compare = compare ? compare : this->type->compare, + }; + assert(vc.compare); assert(Vector_isConsistent(this)); - insertionSort(this->array, 0, this->items - 1, this->type->compare); + + (void)mergeSortSubarray(this->array, this->items, 0, sizeof(*this->array), Vector_sortCompare, &vc); + assert(Vector_isConsistent(this)); } diff --git a/Vector.h b/Vector.h index 95617eda9..d52fb3d41 100644 --- a/Vector.h +++ b/Vector.h @@ -35,12 +35,7 @@ void Vector_delete(Vector* this); void Vector_prune(Vector* this); -void Vector_quickSortCustomCompare(Vector* this, Object_Compare compare); -static inline void Vector_quickSort(Vector* this) { - Vector_quickSortCustomCompare(this, this->type->compare); -} - -void Vector_insertionSort(Vector* this); +void Vector_sort(Vector* this, Object_Compare compare); void Vector_insert(Vector* this, int idx, void* data_); From f29067d5085691a07098fe0f6601681fee248169 Mon Sep 17 00:00:00 2001 From: Explorer09 Date: Sun, 2 Nov 2025 01:48:21 +0800 Subject: [PATCH 2/2] Make Object_Compare functions accept third argument Update the prototypes of all Object_Compare functions and Vector_sort() function to accept the third argument. The third argument allows passing in extra information or states that could be useful in sorting. The definition of Object_Compare now matches the prototype of the compare function in libc qsort_r(). (Previously many programmers would use global variables to read or store extra information needed in a compare operation. This made the sort function not reentrant. It was the primary motivation for the qsort_r() function.) Currently all Object_Compare functions in htop would simply the third argument. No functions are using it yet, but this may be changed in the future. The callers of Vector_sort() will now by default pass in the reference of the container object (usually named "this") as the third argument to the sort compare functions. Signed-off-by: Kang-Che Sung --- Action.c | 2 +- EnvScreen.c | 4 ++-- ListItem.c | 3 ++- ListItem.h | 2 +- Object.h | 4 ++-- OpenFilesScreen.c | 4 ++-- Process.c | 7 ++++--- Process.h | 8 ++++---- ProcessLocksScreen.c | 4 ++-- Row.c | 7 ++++--- Row.h | 11 ++++++----- Table.c | 8 ++++---- Vector.c | 14 +++++++------- Vector.h | 2 +- pcp/Instance.c | 3 ++- 15 files changed, 44 insertions(+), 39 deletions(-) diff --git a/Action.c b/Action.c index 5a390b5c1..3f75bb46c 100644 --- a/Action.c +++ b/Action.c @@ -542,7 +542,7 @@ static Htop_Reaction actionFilterByUser(State* st) { Panel_setHeader(usersPanel, "Show processes of:"); Machine* host = st->host; UsersTable_foreach(host->usersTable, addUserToVector, usersPanel); - Vector_sort(usersPanel->items, NULL); + Vector_sort(usersPanel->items, NULL, usersPanel); ListItem* allUsers = ListItem_new("All users", -1); Panel_insert(usersPanel, 0, (Object*) allUsers); const ListItem* picked = (ListItem*) Action_pickFromVector(st, usersPanel, 19, false); diff --git a/EnvScreen.c b/EnvScreen.c index 00530ff50..0fb8fa106 100644 --- a/EnvScreen.c +++ b/EnvScreen.c @@ -52,8 +52,8 @@ static void EnvScreen_scan(InfoScreen* this) { InfoScreen_addLine(this, "Could not read process environment."); } - Vector_sort(this->lines, NULL); - Vector_sort(panel->items, NULL); + Vector_sort(this->lines, NULL, this); + Vector_sort(panel->items, NULL, this); Panel_setSelected(panel, idx); } diff --git a/ListItem.c b/ListItem.c index 246bc5d0d..ae85d0836 100644 --- a/ListItem.c +++ b/ListItem.c @@ -59,7 +59,8 @@ void ListItem_append(ListItem* this, const char* text) { this->value[newLen] = '\0'; } -int ListItem_compare(const void* cast1, const void* cast2) { +int ListItem_compare(const void* cast1, const void* cast2, void* context) { + (void)context; const ListItem* obj1 = (const ListItem*) cast1; const ListItem* obj2 = (const ListItem*) cast2; return strcmp(obj1->value, obj2->value); diff --git a/ListItem.h b/ListItem.h index 5efe8743b..19e939c27 100644 --- a/ListItem.h +++ b/ListItem.h @@ -32,7 +32,7 @@ ListItem* ListItem_new(const char* value, int key); void ListItem_append(ListItem* this, const char* text); -int ListItem_compare(const void* cast1, const void* cast2); +int ListItem_compare(const void* cast1, const void* cast2, void* context); static inline const char* ListItem_getRef(const ListItem* this) { return this->value; diff --git a/Object.h b/Object.h index 4d7d653ee..73734f1e6 100644 --- a/Object.h +++ b/Object.h @@ -19,7 +19,7 @@ struct Object_; typedef struct Object_ Object; typedef void(*Object_Display)(const Object*, RichString*); -typedef int(*Object_Compare)(const void*, const void*); +typedef int(*Object_Compare)(const void*, const void*, void*); typedef void(*Object_Delete)(Object*); #define Object_getClass(obj_) ((const Object*)(obj_))->klass @@ -28,7 +28,7 @@ typedef void(*Object_Delete)(Object*); #define Object_delete(obj_) (assert(Object_getClass(obj_)->delete), Object_getClass(obj_)->delete((Object*)(obj_))) #define Object_displayFn(obj_) Object_getClass(obj_)->display #define Object_display(obj_, str_) (assert(Object_getClass(obj_)->display), Object_getClass(obj_)->display((const Object*)(obj_), str_)) -#define Object_compare(obj_, other_) (assert(Object_getClass(obj_)->compare), Object_getClass(obj_)->compare((const void*)(obj_), other_)) +#define Object_compare(obj_, other_, ctx_) (assert(Object_getClass(obj_)->compare), Object_getClass(obj_)->compare((const void*)(obj_), other_, ctx_) #define Class(class_) ((const ObjectClass*)(&(class_ ## _class))) diff --git a/OpenFilesScreen.c b/OpenFilesScreen.c index c879bdaa9..d8a1457ec 100644 --- a/OpenFilesScreen.c +++ b/OpenFilesScreen.c @@ -300,8 +300,8 @@ static void OpenFilesScreen_scan(InfoScreen* super) { OpenFiles_Data_clear(&pdata->data); } free(pdata); - Vector_sort(super->lines, NULL); - Vector_sort(panel->items, NULL); + Vector_sort(super->lines, NULL, super); + Vector_sort(panel->items, NULL, super); Panel_setSelected(panel, idx); } diff --git a/Process.c b/Process.c index cd1c08688..437f82825 100644 --- a/Process.c +++ b/Process.c @@ -911,7 +911,8 @@ bool Process_rowSendSignal(Row* super, Arg sgn) { return Process_sendSignal(this, sgn); } -int Process_compare(const void* v1, const void* v2) { +int Process_compare(const void* v1, const void* v2, void* context) { + (void)context; const Process* p1 = (const Process*)v1; const Process* p2 = (const Process*)v2; @@ -928,7 +929,7 @@ int Process_compare(const void* v1, const void* v2) { return (ScreenSettings_getActiveDirection(ss) == 1) ? result : -result; } -int Process_compareByParent(const Row* r1, const Row* r2) { +int Process_compareByParent(const Row* r1, const Row* r2, void* context) { int result = SPACESHIP_NUMBER( r1->isRoot ? 0 : Row_getGroupOrParent(r1), r2->isRoot ? 0 : Row_getGroupOrParent(r2) @@ -937,7 +938,7 @@ int Process_compareByParent(const Row* r1, const Row* r2) { if (result != 0) return result; - return Process_compare(r1, r2); + return Process_compare(r1, r2, context); } int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key) { diff --git a/Process.h b/Process.h index 38e2711f6..da77acfa3 100644 --- a/Process.h +++ b/Process.h @@ -231,8 +231,8 @@ typedef int32_t ProcessField; /* see ReservedField list in RowField.h */ // Implemented in platform-specific code: void Process_writeField(const Process* this, RichString* str, ProcessField field); -int Process_compare(const void* v1, const void* v2); -int Process_compareByParent(const Row* r1, const Row* r2); +int Process_compare(const void* v1, const void* v2, void* context); +int Process_compareByParent(const Row* r1, const Row* r2, void* context); void Process_delete(Object* cast); extern const ProcessFieldData Process_fields[LAST_PROCESSFIELD]; #define Process_pidDigits Row_pidDigits @@ -317,8 +317,8 @@ bool Process_rowIsVisible(const Row* super, const struct Table_* table); bool Process_rowMatchesFilter(const Row* super, const struct Table_* table); -static inline int Process_pidEqualCompare(const void* v1, const void* v2) { - return Row_idEqualCompare(v1, v2); +static inline int Process_pidEqualCompare(const void* v1, const void* v2, void* context) { + return Row_idEqualCompare(v1, v2, context); } int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField key); diff --git a/ProcessLocksScreen.c b/ProcessLocksScreen.c index 550123397..3a8ec067d 100644 --- a/ProcessLocksScreen.c +++ b/ProcessLocksScreen.c @@ -91,8 +91,8 @@ static void ProcessLocksScreen_scan(InfoScreen* this) { } } free(pdata); - Vector_sort(this->lines, NULL); - Vector_sort(panel->items, NULL); + Vector_sort(this->lines, NULL, this); + Vector_sort(panel->items, NULL, this); Panel_setSelected(panel, idx); } diff --git a/Row.c b/Row.c index 13e79e648..21c883bd2 100644 --- a/Row.c +++ b/Row.c @@ -530,14 +530,15 @@ void Row_toggleTag(Row* this) { this->tag = !this->tag; } -int Row_compare(const void* v1, const void* v2) { +int Row_compare(const void* v1, const void* v2, void* context) { + (void)context; const Row* r1 = (const Row*)v1; const Row* r2 = (const Row*)v2; return SPACESHIP_NUMBER(r1->id, r2->id); } -int Row_compareByParent_Base(const void* v1, const void* v2) { +int Row_compareByParent_Base(const void* v1, const void* v2, void* context) { const Row* r1 = (const Row*)v1; const Row* r2 = (const Row*)v2; @@ -549,7 +550,7 @@ int Row_compareByParent_Base(const void* v1, const void* v2) { if (result != 0) return result; - return Row_compare(v1, v2); + return Row_compare(v1, v2, context); } const RowClass Row_class = { diff --git a/Row.h b/Row.h index 78629a3a5..bd58d0322 100644 --- a/Row.h +++ b/Row.h @@ -82,9 +82,9 @@ typedef bool (*Row_IsHighlighted)(const Row*); typedef bool (*Row_IsVisible)(const Row*, const struct Table_*); typedef bool (*Row_MatchesFilter)(const Row*, const struct Table_*); typedef const char* (*Row_SortKeyString)(Row*); -typedef int (*Row_CompareByParent)(const Row*, const Row*); +typedef int (*Row_CompareByParent)(const Row*, const Row*, void*); -int Row_compare(const void* v1, const void* v2); +int Row_compare(const void* v1, const void* v2, void* context); typedef struct RowClass_ { const ObjectClass super; @@ -102,7 +102,7 @@ typedef struct RowClass_ { #define Row_isVisible(r_, t_) (As_Row(r_)->isVisible ? (As_Row(r_)->isVisible(r_, t_)) : true) #define Row_matchesFilter(r_, t_) (As_Row(r_)->matchesFilter ? (As_Row(r_)->matchesFilter(r_, t_)) : false) #define Row_sortKeyString(r_) (As_Row(r_)->sortKeyString ? (As_Row(r_)->sortKeyString(r_)) : "") -#define Row_compareByParent(r1_, r2_) (As_Row(r1_)->compareByParent ? (As_Row(r1_)->compareByParent(r1_, r2_)) : Row_compareByParent_Base(r1_, r2_)) +#define Row_compareByParent(r1_, r2_, ctx_) (As_Row(r1_)->compareByParent ? (As_Row(r1_)->compareByParent(r1_, r2_, ctx_)) : Row_compareByParent_Base(r1_, r2_, ctx_)) #define ONE_K 1024UL #define ONE_M (ONE_K * ONE_K) @@ -162,7 +162,8 @@ void Row_printRate(RichString* str, double rate, bool coloring); int Row_printPercentage(float val, char* buffer, size_t n, uint8_t width, int* attr); -static inline int Row_idEqualCompare(const void* v1, const void* v2) { +static inline int Row_idEqualCompare(const void* v1, const void* v2, void* context) { + (void)context; const int p1 = ((const Row*)v1)->id; const int p2 = ((const Row*)v2)->id; return p1 != p2; /* return zero when equal */ @@ -177,6 +178,6 @@ static inline bool Row_isChildOf(const Row* this, int id) { return id == Row_getGroupOrParent(this); } -int Row_compareByParent_Base(const void* v1, const void* v2); +int Row_compareByParent_Base(const void* v1, const void* v2, void* context); #endif diff --git a/Table.c b/Table.c index 8fd095877..533c3aa4d 100644 --- a/Table.c +++ b/Table.c @@ -137,8 +137,8 @@ static void Table_buildTreeBranch(Table* this, int rowid, unsigned int level, in } } -static int compareRowByKnownParentThenNatural(const void* v1, const void* v2) { - return Row_compareByParent((const Row*) v1, (const Row*) v2); +static int compareRowByKnownParentThenNatural(const void* v1, const void* v2, void* context) { + return Row_compareByParent((const Row*) v1, (const Row*) v2, context); } // Builds a sorted tree from scratch, without relying on previously gathered information @@ -168,7 +168,7 @@ static void Table_buildTree(Table* this) { } // Sort by known parent (roots first), then row ID - Vector_sort(this->rows, compareRowByKnownParentThenNatural); + Vector_sort(this->rows, compareRowByKnownParentThenNatural, this); // Find all processes whose parent is not visible for (int i = 0; i < vsize; i++) { @@ -199,7 +199,7 @@ void Table_updateDisplayList(Table* this) { Table_buildTree(this); } else { if (this->needsSort) - Vector_sort(this->rows, NULL); + Vector_sort(this->rows, NULL, this); Vector_prune(this->displayList); int size = Vector_size(this->rows); for (int i = 0; i < size; i++) diff --git a/Vector.c b/Vector.c index d7a3179e9..44c4e7a65 100644 --- a/Vector.c +++ b/Vector.c @@ -17,10 +17,9 @@ in the source distribution for its full text. #include "XUtils.h" -typedef int(*CompareWithContext)(const void*, const void*, void*); - typedef struct VectorSortContext_ { Object_Compare compare; + void* compareContext; } VectorSortContext; Vector* Vector_new(const ObjectClass* type, bool owner, int size) { @@ -141,7 +140,7 @@ static void rotate(void* buffer, size_t leftSize, size_t rightSize) { } ATTR_NONNULL_N(1, 5) -static void mergeRuns(void* array, size_t leftLen, size_t rightLen, size_t size, CompareWithContext compare, void* context) { +static void mergeRuns(void* array, size_t leftLen, size_t rightLen, size_t size, Object_Compare compare, void* context) { assert(size > 0); if (leftLen == 0 || rightLen == 0 || size == 0) return; @@ -182,7 +181,7 @@ static void mergeRuns(void* array, size_t leftLen, size_t rightLen, size_t size, } ATTR_NONNULL_N(1, 5) -static size_t mergeSortSubarray(void* array, size_t unsortedLen, size_t limit, size_t size, CompareWithContext compare, void* context) { +static size_t mergeSortSubarray(void* array, size_t unsortedLen, size_t limit, size_t size, Object_Compare compare, void* context) { assert(size > 0); if (size == 0) return 0; @@ -248,13 +247,14 @@ ATTR_NONNULL static int Vector_sortCompare(const void* p1, const void* p2, void* context) { VectorSortContext* vc = (VectorSortContext*) context; - return vc->compare(*(const void* const*)p1, *(const void* const*)p2); + return vc->compare(*(const void* const*)p1, *(const void* const*)p2, vc->compareContext); } ATTR_NONNULL_N(1) -void Vector_sort(Vector* this, Object_Compare compare) { +void Vector_sort(Vector* this, Object_Compare compare, void* context) { VectorSortContext vc = { .compare = compare ? compare : this->type->compare, + .compareContext = context ? context : this, }; assert(vc.compare); assert(Vector_isConsistent(this)); @@ -440,7 +440,7 @@ int Vector_indexOf(const Vector* this, const void* search_, Object_Compare compa for (int i = 0; i < this->items; i++) { const Object* o = this->array[i]; assert(o); - if (compare(search, o) == 0) { + if (compare(search, o, NULL) == 0) { return i; } } diff --git a/Vector.h b/Vector.h index d52fb3d41..14da71767 100644 --- a/Vector.h +++ b/Vector.h @@ -35,7 +35,7 @@ void Vector_delete(Vector* this); void Vector_prune(Vector* this); -void Vector_sort(Vector* this, Object_Compare compare); +void Vector_sort(Vector* this, Object_Compare compare, void* context); void Vector_insert(Vector* this, int idx, void* data_); diff --git a/pcp/Instance.c b/pcp/Instance.c index 8ae90512f..a0ea3cf0a 100644 --- a/pcp/Instance.c +++ b/pcp/Instance.c @@ -137,7 +137,8 @@ static int Instance_compareByKey(const Row* v1, const Row* v2, int key) { return 0; } -static int Instance_compare(const void* v1, const void* v2) { +static int Instance_compare(const void* v1, const void* v2, void* context) { + (void)context; const Instance* i1 = (const Instance*)v1; const Instance* i2 = (const Instance*)v2; const ScreenSettings* ss = i1->super.host->settings->ss;