diff options
author | Phil Jones <philj56@gmail.com> | 2022-11-28 22:19:12 +0000 |
---|---|---|
committer | Phil Jones <philj56@gmail.com> | 2022-11-28 22:19:12 +0000 |
commit | 3861e8289ae40bac168275ce6f10b231e11baa55 (patch) | |
tree | 30093bb562e9718c60d3d36c9942ce4714c43488 /src | |
parent | 574b523ab5d9b63a114ac905e5ff8494f4b2f233 (diff) |
Refactor string vector code.
Previously, string vectors were built by reading input line-by line, and
multiple copies of string vectors were made when searching.
Now, input is read into one big buffer, and string vectors only contain
references to the strings in this buffer. This both speeds up reading of
input, and avoids unnecessary copying of strings in various places.
The main downside currently is that input read from stdin is no longer
UTF-8 normalised. This means, for example, that a search for `e` won't
necessarily match `é`. Normalisation is very slow relative to the rest
of tofi, however, and not needed for most use-cases. This could either
be solved by accepting the slowdown, or making this an option, such as
--unicode or --unicode-normalize.
Diffstat (limited to 'src')
-rw-r--r-- | src/compgen.c | 91 | ||||
-rw-r--r-- | src/compgen.h | 10 | ||||
-rw-r--r-- | src/desktop_vec.c | 10 | ||||
-rw-r--r-- | src/desktop_vec.h | 2 | ||||
-rw-r--r-- | src/entry.h | 5 | ||||
-rw-r--r-- | src/input.c | 14 | ||||
-rw-r--r-- | src/log.c | 12 | ||||
-rw-r--r-- | src/main.c | 90 | ||||
-rw-r--r-- | src/main_compgen.c | 12 | ||||
-rw-r--r-- | src/string_vec.c | 91 | ||||
-rw-r--r-- | src/string_vec.h | 49 |
11 files changed, 257 insertions, 129 deletions
diff --git a/src/compgen.c b/src/compgen.c index 6cc21a2..2fb2a04 100644 --- a/src/compgen.c +++ b/src/compgen.c @@ -50,7 +50,60 @@ static char *get_cache_path() { return cache_name; } -struct string_vec compgen_cached() +static void write_cache(const char *buffer, const char *filename) +{ + errno = 0; + FILE *fp = fopen(filename, "wb"); + if (!fp) { + log_error("Failed to open cache file \"%s\": %s\n", filename, strerror(errno)); + return; + } + size_t len = strlen(buffer); + errno = 0; + if (fwrite(buffer, len, 1, fp) != len) { + log_error("Error writing cache file \"%s\": %s\n", filename, strerror(errno)); + } + fclose(fp); +} + +static char *read_cache(const char *filename) +{ + errno = 0; + FILE *fp = fopen(filename, "rb"); + if (!fp) { + log_error("Failed to open cache file \"%s\": %s\n", filename, strerror(errno)); + return NULL; + } + if (fseek(fp, 0, SEEK_END)) { + log_error("Failed to seek in cache file: %s\n", strerror(errno)); + fclose(fp); + return NULL; + } + size_t size; + { + long ssize = ftell(fp); + if (ssize < 0) { + log_error("Failed to determine cache file size: %s\n", strerror(errno)); + fclose(fp); + return NULL; + } + size = (size_t)ssize; + } + char *cache = xmalloc(size + 1); + rewind(fp); + if (fread(cache, 1, size, fp) != size) { + log_error("Failed to read cache file: %s\n", strerror(errno)); + free(cache); + fclose(fp); + return NULL; + } + fclose(fp); + cache[size] = '\0'; + + return cache; +} + +char *compgen_cached() { log_debug("Retrieving PATH.\n"); const char *env_path = getenv("PATH"); @@ -71,15 +124,12 @@ struct string_vec compgen_cached() errno = 0; if (stat(cache_path, &sb) == -1) { if (errno == ENOENT) { - struct string_vec commands = compgen(); + char *commands = compgen(); if (!mkdirp(cache_path)) { free(cache_path); return commands; } - FILE *cache = fopen(cache_path, "wb"); - string_vec_save(&commands, cache); - fclose(cache); - free(cache_path); + write_cache(commands, cache_path); return commands; } free(cache_path); @@ -103,26 +153,22 @@ struct string_vec compgen_cached() } free(path); - struct string_vec commands; + char *commands; if (out_of_date) { log_debug("Cache out of date, updating.\n"); log_indent(); commands = compgen(); log_unindent(); - FILE *cache = fopen(cache_path, "wb"); - string_vec_save(&commands, cache); - fclose(cache); + write_cache(commands, cache_path); } else { log_debug("Cache up to date, loading.\n"); - FILE *cache = fopen(cache_path, "rb"); - commands = string_vec_load(cache); - fclose(cache); + commands = read_cache(cache_path); } free(cache_path); return commands; } -struct string_vec compgen() +char *compgen() { log_debug("Retrieving PATH.\n"); const char *env_path = getenv("PATH"); @@ -167,7 +213,18 @@ struct string_vec compgen() log_debug("Making unique.\n"); string_vec_uniq(&programs); - return programs; + size_t buf_len = 0; + for (size_t i = 0; i < programs.count; i++) { + buf_len += strlen(programs.buf[i].string) + 1; + } + char *buf = xmalloc(buf_len + 1); + size_t bytes_written = 0; + for (size_t i = 0; i < programs.count; i++) { + bytes_written += sprintf(&buf[bytes_written], "%s\n", programs.buf[i].string); + } + buf[bytes_written] = '\0'; + + return buf; } static int cmpscorep(const void *restrict a, const void *restrict b) @@ -177,11 +234,11 @@ static int cmpscorep(const void *restrict a, const void *restrict b) return str2->history_score - str1->history_score; } -void compgen_history_sort(struct string_vec *programs, struct history *history) +void compgen_history_sort(struct string_ref_vec *programs, struct history *history) { log_debug("Moving already known programs to the front.\n"); for (size_t i = 0; i < history->count; i++) { - struct scored_string *res = string_vec_find_sorted(programs, history->buf[i].name); + struct scored_string_ref *res = string_ref_vec_find_sorted(programs, history->buf[i].name); if (res == NULL) { log_debug("History entry \"%s\" not found.\n", history->buf[i].name); continue; diff --git a/src/compgen.h b/src/compgen.h index a80c62b..5e36576 100644 --- a/src/compgen.h +++ b/src/compgen.h @@ -4,8 +4,12 @@ #include "history.h" #include "string_vec.h" -struct string_vec compgen(void); -struct string_vec compgen_cached(void); -void compgen_history_sort(struct string_vec *programs, struct history *history); +[[nodiscard("memory leaked")]] +char *compgen(void); + +[[nodiscard("memory leaked")]] +char *compgen_cached(void); + +void compgen_history_sort(struct string_ref_vec *programs, struct history *history); #endif /* COMPGEN_H */ diff --git a/src/desktop_vec.c b/src/desktop_vec.c index a9aa1f7..de7fcd7 100644 --- a/src/desktop_vec.c +++ b/src/desktop_vec.c @@ -145,12 +145,12 @@ struct desktop_entry *desktop_vec_find_sorted(struct desktop_vec *restrict vec, return bsearch(&tmp, vec->buf, vec->count, sizeof(vec->buf[0]), cmpdesktopp); } -struct string_vec desktop_vec_filter( +struct string_ref_vec desktop_vec_filter( const struct desktop_vec *restrict vec, const char *restrict substr, bool fuzzy) { - struct string_vec filt = string_vec_create(); + struct string_ref_vec filt = string_ref_vec_create(); for (size_t i = 0; i < vec->count; i++) { int32_t search_score; if (fuzzy) { @@ -159,7 +159,7 @@ struct string_vec desktop_vec_filter( search_score = fuzzy_match_simple_words(substr, vec->buf[i].name); } if (search_score != INT32_MIN) { - string_vec_add(&filt, vec->buf[i].name); + string_ref_vec_add(&filt, vec->buf[i].name); /* * Store the position of the match in the string as * its search_score, for later sorting. @@ -174,7 +174,7 @@ struct string_vec desktop_vec_filter( search_score = fuzzy_match_simple_words(substr, vec->buf[i].keywords); } if (search_score != INT32_MIN) { - string_vec_add(&filt, vec->buf[i].name); + string_ref_vec_add(&filt, vec->buf[i].name); /* * Arbitrary score addition to make name * matches preferred over keyword matches. @@ -254,7 +254,6 @@ bool match_current_desktop(char * const *desktop_list, gsize length) string_vec_add(&desktops, desktop); desktop = strtok_r(NULL, ":", &saveptr); } - free(tmp); string_vec_sort(&desktops); for (gsize i = 0; i < length; i++) { @@ -264,6 +263,7 @@ bool match_current_desktop(char * const *desktop_list, gsize length) } string_vec_destroy(&desktops); + free(tmp); return false; } diff --git a/src/desktop_vec.h b/src/desktop_vec.h index 760db30..f8d0b79 100644 --- a/src/desktop_vec.h +++ b/src/desktop_vec.h @@ -34,7 +34,7 @@ void desktop_vec_add_file(struct desktop_vec *desktop, const char *id, const cha void desktop_vec_sort(struct desktop_vec *restrict vec); struct desktop_entry *desktop_vec_find_sorted(struct desktop_vec *restrict vec, const char *name); -struct string_vec desktop_vec_filter( +struct string_ref_vec desktop_vec_filter( const struct desktop_vec *restrict vec, const char *restrict substr, bool fuzzy); diff --git a/src/entry.h b/src/entry.h index a9353a5..4ee6316 100644 --- a/src/entry.h +++ b/src/entry.h @@ -55,8 +55,9 @@ struct entry { uint32_t selection; uint32_t first_result; - struct string_vec results; - struct string_vec commands; + char *command_buffer; + struct string_ref_vec results; + struct string_ref_vec commands; struct desktop_vec apps; struct history history; bool use_pango; diff --git a/src/input.c b/src/input.c index 5864cd1..9be16e9 100644 --- a/src/input.c +++ b/src/input.c @@ -130,13 +130,13 @@ void add_character(struct tofi *tofi, xkb_keycode_t keycode) N_ELEM(buf)); entry->input_utf8_length += len; if (entry->drun) { - struct string_vec results = desktop_vec_filter(&entry->apps, entry->input_utf8, tofi->fuzzy_match); - string_vec_destroy(&entry->results); + struct string_ref_vec results = desktop_vec_filter(&entry->apps, entry->input_utf8, tofi->fuzzy_match); + string_ref_vec_destroy(&entry->results); entry->results = results; } else { - struct string_vec tmp = entry->results; - entry->results = string_vec_filter(&entry->results, entry->input_utf8, tofi->fuzzy_match); - string_vec_destroy(&tmp); + struct string_ref_vec tmp = entry->results; + entry->results = string_ref_vec_filter(&entry->results, entry->input_utf8, tofi->fuzzy_match); + string_ref_vec_destroy(&tmp); } reset_selection(tofi); @@ -154,11 +154,11 @@ void input_refresh_results(struct tofi *tofi) } entry->input_utf8[bytes_written] = '\0'; entry->input_utf8_length = bytes_written; - string_vec_destroy(&entry->results); + string_ref_vec_destroy(&entry->results); if (entry->drun) { entry->results = desktop_vec_filter(&entry->apps, entry->input_utf8, tofi->fuzzy_match); } else { - entry->results = string_vec_filter(&entry->commands, entry->input_utf8, tofi->fuzzy_match); + entry->results = string_ref_vec_filter(&entry->commands, entry->input_utf8, tofi->fuzzy_match); } reset_selection(tofi); @@ -53,11 +53,11 @@ void log_warning(const char *const fmt, ...) void log_debug(const char *const fmt, ...) { #ifndef DEBUG - return; + //return; #endif static struct timespec start_time; if (start_time.tv_nsec == 0) { - fprintf(stderr, "[ real, cpu, maxRSS]\n"); + fprintf(stderr, "[ real, cpu, maxRSS]\n"); clock_gettime(CLOCK_REALTIME, &start_time); } struct timespec real_time; @@ -73,11 +73,11 @@ void log_debug(const char *const fmt, ...) va_start(args, fmt); fprintf( stderr, - "[%ld.%03ld, %ld.%03ld, %5ld KB][DEBUG]: ", + "[%ld.%06ld, %ld.%06ld, %5ld KB][DEBUG]: ", real_time.tv_sec, - real_time.tv_nsec / 1000000, + real_time.tv_nsec / 1000, cpu_time.tv_sec, - cpu_time.tv_nsec / 1000000, + cpu_time.tv_nsec / 1000, usage.ru_maxrss ); print_indent(stderr); @@ -114,7 +114,7 @@ void log_append_warning(const char *const fmt, ...) void log_append_debug(const char *const fmt, ...) { #ifndef DEBUG - return; + //return; #endif va_list args; va_start(args, fmt); @@ -46,6 +46,35 @@ static uint32_t gettime_ms() { return ms; } + +/* Read all of stdin into a buffer. */ +static char *read_stdin() { + const size_t block_size = BUFSIZ; + size_t num_blocks = 1; + size_t buf_size = block_size; + + char *buf = xmalloc(buf_size); + for (size_t block = 0; ; block++) { + if (block == num_blocks) { + num_blocks *= 2; + buf = xrealloc(buf, num_blocks * block_size); + } + size_t bytes_read = fread( + &buf[block * block_size], + 1, + block_size, + stdin); + if (bytes_read != block_size) { + if (!feof(stdin) && ferror(stdin)) { + log_error("Error reading stdin\n"); + } + buf[block * block_size + bytes_read] = '\0'; + break; + } + } + return buf; +} + static void zwlr_layer_surface_configure( void *data, struct zwlr_layer_surface_v1 *zwlr_layer_surface, @@ -100,14 +129,15 @@ static void wl_keyboard_keymap( { struct tofi *tofi = data; assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); - log_debug("Configuring keyboard.\n"); char *map_shm = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); assert(map_shm != MAP_FAILED); if (tofi->late_keyboard_init) { + log_debug("Delaying keyboard configuration.\n"); tofi->xkb_keymap_string = xstrdup(map_shm); } else { + log_debug("Configuring keyboard.\n"); struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string( tofi->xkb_context, map_shm, @@ -121,11 +151,10 @@ static void wl_keyboard_keymap( xkb_state_unref(tofi->xkb_state); tofi->xkb_keymap = xkb_keymap; tofi->xkb_state = xkb_state; + log_debug("Keyboard configured.\n"); } munmap(map_shm, size); close(fd); - - log_debug("Keyboard configured.\n"); } static void wl_keyboard_enter( @@ -1095,6 +1124,7 @@ int main(int argc, char *argv[]) } parse_args(&tofi, argc, argv); + log_debug("Config done\n"); if (!tofi.multiple_instance && lock_check()) { log_error("Another instance of tofi is already running.\n"); @@ -1108,16 +1138,20 @@ int main(int argc, char *argv[]) * to Wayland. */ + log_debug("Connecting to Wayland display.\n"); tofi.wl_display = wl_display_connect(NULL); if (tofi.wl_display == NULL) { log_error("Couldn't connect to Wayland display.\n"); exit(EXIT_FAILURE); } tofi.wl_registry = wl_display_get_registry(tofi.wl_display); - tofi.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (tofi.xkb_context == NULL) { - log_error("Couldn't create an XKB context.\n"); - exit(EXIT_FAILURE); + if (!tofi.late_keyboard_init) { + log_debug("Creating xkb context.\n"); + tofi.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (tofi.xkb_context == NULL) { + log_error("Couldn't create an XKB context.\n"); + exit(EXIT_FAILURE); + } } wl_registry_add_listener( tofi.wl_registry, @@ -1286,7 +1320,8 @@ int main(int argc, char *argv[]) if (strstr(argv[0], "-run")) { log_debug("Generating command list.\n"); log_indent(); - tofi.window.entry.commands = compgen_cached(); + tofi.window.entry.command_buffer = compgen_cached(); + tofi.window.entry.commands = string_ref_vec_from_buffer(tofi.window.entry.command_buffer); if (tofi.use_history) { if (tofi.history_file[0] == 0) { tofi.window.entry.history = history_load_default_file(tofi.window.entry.drun); @@ -1312,36 +1347,30 @@ int main(int argc, char *argv[]) drun_history_sort(&apps, &tofi.window.entry.history); } } - struct string_vec commands = string_vec_create(); + struct string_ref_vec commands = string_ref_vec_create(); for (size_t i = 0; i < apps.count; i++) { - string_vec_add(&commands, apps.buf[i].name); + string_ref_vec_add(&commands, apps.buf[i].name); } tofi.window.entry.commands = commands; tofi.window.entry.apps = apps; log_unindent(); log_debug("App list generated.\n"); } else { - char *line = NULL; - size_t n = 0; - tofi.window.entry.commands = string_vec_create(); - while (getline(&line, &n, stdin) != -1) { - char *c = strchr(line, '\n'); - if (c) { - *c = '\0'; - } - string_vec_add(&tofi.window.entry.commands, line); - } - free(line); + log_debug("Reading stdin.\n"); + char *buf = read_stdin(); + tofi.window.entry.command_buffer = buf; + tofi.window.entry.commands = string_ref_vec_from_buffer(buf); if (tofi.use_history) { if (tofi.history_file[0] == 0) { tofi.use_history = false; } else { tofi.window.entry.history = history_load(tofi.history_file); - string_vec_history_sort(&tofi.window.entry.commands, &tofi.window.entry.history); + string_ref_vec_history_sort(&tofi.window.entry.commands, &tofi.window.entry.history); } } + log_debug("Result list generated.\n"); } - tofi.window.entry.results = string_vec_copy(&tofi.window.entry.commands); + tofi.window.entry.results = string_ref_vec_copy(&tofi.window.entry.commands); /* * Next, we create the Wayland surface, which takes on the @@ -1476,6 +1505,13 @@ int main(int argc, char *argv[]) /* If we delayed keyboard initialisation, do it now */ if (tofi.late_keyboard_init) { + log_debug("Creating xkb context.\n"); + tofi.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (tofi.xkb_context == NULL) { + log_error("Couldn't create an XKB context.\n"); + exit(EXIT_FAILURE); + } + log_debug("Configuring keyboard.\n"); struct xkb_keymap *xkb_keymap = xkb_keymap_new_from_string( tofi.xkb_context, tofi.xkb_keymap_string, @@ -1489,6 +1525,7 @@ int main(int argc, char *argv[]) tofi.xkb_state = xkb_state; free(tofi.xkb_keymap_string); tofi.late_keyboard_init = false; + log_debug("Keyboard configured.\n"); } /* @@ -1642,8 +1679,11 @@ int main(int argc, char *argv[]) if (tofi.window.entry.drun) { desktop_vec_destroy(&tofi.window.entry.apps); } - string_vec_destroy(&tofi.window.entry.commands); - string_vec_destroy(&tofi.window.entry.results); + if (tofi.window.entry.command_buffer != NULL) { + free(tofi.window.entry.command_buffer); + } + string_ref_vec_destroy(&tofi.window.entry.commands); + string_ref_vec_destroy(&tofi.window.entry.results); if (tofi.use_history) { history_destroy(&tofi.window.entry.history); } diff --git a/src/main_compgen.c b/src/main_compgen.c index 743d2cc..6375c54 100644 --- a/src/main_compgen.c +++ b/src/main_compgen.c @@ -1,10 +1,16 @@ #include <stdio.h> +#include <stdlib.h> #include "compgen.h" #include "string_vec.h" int main() { - struct string_vec commands = compgen_cached(); - string_vec_save(&commands, stdout); - string_vec_destroy(&commands); + char *buf = compgen_cached(); + struct string_ref_vec commands = string_ref_vec_from_buffer(buf); + for (size_t i = 0; i < commands.count; i++) { + fputs(commands.buf[i].string, stdout); + fputc('\n', stdout); + } + string_ref_vec_destroy(&commands); + free(buf); } diff --git a/src/string_vec.c b/src/string_vec.c index 342fd9f..039d191 100644 --- a/src/string_vec.c +++ b/src/string_vec.c @@ -56,6 +56,16 @@ struct string_vec string_vec_create(void) return vec; } +struct string_ref_vec string_ref_vec_create(void) +{ + struct string_ref_vec vec = { + .count = 0, + .size = 128, + .buf = xcalloc(128, sizeof(*vec.buf)), + }; + return vec; +} + void string_vec_destroy(struct string_vec *restrict vec) { for (size_t i = 0; i < vec->count; i++) { @@ -64,16 +74,21 @@ void string_vec_destroy(struct string_vec *restrict vec) free(vec->buf); } -struct string_vec string_vec_copy(const struct string_vec *restrict vec) +void string_ref_vec_destroy(struct string_ref_vec *restrict vec) +{ + free(vec->buf); +} + +struct string_ref_vec string_ref_vec_copy(const struct string_ref_vec *restrict vec) { - struct string_vec copy = { + struct string_ref_vec copy = { .count = vec->count, .size = vec->size, .buf = xcalloc(vec->size, sizeof(*copy.buf)), }; for (size_t i = 0; i < vec->count; i++) { - copy.buf[i].string = xstrdup(vec->buf[i].string); + copy.buf[i].string = vec->buf[i].string; copy.buf[i].search_score = vec->buf[i].search_score; copy.buf[i].history_score = vec->buf[i].history_score; } @@ -99,14 +114,13 @@ void string_vec_add(struct string_vec *restrict vec, const char *restrict str) vec->count++; } -/* Same as string_vec_add(), but assume str is normalized for speed. */ -static void string_vec_add_normalized(struct string_vec *restrict vec, const char *restrict str) +void string_ref_vec_add(struct string_ref_vec *restrict vec, char *restrict str) { if (vec->count == vec->size) { vec->size *= 2; vec->buf = xrealloc(vec->buf, vec->size * sizeof(vec->buf[0])); } - vec->buf[vec->count].string = xstrdup(str); + vec->buf[vec->count].string = str; vec->buf[vec->count].search_score = 0; vec->buf[vec->count].history_score = 0; vec->count++; @@ -117,7 +131,7 @@ void string_vec_sort(struct string_vec *restrict vec) qsort(vec->buf, vec->count, sizeof(vec->buf[0]), cmpstringp); } -void string_vec_history_sort(struct string_vec *restrict vec, struct history *history) +void string_ref_vec_history_sort(struct string_ref_vec *restrict vec, struct history *history) { /* * To find elements without assuming the vector is pre-sorted, we use a @@ -129,7 +143,7 @@ void string_vec_history_sort(struct string_vec *restrict vec, struct history *hi g_hash_table_insert(hash, vec->buf[i].string, &vec->buf[i]); } for (size_t i = 0; i < history->count; i++) { - struct scored_string *res = g_hash_table_lookup(hash, history->buf[i].name); + struct scored_string_ref *res = g_hash_table_lookup(hash, history->buf[i].name); if (res == NULL) { continue; } @@ -159,15 +173,20 @@ struct scored_string *string_vec_find_sorted(struct string_vec *restrict vec, co return bsearch(&str, vec->buf, vec->count, sizeof(vec->buf[0]), cmpstringp); } -struct string_vec string_vec_filter( - const struct string_vec *restrict vec, +struct scored_string_ref *string_ref_vec_find_sorted(struct string_ref_vec *restrict vec, const char * str) +{ + return bsearch(&str, vec->buf, vec->count, sizeof(vec->buf[0]), cmpstringp); +} + +struct string_ref_vec string_ref_vec_filter( + const struct string_ref_vec *restrict vec, const char *restrict substr, bool fuzzy) { if (substr[0] == '\0') { - return string_vec_copy(vec); + return string_ref_vec_copy(vec); } - struct string_vec filt = string_vec_create(); + struct string_ref_vec filt = string_ref_vec_create(); for (size_t i = 0; i < vec->count; i++) { int32_t search_score; if (fuzzy) { @@ -176,55 +195,25 @@ struct string_vec string_vec_filter( search_score = fuzzy_match_simple_words(substr, vec->buf[i].string); } if (search_score != INT32_MIN) { - /* - * Assume that the vector we're filtering is already - * normalized. - */ - string_vec_add_normalized(&filt, vec->buf[i].string); - /* - * Store the position of the match in the string as - * its search_score, for later sorting. - */ + string_ref_vec_add(&filt, vec->buf[i].string); filt.buf[filt.count - 1].search_score = search_score; filt.buf[filt.count - 1].history_score = vec->buf[i].history_score; } } - /* - * Sort the results by this search_score. This moves matches at the beginnings - * of words to the front of the result list. - */ + /* Sort the results by their search score. */ qsort(filt.buf, filt.count, sizeof(filt.buf[0]), cmpscorep); return filt; } -struct string_vec string_vec_load(FILE *file) +struct string_ref_vec string_ref_vec_from_buffer(char *buffer) { - struct string_vec vec = string_vec_create(); - if (file == NULL) { - return vec; - } + struct string_ref_vec vec = string_ref_vec_create(); - ssize_t bytes_read; - char *line = NULL; - size_t len; - while ((bytes_read = getline(&line, &len, file)) != -1) { - if (line[bytes_read - 1] == '\n') { - line[bytes_read - 1] = '\0'; - } - /* - * Assume that the vector we're loading is already normalized. - */ - string_vec_add_normalized(&vec, line); + char *saveptr = NULL; + char *line = strtok_r(buffer, "\n", &saveptr); + while (line != NULL) { + string_ref_vec_add(&vec, line); + line = strtok_r(NULL, "\n", &saveptr); } - free(line); - return vec; } - -void string_vec_save(struct string_vec *restrict vec, FILE *restrict file) -{ - for (size_t i = 0; i < vec->count; i++) { - fputs(vec->buf[i].string, file); - fputc('\n', file); - } -} diff --git a/src/string_vec.h b/src/string_vec.h index f04b385..6f54d56 100644 --- a/src/string_vec.h +++ b/src/string_vec.h @@ -24,27 +24,58 @@ struct string_vec string_vec_create(void); void string_vec_destroy(struct string_vec *restrict vec); -[[nodiscard("memory leaked")]] -struct string_vec string_vec_copy(const struct string_vec *restrict vec); - void string_vec_add(struct string_vec *restrict vec, const char *restrict str); void string_vec_sort(struct string_vec *restrict vec); -void string_vec_history_sort(struct string_vec *restrict vec, struct history *history); +struct scored_string *string_vec_find_sorted(struct string_vec *restrict vec, const char *str); + + +/* + * Like a string_vec, but only store a reference to the corresponding string + * rather than copying it. Although compatible with the string_vec struct, we + * create a new struct to make the compiler complain if we mix them up. + */ +struct scored_string_ref { + char *string; + int32_t search_score; + int32_t history_score; +}; + +struct string_ref_vec { + size_t count; + size_t size; + struct scored_string_ref *buf; +}; + +/* + * Although some of these functions are identical to the corresponding + * string_vec ones, we create new functions to avoid potentially mixing up + * the two. + */ +[[nodiscard("memory leaked")]] +struct string_ref_vec string_ref_vec_create(void); + +void string_ref_vec_destroy(struct string_ref_vec *restrict vec); + +[[nodiscard("memory leaked")]] +struct string_ref_vec string_ref_vec_copy(const struct string_ref_vec *restrict vec); + +void string_ref_vec_add(struct string_ref_vec *restrict vec, char *restrict str); + +void string_ref_vec_history_sort(struct string_ref_vec *restrict vec, struct history *history); void string_vec_uniq(struct string_vec *restrict vec); -struct scored_string *string_vec_find_sorted(struct string_vec *restrict vec, const char *str); +struct scored_string_ref *string_ref_vec_find_sorted(struct string_ref_vec *restrict vec, const char *str); [[nodiscard("memory leaked")]] -struct string_vec string_vec_filter( - const struct string_vec *restrict vec, +struct string_ref_vec string_ref_vec_filter( + const struct string_ref_vec *restrict vec, const char *restrict substr, bool fuzzy); [[nodiscard("memory leaked")]] -struct string_vec string_vec_load(FILE *file); -void string_vec_save(struct string_vec *restrict vec, FILE *restrict file); +struct string_ref_vec string_ref_vec_from_buffer(char *buffer); #endif /* STRING_VEC_H */ |