From: Michael Tremer Date: Sat, 11 Jan 2025 17:28:30 +0000 (+0000) Subject: packagelist: Refactor using binary search X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=84547dfcf5d8313757d6368d84d7f625579a1bc2;p=people%2Fric9%2Fpakfire.git packagelist: Refactor using binary search This will keep the list sorted at all times and inserts will be a lot faster than using the iteration over the linked lists. Signed-off-by: Michael Tremer --- diff --git a/src/pakfire/packagelist.c b/src/pakfire/packagelist.c index 3865c30a8..b8a80833d 100644 --- a/src/pakfire/packagelist.c +++ b/src/pakfire/packagelist.c @@ -18,8 +18,8 @@ # # #############################################################################*/ +#include #include -#include #include @@ -29,147 +29,144 @@ #include #include -struct pakfire_packagelist_element { - TAILQ_ENTRY(pakfire_packagelist_element) nodes; - - struct pakfire_package* pkg; -}; - struct pakfire_packagelist { struct pakfire_ctx* ctx; int nrefs; - TAILQ_HEAD(entries, pakfire_packagelist_element) packages; + struct pakfire_package** packages; + unsigned int num_packages; }; -static void pakfire_packagelist_clear(struct pakfire_packagelist* list) { - struct pakfire_packagelist_element* element = NULL; - - while (!TAILQ_EMPTY(&list->packages)) { - // Fetch the first element - element = TAILQ_FIRST(&list->packages); - - // Remove it from the list - TAILQ_REMOVE(&list->packages, element, nodes); +static void pakfire_packagelist_clear(struct pakfire_packagelist* self) { + if (self->packages) { + for (unsigned int i = 0; i < self->num_packages; i++) + pakfire_package_unref(self->packages[i]); - // Dereference the file - pakfire_package_unref(element->pkg); + // Free the array + free(self->packages); + self->packages = NULL; - // Free it all - free(element); + // Reset number of files on the list + self->num_packages = 0; } } static void pakfire_packagelist_free(struct pakfire_packagelist* list) { pakfire_packagelist_clear(list); + if (list->ctx) pakfire_ctx_unref(list->ctx); free(list); } -int pakfire_packagelist_create( - struct pakfire_packagelist** list, struct pakfire_ctx* ctx) { - struct pakfire_packagelist* l = calloc(1, sizeof(*l)); - if (!l) - return 1; +int pakfire_packagelist_create(struct pakfire_packagelist** list, struct pakfire_ctx* ctx) { + struct pakfire_packagelist* self = NULL; + + // Allocate some memory + self = calloc(1, sizeof(*self)); + if (!self) + return -errno; // Store a reference to the context - l->ctx = pakfire_ctx_ref(ctx); + self->ctx = pakfire_ctx_ref(ctx); // Initialize the reference counter - l->nrefs = 1; + self->nrefs = 1; - // Initialise packages - TAILQ_INIT(&l->packages); + // Return the pointer + *list = self; - *list = l; return 0; } -struct pakfire_packagelist* pakfire_packagelist_ref(struct pakfire_packagelist* list) { - list->nrefs++; +struct pakfire_packagelist* pakfire_packagelist_ref(struct pakfire_packagelist* self) { + self->nrefs++; - return list; + return self; } -struct pakfire_packagelist* pakfire_packagelist_unref(struct pakfire_packagelist* list) { - if (--list->nrefs > 0) - return list; +struct pakfire_packagelist* pakfire_packagelist_unref(struct pakfire_packagelist* self) { + if (--self->nrefs > 0) + return self; - pakfire_packagelist_free(list); + pakfire_packagelist_free(self); return NULL; } -size_t pakfire_packagelist_length(struct pakfire_packagelist* list) { - struct pakfire_packagelist_element* element = NULL; - size_t size = 0; +size_t pakfire_packagelist_length(struct pakfire_packagelist* self) { + return self->num_packages; +} - TAILQ_FOREACH(element, &list->packages, nodes) - size++; +struct pakfire_package* pakfire_packagelist_get(struct pakfire_packagelist* self, unsigned int index) { + // Check that index is in range + if (index >= self->num_packages) { + errno = ERANGE; + return NULL; + } - return size; + return pakfire_package_ref(self->packages[index]); } -struct pakfire_package* pakfire_packagelist_get(struct pakfire_packagelist* list, unsigned int index) { - struct pakfire_packagelist_element* element = NULL; +static int pakfire_packagelist_search(struct pakfire_packagelist* self, struct pakfire_package* pkg) { + int i; + int r; - // Fetch the first element - element = TAILQ_FIRST(&list->packages); + // Set starting points + int lo = 0; + int hi = self->num_packages; - while (element && index--) - element = TAILQ_NEXT(element, nodes); + while (lo < hi) { + // Find the middle + i = (lo + hi) / 2; - if (!element) - return NULL; - - return pakfire_package_ref(element->pkg); -} + // Compare the packages + r = pakfire_package_cmp(self->packages[i], pkg); -int pakfire_packagelist_push(struct pakfire_packagelist* list, struct pakfire_package* pkg) { - struct pakfire_packagelist_element* element = NULL; - struct pakfire_packagelist_element* e = NULL; + // If the file is greater than the middle, we only need to search in the left half + if (r >= 0) + hi = i; - // Allocate a new element - element = calloc(1, sizeof *element); - if (!element) { - ERROR(list->ctx, "Could not allocate a new packagelist element: %m\n"); - return 1; + // Otherwise we need to search in the right half + else + lo = i + 1; } - // Reference the package - element->pkg = pakfire_package_ref(pkg); - - // Fetch the last element - e = TAILQ_LAST(&list->packages, entries); + return lo; +} - // Skip all elements that are "greater than" the element we want to add - while (e) { - if (pakfire_package_cmp(e->pkg, pkg) <= 0) - break; +int pakfire_packagelist_push(struct pakfire_packagelist* self, struct pakfire_package* pkg) { + // Find the index where the package should go + int pos = pakfire_packagelist_search(self, pkg); - e = TAILQ_PREV(e, entries, nodes); + // Make space + self->packages = reallocarray(self->packages, self->num_packages + 1, sizeof(*self->packages)); + if (!self->packages) { + ERROR(self->ctx, "Could not allocate packagelist: %m\n"); + return -errno; } - // If we found an element on the list, we will append after it, - // otherwise we add right at the start. - if (e) - TAILQ_INSERT_AFTER(&list->packages, e, element, nodes); - else - TAILQ_INSERT_HEAD(&list->packages, element, nodes); + // Move everything up one slot + for (int i = self->num_packages; i > pos; i--) + self->packages[i] = self->packages[i - 1]; + + // Store the package + self->packages[pos] = pakfire_package_ref(pkg); + + // The list is now longer + ++self->num_packages; return 0; } -int pakfire_packagelist_walk(struct pakfire_packagelist* list, +int pakfire_packagelist_walk(struct pakfire_packagelist* self, pakfire_packagelist_walk_callback callback, void* data, int flags) { - struct pakfire_packagelist_element* element = NULL; int kept_going = 0; int r = 0; // Call the callback once for every element on the list - TAILQ_FOREACH(element, &list->packages, nodes) { + for (unsigned int i = 0; i < self->num_packages; i++) { // Call the callback - r = callback(list->ctx, element->pkg, data); + r = callback(self->ctx, self->packages[i], data); // Continue if there was no error if (r == 0) @@ -190,7 +187,7 @@ int pakfire_packagelist_walk(struct pakfire_packagelist* list, return (kept_going > 0) ? kept_going : r; } -int pakfire_packagelist_import_solvables(struct pakfire_packagelist* list, +int pakfire_packagelist_import_solvables(struct pakfire_packagelist* self, struct pakfire* pakfire, Queue* q) { struct pakfire_package* pkg = NULL; int r; @@ -201,7 +198,7 @@ int pakfire_packagelist_import_solvables(struct pakfire_packagelist* list, if (r) return r; - r = pakfire_packagelist_push(list, pkg); + r = pakfire_packagelist_push(self, pkg); pakfire_package_unref(pkg); if (r) return r;