diff --git a/src/choices.c b/src/choices.c index 1244d06..09733d6 100644 --- a/src/choices.c +++ b/src/choices.c @@ -15,6 +15,9 @@ /* Initial size of choices array */ #define INITIAL_CHOICE_CAPACITY 128 +/* Initial size of multi-selection buffer */ +#define INITIAL_SELECTIONS_CAPACITY 8 + static int cmpchoice(const void *_idx1, const void *_idx2) { const struct scored_result *a = _idx1; const struct scored_result *b = _idx2; @@ -92,6 +95,11 @@ static void choices_resize(choices_t *c, size_t new_capacity) { c->capacity = new_capacity; } +static void choices_resize_selections(choices_t *c, size_t new_capacity) { + c->selections.strings = safe_realloc(c->selections.strings, new_capacity * sizeof(const char *)); + c->selections.capacity = new_capacity; +} + static void choices_reset_search(choices_t *c) { free(c->results); c->results = NULL; @@ -101,8 +109,8 @@ static void choices_reset_search(choices_t *c) { void choices_init(choices_t *c, options_t *options) { c->strings = NULL; c->results = NULL; - c->selections = NULL; - c->num_selections = 0; + c->selections.strings = NULL; + c->selections.capacity = c->selections.size = 0; c->buffer_size = 0; c->buffer = NULL; @@ -132,9 +140,9 @@ void choices_destroy(choices_t *c) { c->results = NULL; c->available = c->selection = 0; - free(c->selections); - c->selections = NULL; - c->num_selections = 0; + free(c->selections.strings); + c->selections.strings = NULL; + c->selections.capacity = c->selections.size = 0; } void choices_add(choices_t *c, const char *choice) { @@ -148,30 +156,37 @@ void choices_add(choices_t *c, const char *choice) { } void choices_select(choices_t *c, const char *choice) { - if (!c->selections) { - c->num_selections = 1; - c->selections = malloc(sizeof(char *)); - c->selections[0] = choice; - } else if (!choices_selected(c, choice)) { - c->num_selections++; - c->selections = realloc(c->selections, c->num_selections * sizeof(char *)); - c->selections[c->num_selections - 1] = choice; + if (c->selections.size == c->selections.capacity) { + if (c->selections.capacity == 0) { + choices_resize_selections(c, INITIAL_SELECTIONS_CAPACITY); + } else { + choices_resize_selections(c, c->selections.capacity * 2); + } + } + + if (!choices_selected(c, choice)) { + c->selections.strings[c->selections.size++] = choice; } } void choices_deselect(choices_t *c, const char *choice) { - for (size_t i = 0; i < c->num_selections; i++) { - if (c->selections[i] == choice) { - c->selections[i] = NULL; - c->num_selections--; + size_t index = c->selections.size; + for (size_t i = 0; i < c->selections.size; i++) { + if (c->selections.strings[i] == choice) { + c->selections.size--; + index = i; break; } } + + for (size_t i = index; i < c->selections.size; i++) { + c->selections.strings[i] = c->selections.strings[i+1]; + } } bool choices_selected(choices_t *c, const char *choice) { - for (size_t i = 0; i < c->num_selections; i++) { - if (c->selections[i] == choice) { + for (size_t i = 0; i < c->selections.size; i++) { + if (c->selections.strings[i] == choice) { return true; } } diff --git a/src/choices.h b/src/choices.h index 222bdb5..50ef7dc 100644 --- a/src/choices.h +++ b/src/choices.h @@ -25,8 +25,12 @@ typedef struct { size_t available; size_t selection; - const char **selections; - size_t num_selections; + struct { + const char **strings; + + size_t capacity; + size_t size; + } selections; unsigned int worker_count; } choices_t; diff --git a/src/tty_interface.c b/src/tty_interface.c index 571808d..fb34186 100644 --- a/src/tty_interface.c +++ b/src/tty_interface.c @@ -150,7 +150,7 @@ static void action_emit(tty_interface_t *state) { /* If no choices were selected with multi-select, use the choice under * the cursor */ - if (!state->choices->num_selections) { + if (!state->choices->selections.size) { const char *selection = choices_get(state->choices, state->choices->selection); if (selection) { /* output the result */ @@ -160,8 +160,8 @@ static void action_emit(tty_interface_t *state) { printf("%s\n", state->search); } } else { - for (size_t i = 0; i < state->choices->num_selections; i++) { - printf("%s\n", state->choices->selections[i]); + for (size_t i = 0; i < state->choices->selections.size; i++) { + printf("%s\n", state->choices->selections.strings[i]); } } diff --git a/test/test_choices.c b/test/test_choices.c index 33a610e..107568d 100644 --- a/test/test_choices.c +++ b/test/test_choices.c @@ -9,27 +9,28 @@ #include "greatest/greatest.h" #define ASSERT_SIZE_T_EQ(a,b) ASSERT_EQ_FMT((size_t)(a), (b), "%zu") +#define ASSERT_TRUE(cond) ASSERT_FALSE(!cond) static options_t default_options; static choices_t choices; static void setup(void *udata) { - (void)udata; + (void)udata; - options_init(&default_options); - choices_init(&choices, &default_options); + options_init(&default_options); + choices_init(&choices, &default_options); } static void teardown(void *udata) { - (void)udata; - choices_destroy(&choices); + (void)udata; + choices_destroy(&choices); } TEST test_choices_empty() { ASSERT_SIZE_T_EQ(0, choices.size); ASSERT_SIZE_T_EQ(0, choices.available); ASSERT_SIZE_T_EQ(0, choices.selection); - ASSERT_SIZE_T_EQ(0, choices.num_selections); + ASSERT_SIZE_T_EQ(0, choices.selections.size); choices_prev(&choices); ASSERT_SIZE_T_EQ(0, choices.selection); @@ -166,18 +167,18 @@ TEST test_choices_multi_select() { const char *first_choice = choices_get(&choices, 0); ASSERT_FALSE(choices_selected(&choices, first_choice)); choices_select(&choices, first_choice); - ASSERT_FALSE(!choices_selected(&choices, first_choice)); - ASSERT_SIZE_T_EQ(1, choices.num_selections); + ASSERT_TRUE(choices_selected(&choices, first_choice)); + ASSERT_SIZE_T_EQ(1, choices.selections.size); const char *second_choice = choices_get(&choices, 1); ASSERT_FALSE(choices_selected(&choices, second_choice)); choices_select(&choices, second_choice); - ASSERT_FALSE(!choices_selected(&choices, second_choice)); - ASSERT_SIZE_T_EQ(2, choices.num_selections); + ASSERT_TRUE(choices_selected(&choices, second_choice)); + ASSERT_SIZE_T_EQ(2, choices.selections.size); choices_deselect(&choices, second_choice); ASSERT_FALSE(choices_selected(&choices, second_choice)); - ASSERT_SIZE_T_EQ(1, choices.num_selections); + ASSERT_SIZE_T_EQ(1, choices.selections.size); PASS(); }