strvec: strvec_splice() to a statically initialized vector

We use a singleton empty array to initialize a `struct strvec`;
similar to the empty string singleton we use to initialize a `struct
strbuf`.

Note that an empty strvec instance (with zero elements) does not
necessarily need to be an instance initialized with the singleton.
Let's refer to strvec instances initialized with the singleton as
"empty-singleton" instances.

    As a side note, this is the current `strvec_pop()`:

    void strvec_pop(struct strvec *array)
    {
    	if (!array->nr)
    		return;
    	free((char *)array->v[array->nr - 1]);
    	array->v[array->nr - 1] = NULL;
    	array->nr--;
    }

    So, with `strvec_pop()` an instance can become empty but it does
    not going to be the an "empty-singleton".

This "empty-singleton" circumstance requires us to be careful when
adding elements to instances.  Specifically, when adding the first
element:  when we detach the strvec instance from the singleton and
set the internal pointer in the instance to NULL.  After this point we
apply `realloc()` on the pointer.  We do this in
`strvec_push_nodup()`, for example.

The recently introduced `strvec_splice()` API is expected to be
normally used with non-empty strvec's.  However, it can also end up
being used with "empty-singleton" strvec's:

       struct strvec arr = STRVEC_INIT;
       int a = 0, b = 0;

       ... no modification to arr, a or b ...

       const char *rep[] = { "foo" };
       strvec_splice(&arr, a, b, rep, ARRAY_SIZE(rep));

So, we'll try to add elements to an "empty-singleton" strvec instance.

Avoid misapplying `realloc()` to the singleton in `strvec_splice()` by
adding a special case for strvec's initialized with the singleton.

Signed-off-by: Rubén Justo <rjusto@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Rubén Justo
2024-12-04 23:44:25 +01:00
committed by Junio C Hamano
parent 60c778d172
commit 14ef8c04c5
2 changed files with 17 additions and 4 deletions

View File

@ -61,16 +61,19 @@ void strvec_splice(struct strvec *array, size_t idx, size_t len,
{
if (idx + len > array->nr)
BUG("range outside of array boundary");
if (replacement_len > len)
if (replacement_len > len) {
if (array->v == empty_strvec)
array->v = NULL;
ALLOC_GROW(array->v, array->nr + (replacement_len - len) + 1,
array->alloc);
array->v[array->nr + (replacement_len - len)] = NULL;
}
for (size_t i = 0; i < len; i++)
free((char *)array->v[idx + i]);
if (replacement_len != len) {
if ((replacement_len != len) && array->nr)
memmove(array->v + idx + replacement_len, array->v + idx + len,
(array->nr - idx - len + 1) * sizeof(char *));
array->nr += (replacement_len - len);
}
array->nr += replacement_len - len;
for (size_t i = 0; i < replacement_len; i++)
array->v[idx + i] = xstrdup(replacement[i]);
}

View File

@ -88,6 +88,16 @@ void test_strvec__pushv(void)
strvec_clear(&vec);
}
void test_strvec__splice_just_initialized_strvec(void)
{
struct strvec vec = STRVEC_INIT;
const char *replacement[] = { "foo" };
strvec_splice(&vec, 0, 0, replacement, ARRAY_SIZE(replacement));
check_strvec(&vec, "foo", NULL);
strvec_clear(&vec);
}
void test_strvec__splice_with_same_size_replacement(void)
{
struct strvec vec = STRVEC_INIT;