]> git.ipfire.org Git - people/ric9/pakfire.git/commitdiff
packagelist: Refactor using binary search
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 11 Jan 2025 17:28:30 +0000 (17:28 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 11 Jan 2025 17:28:30 +0000 (17:28 +0000)
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 <michael.tremer@ipfire.org>
src/pakfire/packagelist.c

index 3865c30a85b86687198d505fad57121dd713f0c4..b8a80833d1293f8e7317a24d0954ea02ab80c120 100644 (file)
@@ -18,8 +18,8 @@
 #                                                                             #
 #############################################################################*/
 
+#include <errno.h>
 #include <stdlib.h>
-#include <sys/queue.h>
 
 #include <solv/queue.h>
 
 #include <pakfire/packagelist.h>
 #include <pakfire/pakfire.h>
 
-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;