]> git.ipfire.org Git - pakfire.git/commitdiff
filelist: Refactor using binary search
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 27 Oct 2024 11:12:28 +0000 (11:12 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 27 Oct 2024 11:37:55 +0000 (11:37 +0000)
If we want to conduct fast searches over the list we should implement it
like this so that we don't have to iterate very long lists.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/filelist.c

index 8ce12197ae6691a934ae9838b9562723467723ce..baca546b6d02fbd98ae9a3f37b746b067704e08a 100644 (file)
@@ -20,7 +20,6 @@
 
 #include <errno.h>
 #include <stdlib.h>
-#include <sys/queue.h>
 
 // libarchive
 #include <archive.h>
 #include <pakfire/string.h>
 #include <pakfire/util.h>
 
-struct pakfire_filelist_element {
-       TAILQ_ENTRY(pakfire_filelist_element) nodes;
-
-       struct pakfire_file* file;
-};
-
 struct pakfire_filelist {
        struct pakfire_ctx* ctx;
        struct pakfire* pakfire;
        int nrefs;
 
-       TAILQ_HEAD(entries, pakfire_filelist_element) files;
+       struct pakfire_file** files;
+       unsigned int num_files;
 };
 
 PAKFIRE_EXPORT int pakfire_filelist_create(struct pakfire_filelist** list, struct pakfire* pakfire) {
@@ -68,9 +62,6 @@ PAKFIRE_EXPORT int pakfire_filelist_create(struct pakfire_filelist** list, struc
        // Initialize the reference counter
        l->nrefs = 1;
 
-       // Initialise files
-       TAILQ_INIT(&l->files);
-
        // Return the pointer
        *list = l;
 
@@ -83,6 +74,8 @@ static void pakfire_filelist_free(struct pakfire_filelist* list) {
                pakfire_unref(list->pakfire);
        if (list->ctx)
                pakfire_ctx_unref(list->ctx);
+       if (list->files)
+               free(list->files);
        free(list);
 }
 
@@ -101,137 +94,132 @@ PAKFIRE_EXPORT struct pakfire_filelist* pakfire_filelist_unref(struct pakfire_fi
 }
 
 PAKFIRE_EXPORT size_t pakfire_filelist_length(struct pakfire_filelist* list) {
-       struct pakfire_filelist_element* element = NULL;
-       size_t size = 0;
-
-       TAILQ_FOREACH(element, &list->files, nodes)
-               size++;
-
-       return size;
+       return list->num_files;
 }
 
 size_t pakfire_filelist_total_size(struct pakfire_filelist* list) {
-       struct pakfire_filelist_element* element = NULL;
        size_t size = 0;
 
-       TAILQ_FOREACH(element, &list->files, nodes)
-               size += pakfire_file_get_size(element->file);
+       for (unsigned int i = 0; i < list->num_files; i++)
+               size += pakfire_file_get_size(list->files[i]);
 
        return size;
 }
 
 PAKFIRE_EXPORT int pakfire_filelist_is_empty(struct pakfire_filelist* list) {
-       return TAILQ_EMPTY(&list->files);
+       return list->num_files == 0;
 }
 
 PAKFIRE_EXPORT void pakfire_filelist_clear(struct pakfire_filelist* list) {
-       struct pakfire_filelist_element* element = NULL;
-
-       while (!TAILQ_EMPTY(&list->files)) {
-               // Fetch the first element
-               element = TAILQ_FIRST(&list->files);
-
-               // Remove it from the list
-               TAILQ_REMOVE(&list->files, element, nodes);
-
-               // Dereference the file
-               pakfire_file_unref(element->file);
-
-               // Free it all
-               free(element);
-       }
+       for (unsigned int i = 0; i < list->num_files; i++)
+               pakfire_file_unref(list->files[i]);
 }
 
 PAKFIRE_EXPORT struct pakfire_file* pakfire_filelist_get(struct pakfire_filelist* list, size_t index) {
-       struct pakfire_filelist_element* element = NULL;
-
-       // Fetch the first element
-       element = TAILQ_FIRST(&list->files);
-
-       while (element && index--)
-               element = TAILQ_NEXT(element, nodes);
-
-       if (!element)
+       // Check that index is in range
+       if (index >= list->num_files) {
+               errno = ERANGE;
                return NULL;
+       }
 
-       return pakfire_file_ref(element->file);
+       return pakfire_file_ref(list->files[index]);
 }
 
 /*
        Checks if the file is already on the list
 */
 static int pakfire_filelist_has_file(struct pakfire_filelist* list, struct pakfire_file* file) {
-       struct pakfire_filelist_element* e = NULL;
-
-       TAILQ_FOREACH(e, &list->files, nodes) {
-               if (e->file == file)
-                       return 1;
+       for (unsigned int i = 0; i < list->num_files; i++) {
+               if (list->files[i] == file)
+                       return i;
        }
 
-       return 0;
+       // Not found
+       return -1;
 }
 
-PAKFIRE_EXPORT int pakfire_filelist_add(struct pakfire_filelist* list, struct pakfire_file* file) {
-       struct pakfire_filelist_element* element = NULL;
-       struct pakfire_filelist_element* e = NULL;
+static int pakfire_filelist_search(struct pakfire_filelist* list, struct pakfire_file* file) {
+       int i;
+       int r;
 
-       // Do not do anything if the file is already on the list
-       if (pakfire_filelist_has_file(list, file))
-               return 0;
+       // Set starting points
+       int lo = 0;
+       int hi = list->num_files - 1;
 
-       // Allocate a new element
-       element = calloc(1, sizeof *element);
-       if (!element) {
-               ERROR(list->ctx, "Could not allocate a new filelist element: %m\n");
-               return 1;
+       while (lo <= hi) {
+               // Find the middle
+               i = lo + (hi - lo) / 2;
+
+               // Compare the files
+               r = pakfire_file_cmp(list->files[i], file);
+
+               // We have an exact match!
+               if (r == 0)
+                       return i;
+
+               // Move to the right
+               else if (r < 0)
+                       lo = i + 1;
+
+               // Move to the left
+               else if (r > 0)
+                       hi = i - 1;
        }
 
-       // Reference the file
-       element->file = pakfire_file_ref(file);
+       // Not found (so put it at the end)
+       return list->num_files;
+}
 
-       // Fetch the last element
-       e = TAILQ_LAST(&list->files, entries);
+PAKFIRE_EXPORT int pakfire_filelist_add(struct pakfire_filelist* list, struct pakfire_file* file) {
+       // Do not do anything if the file is already on the list
+       if (pakfire_filelist_has_file(list, file) >= 0)
+               return 0;
 
-       // Skip all elements that are "greater than" the element we want to add
-       while (e) {
-               if (pakfire_file_cmp(e->file, file) <= 0)
-                       break;
+       // Find the index where the file should go
+       int i = pakfire_filelist_search(list, file);
 
-               e = TAILQ_PREV(e, entries, nodes);
+       // Make space
+       list->files = reallocarray(list->files, list->num_files + 1, sizeof(*list->files));
+       if (!list->files) {
+               ERROR(list->ctx, "Could not allocate filelist: %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->files, e, element, nodes);
-       else
-               TAILQ_INSERT_HEAD(&list->files, element, nodes);
+       // Move everything up one slot
+       memmove(list->files + i + 1, list->files + i, list->num_files - i);
+
+       // Store the file
+       list->files[i] = pakfire_file_ref(file);
+
+       // The list is now longer
+       ++list->num_files;
 
        return 0;
 }
 
 static int pakfire_filelist_remove(struct pakfire_filelist* list, struct pakfire_file* file) {
-       struct pakfire_filelist_element* element = NULL;
+       // Check if the file is on the list
+       int i = pakfire_filelist_has_file(list, file);
 
-       TAILQ_FOREACH(element, &list->files, nodes) {
-               if (element->file == file) {
-                       // Remove the element from the list
-                       TAILQ_REMOVE(&list->files, element, nodes);
+       // Do nothing if the file is not on the list
+       if (i < 0)
+               return 0;
 
-                       // Free the element
-                       pakfire_file_unref(element->file);
-                       free(element);
+       // Remove the reference
+       pakfire_file_unref(list->files[i]);
 
-                       return 0;
-               }
-       }
+       // Fill the gap
+       memmove(list->files + i, list->files + i + 1, list->num_files - i - 1);
+
+       // The list is now shorter
+       --list->num_files;
 
        return 0;
 }
 
 static int __pakfire_filelist_remove_one(
                struct pakfire_ctx* ctx, struct pakfire_file* file, void* data) {
-       struct pakfire_filelist* list = (struct pakfire_filelist*)data;
+       struct pakfire_filelist* list = data;
 
        // Remove the file from the given filelist
        return pakfire_filelist_remove(list, file);
@@ -434,17 +422,13 @@ ERROR:
 }
 
 int pakfire_filelist_contains(struct pakfire_filelist* list, const char* pattern) {
-       struct pakfire_filelist_element* element = NULL;
-
-       if (!pattern) {
-               errno = EINVAL;
-               return -1;
-       }
+       if (!pattern)
+               return -EINVAL;
 
-       TAILQ_FOREACH(element, &list->files, nodes) {
-               const char* path = pakfire_file_get_path(element->file);
+       for (unsigned int i = 0; i < list->num_files; i++) {
+               const char* path = pakfire_file_get_path(list->files[i]);
                if (!path)
-                       return -1;
+                       return -errno;
 
                if (pakfire_path_match(pattern, path))
                        return 1;
@@ -456,7 +440,6 @@ int pakfire_filelist_contains(struct pakfire_filelist* list, const char* pattern
 int pakfire_filelist_walk(struct pakfire_filelist* list,
                pakfire_filelist_walk_callback callback, void* data, int flags) {
        struct pakfire_progress* progress = NULL;
-       struct pakfire_filelist_element* element = NULL;
        int r = 0;
 
        // Show progress when iterating over the filelist
@@ -480,13 +463,13 @@ int pakfire_filelist_walk(struct pakfire_filelist* list,
        }
 
        // Call the callback once for every element on the list
-       TAILQ_FOREACH(element, &list->files, nodes) {
+       for (unsigned int i = 0; i < list->num_files; i++) {
                // Increment the progress
                if (progress)
                        pakfire_progress_increment(progress, 1);
 
                // Call the callback
-               r = callback(list->ctx, element->file, data);
+               r = callback(list->ctx, list->files[i], data);
                if (r)
                        break;
        }
@@ -527,7 +510,6 @@ int pakfire_filelist_dump(struct pakfire_filelist* list, int flags) {
        Verifies all files on the filelist
 */
 int pakfire_filelist_verify(struct pakfire_filelist* list, struct pakfire_filelist* errors) {
-       struct pakfire_filelist_element* element = NULL;
        struct pakfire_progress* progress = NULL;
        int status;
        int r;
@@ -553,16 +535,16 @@ int pakfire_filelist_verify(struct pakfire_filelist* list, struct pakfire_fileli
                goto ERROR;
 
        // Iterate over the entire list
-       TAILQ_FOREACH(element, &list->files, nodes) {
+       for (unsigned int i = 0; i < list->num_files; i++) {
                // Verify the file
-               r = pakfire_file_verify(element->file, &status);
+               r = pakfire_file_verify(list->files[i], &status);
                if (r)
                        goto ERROR;
 
                // If the verification failed, we append it to the errors list
                if (status) {
                        // Append the file to the error list
-                       r = pakfire_filelist_add(errors, element->file);
+                       r = pakfire_filelist_add(errors, list->files[i]);
                        if (r)
                                goto ERROR;
                }
@@ -586,7 +568,6 @@ ERROR:
 }
 
 int pakfire_filelist_cleanup(struct pakfire_filelist* list, int flags) {
-       struct pakfire_filelist_element* element = NULL;
        int r;
 
        // Nothing to do if the filelist is empty
@@ -594,8 +575,8 @@ int pakfire_filelist_cleanup(struct pakfire_filelist* list, int flags) {
                return 0;
 
        // Walk through the list backwards
-       TAILQ_FOREACH_REVERSE(element, &list->files, entries, nodes) {
-               r = pakfire_file_cleanup(element->file, flags);
+       for (int i = list->num_files - 1; i >= 0; i--) {
+               r = pakfire_file_cleanup(list->files[i], flags);
                if (r)
                        return r;
        }