From: Michael Tremer Date: Sun, 27 Oct 2024 11:12:28 +0000 (+0000) Subject: filelist: Refactor using binary search X-Git-Tag: 0.9.30~794 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=73287e4fa739ba30df0645c509288a874197defc;p=pakfire.git filelist: Refactor using binary search 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 --- diff --git a/src/libpakfire/filelist.c b/src/libpakfire/filelist.c index 8ce12197a..baca546b6 100644 --- a/src/libpakfire/filelist.c +++ b/src/libpakfire/filelist.c @@ -20,7 +20,6 @@ #include #include -#include // libarchive #include @@ -37,18 +36,13 @@ #include #include -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; }