On Wed, Apr 30, 2025 at 7:54 AM Ales Musil <amu...@redhat.com> wrote:

> The aim of the structure is to reduce certain type of allocations
> and help with the cache consistency. There are places in the codebase
> that essentially do vectoring just raw with x2nrealloc() calls. The
> second category are computations that use struct ovs_list to achieve
> array like iterations.
>
> This is preparation with the structure testing, the real replacement
> is done later on.
>
> Signed-off-by: Ales Musil <amu...@redhat.com>
> Acked-by: Mark Michelson <mmich...@redhat.com>
> ---
> v4: Rebase on top of current main.
>     Add ack from Mark.
> v3: Rebase on top of current main.
>     Add more comments.
> v2: Rebase on top of current main.
> ---
>  lib/automake.mk     |   2 +
>  lib/vec.c           | 195 +++++++++++++++++++++++++++
>  lib/vec.h           | 175 ++++++++++++++++++++++++
>  tests/automake.mk   |   1 +
>  tests/ovn.at        |  14 ++
>  tests/test-vector.c | 316 ++++++++++++++++++++++++++++++++++++++++++++
>  6 files changed, 703 insertions(+)
>  create mode 100644 lib/vec.c
>  create mode 100644 lib/vec.h
>  create mode 100644 tests/test-vector.c
>
> diff --git a/lib/automake.mk b/lib/automake.mk
> index 25e516406..a59c722d6 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -45,6 +45,8 @@ lib_libovn_la_SOURCES = \
>         lib/lb.c \
>         lib/lb.h \
>         lib/stopwatch-names.h \
> +       lib/vec.c \
> +       lib/vec.h \
>         lib/vif-plug-provider.h \
>         lib/vif-plug-provider.c \
>         lib/vif-plug-providers/dummy/vif-plug-dummy.c
> diff --git a/lib/vec.c b/lib/vec.c
> new file mode 100644
> index 000000000..97e6b6549
> --- /dev/null
> +++ b/lib/vec.c
> @@ -0,0 +1,195 @@
> +/* Copyright (c) 2025, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include "vec.h"
> +#include "util.h"
> +
> +#define BYTE_SIZE(VEC, N) ((VEC)->esize * (N))
> +
> +static void vector_resize(struct vector *vec, size_t new_capacity);
> +
> +/* Inerts element at index, the content at the index is shifted right.
> + * Returns 'false' if the index is out of bounds. Note that the element
> + * is pointer to the type being stored, e.g. in case of char *
> + * char ** should be passed. */
> +bool
> +vector_insert(struct vector *vec, size_t index, const void *element)
> +{
> +    if (index > vec->len) {
> +        return false;
> +    }
> +
> +    vector_reserve(vec, 1);
> +    uint8_t *dst = (uint8_t *) vec->buffer + BYTE_SIZE(vec, index);
> +    size_t shift_len = vec->len - index;
> +    memmove(dst + vec->esize, dst, BYTE_SIZE(vec, shift_len));
> +    memcpy(dst, element, vec->esize);
> +    vec->len++;
> +
> +    return true;
> +}
> +
> +/* Pushes array of n elements at the end of vector. */
> +void
> +vector_push_array(struct vector *vec, const void *src, size_t n)
> +{
> +    vector_reserve(vec, n);
> +    memcpy((uint8_t *) vec->buffer + BYTE_SIZE(vec, vec->len), src,
> +           BYTE_SIZE(vec, n));
> +    vec->len = vec->len + n;
> +}
> +
> +/* Removes element at index, the argument "element" is populated with the
> + * data. The content after index is shifted left. Returns 'false' if the
> index
> + * is out of bounds. */
> +bool
> +vector_remove(struct vector *vec, size_t index, void *element)
> +{
> +    if (index >= vec->len || vec->len == 0) {
> +        return false;
> +    }
> +
> +    uint8_t *dst = (uint8_t *) vec->buffer + BYTE_SIZE(vec, index);
> +    size_t shift_len = vec->len - index - 1;
> +
> +    if (element) {
> +        memcpy(element, dst, vec->esize);
> +    }
> +
> +    memmove(dst, dst + vec->esize, BYTE_SIZE(vec, shift_len));
> +    vec->len--;
> +
> +    return true;
> +}
> +
> +/* Removes element at index, the argument "element" is populated with the
> + * data. The last element takes place of the element removed, this breaks
> the
> + * original order of insert. Returns 'false' if the index is out of
> bounds. */
> +bool
> +vector_remove_fast(struct vector *vec, size_t index, void *element)
> +{
> +    if (index >= vec->len || vec->len == 0) {
> +        return false;
> +    }
> +
> +    uint8_t *dst = (uint8_t *) vec->buffer + BYTE_SIZE(vec, index);
> +    uint8_t *last = (uint8_t *) vec->buffer + BYTE_SIZE(vec, vec->len -
> 1);
> +
> +    if (element) {
> +        memcpy(element, dst, vec->esize);
> +    }
> +
> +    memcpy(dst, last, vec->esize);
> +    vec->len--;
> +
> +    return true;
> +}
> +
> +/* Removes block of elements between start (inclusive) and end
> (exclusive),
> + * The content at end is shifted left. Returns 'false' if the index
> + * is out of bounds. */
> +bool
> +vector_remove_block(struct vector *vec, size_t start, size_t end)
> +{
> +    if (vec->len == 0) {
> +        return false;
> +    }
> +
> +    if (start >= end) {
> +        return false;
> +    }
> +
> +    if (start >= vec->len || end > vec->len) {
> +        return false;
> +    }
> +
> +    uint8_t *dst = (uint8_t *) vec->buffer + BYTE_SIZE(vec, start);
> +    uint8_t *src = (uint8_t *) vec->buffer + BYTE_SIZE(vec, end);
> +    size_t shift_len = vec->len - end;
> +    memmove(dst, src, BYTE_SIZE(vec, shift_len));
> +    vec->len = vec->len + start - end;
> +
> +    return true;
> +}
> +
> +/* Gets pointer to the item at index. Note that holding pointer across
> inserts
> + * can cause UB. */
> +void *
> +vector_get_ptr(const struct vector *vec, size_t index)
> +{
> +    if (index >= vec->len) {
> +        return NULL;
> +    }
> +
> +    return (uint8_t *) vec->buffer + BYTE_SIZE(vec, index);
> +}
> +
> +/* Reallocates the vector to fit exactly the length if that's not the
> case. */
> +void
> +vector_shrink_to_fit(struct vector *vec)
> +{
> +    if (vec->len == vec->capacity) {
> +        return;
> +    }
> +
> +    vector_resize(vec, vec->len);
> +}
> +
> +/* Clones the vector vec into new one, the content is memcopied. */
> +struct vector
> +vector_clone(struct vector *vec)
> +{
> +    struct vector clone = (struct vector) {
> +        .buffer = NULL,
> +        .esize = vec->esize,
> +        .len = vec->len,
> +        .capacity = vec->capacity,
> +    };
> +
> +    if (vec->len) {
> +        clone.buffer = xmalloc(BYTE_SIZE(vec, vec->capacity));
> +        memcpy(clone.buffer, vec->buffer, BYTE_SIZE(vec, vec->len));
> +    }
> +
> +    return clone;
> +}
> +
> +/* Reserves additional space to fit extra n items. */
> +void
> +vector_reserve(struct vector *vec, size_t n)
> +{
> +    size_t new_len = vec->len + n;
> +    if (new_len <= vec->capacity) {
> +        return;
> +    }
> +
> +    vector_resize(vec, new_len <= 2 * vec->capacity ?
> +                  2 * vec->capacity :
> +                  new_len);
> +}
> +
> +static void
> +vector_resize(struct vector *vec, size_t new_capacity)
> +{
> +    if (!new_capacity) {
> +        vector_destroy(vec);
> +        return;
> +    }
> +
> +    vec->buffer = xrealloc(vec->buffer, BYTE_SIZE(vec, new_capacity));
> +    vec->capacity = new_capacity;
> +}
> diff --git a/lib/vec.h b/lib/vec.h
> new file mode 100644
> index 000000000..5e51357de
> --- /dev/null
> +++ b/lib/vec.h
> @@ -0,0 +1,175 @@
> +/* Copyright (c) 2025, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#ifndef VEC_H
> +#define VEC_H
> +
> +
> +#include <stdbool.h>
> +#include <stddef.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +
> +struct vector {
> +    void *buffer;    /* Data bytes. */
> +    size_t len;         /* Number of elements. */
> +    size_t esize;       /* Size of each element in bytes. */
> +    size_t capacity;    /* Element capacity. */
> +};
> +
> +#define VECTOR_EMPTY_INITIALIZER(TYPE) \
> +    (struct vector) {                  \
> +        .buffer = NULL,                \
> +        .len = 0,                      \
> +        .esize = sizeof(TYPE),         \
> +        .capacity = 0                  \
> +    }
> +
> +#define VECTOR_CAPACITY_INITIALIZER(TYPE, CAP)    \
> +    (struct vector) {                             \
> +        .buffer = xmalloc(sizeof (TYPE) * (CAP)), \
> +        .esize = sizeof(TYPE),                    \
> +        .len = 0,                                 \
> +        .capacity = (CAP),                        \
> +    }
> +
> +/* Note that storing the returned pointer will result in UB, as the vector
> + * might change the memory location during insert. */
> +#define VECTOR_FOR_EACH_PTR(VEC, NODE)
>     \
> +    for (INIT_MULTIVAR(NODE, 0, (VEC)->buffer, OVS_TYPEOF(*NODE));
>     \
> +         (ITER_VAR(NODE) - (OVS_TYPEOF(*NODE) *) (VEC)->buffer <
> (VEC)->len ? \
> +            (((NODE) = ITER_VAR(NODE)), 1) :
>     \
> +            0);
>      \
> +         UPDATE_MULTIVAR(NODE, ITER_VAR(NODE) + 1))
> +
> +/* Note that the iterator copies each element, this is useful for small
> sized
> + * elements like pointers. */
> +#define VECTOR_FOR_EACH(VEC, NODE)
>     \
> +    for (INIT_MULTIVAR(NODE, 0, (VEC)->buffer, OVS_TYPEOF(NODE));
>      \
> +         (ITER_VAR(NODE) - (OVS_TYPEOF(NODE) *) (VEC)->buffer <
> (VEC)->len ?  \
> +            (((NODE) = *ITER_VAR(NODE)), 1) :
>      \
> +            0);
>      \
> +         UPDATE_MULTIVAR(NODE, ITER_VAR(NODE) + 1))
> +
> +/* Note that the returns element is copy of the element stored in the
> + * vector. This is useful for small sized elements like pointers. */
> +#define vector_get(VEC, INDEX, TYPE) \
> +    (*(TYPE *) vector_get_ptr((VEC), (INDEX)))
> +
> +bool vector_insert(struct vector *vec, size_t index, const void *element);
> +void vector_push_array(struct vector *vec, const void *src, size_t n);
> +bool vector_remove(struct vector *vec, size_t index, void *element);
> +bool vector_remove_fast(struct vector *vec, size_t index, void *element);
> +bool vector_remove_block(struct vector *vec, size_t start, size_t end);
> +void *vector_get_ptr(const struct vector *vec, size_t index);
> +void vector_shrink_to_fit(struct vector *vec);
> +struct vector vector_clone(struct vector *vec);
> +void vector_reserve(struct vector *vec, size_t n);
> +
> +/* Pushes element into the end of the vector. */
> +static inline void
> +vector_push(struct vector *vec, const void *element)
> +{
> +    vector_insert(vec, vec->len, element);
> +}
> +
> +/* Pops element from the end, the argument "element" is populated
> + * with the data. */
> +static inline void
> +vector_pop(struct vector *vec, void *element)
> +{
> +    vector_remove(vec, vec->len - 1, element);
> +}
> +
> +/* Clears the vector without deallocating the buffer. */
> +static inline void
> +vector_clear(struct vector *vec)
> +{
> +    vec->len = 0;
> +}
> +
> +/* Initializes the vector as empty. */
> +static inline void
> +vector_init(struct vector *vec)
> +{
> +    vec->len = 0;
> +    vec->capacity = 0;
> +    vec->buffer = NULL;
> +}
> +
> +/* Destroys the vector content. It doesn't free individual elements,
> that's up
> + * to the caller. */
> +static inline void
> +vector_destroy(struct vector *vec)
> +{
> +    free(vec->buffer);
> +    vector_init(vec);
> +}
> +
> +/* Returns the length in number of elements. */
> +static inline size_t
> +vector_len(const struct vector *vec)
> +{
> +    return vec->len;
> +}
> +
> +/* Returns the capacity in number of elements. */
> +static inline size_t
> +vector_capacity(const struct vector *vec)
> +{
> +    return vec->capacity;
> +}
> +
> +/* Return true if vector is empty. */
> +static inline bool
> +vector_is_empty(const struct vector * vec)
> +{
> +    return vec->len == 0;
> +}
> +
> +/* Quick sort of all elements in the vector. */
> +static inline void
> +vector_qsort(struct vector *vec, int (*cmp)(const void *a, const void *b))
> +{
> +    if (vec->len) {
> +        qsort(vec->buffer, vec->len, vec->esize, cmp);
> +    }
> +}
> +
> +/* Returns the size of allocated space for the vector elements in bytes.
> */
> +static inline size_t
> +vector_memory_usage(struct vector *vec)
> +{
> +    return vec->capacity * vec->esize;
> +}
> +
> +/* Returns the array pointer. */
> +static inline void *
> +vector_get_array(const struct vector *vec)
> +{
> +    return vec->buffer;
> +}
> +
> +/* Returns the array pointer, the vector is re-initialized. It is up to
> caller
> + * to free the array. */
> +static inline void *
> +vector_steal_array(struct vector *vec)
> +{
> +    void *buffer = vec->buffer;
> +    vector_init(vec);
> +    return buffer;
> +}
> +
> +#endif /* lib/vec.h */
> diff --git a/tests/automake.mk b/tests/automake.mk
> index fbcd4a848..b96932dc2 100644
> --- a/tests/automake.mk
> +++ b/tests/automake.mk
> @@ -284,6 +284,7 @@ tests_ovstest_SOURCES = \
>         tests/test-utils.c \
>         tests/test-utils.h \
>         tests/test-ovn.c \
> +       tests/test-vector.c \
>         controller/test-lflow-cache.c \
>         controller/test-lflow-conj-ids.c \
>         controller/test-ofctrl-seqno.c \
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 335425532..596a7be2b 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -2334,6 +2334,20 @@ cp test-cases.txt expout
>  AT_CHECK([ovstest test-ovn parse-actions < input.txt], [0], [expout])
>  AT_CLEANUP
>
> +AT_SETUP([Vector operations])
> +check ovstest test-vector add
> +
> +check ovstest test-vector remove
> +
> +check ovstest test-vector out-of-bounds
> +
> +check ovstest test-vector shrink
> +
> +check ovstest test-vector clone
> +
> +check ovstest test-vector pointers
> +AT_CLEANUP
> +
>  AT_BANNER([OVN end-to-end tests])
>
>  OVN_FOR_EACH_NORTHD([
> diff --git a/tests/test-vector.c b/tests/test-vector.c
> new file mode 100644
> index 000000000..f2bad0c33
> --- /dev/null
> +++ b/tests/test-vector.c
> @@ -0,0 +1,316 @@
> +/* Copyright (c) 2025, Red Hat, Inc.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
> implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include <stdint.h>
> +#include <string.h>
> +
> +#include "lib/vec.h"
> +#include "tests/ovstest.h"
> +#include "lib/ovn-util.h"
> +
> +static void
> +test_add(struct ovs_cmdl_context *ctx OVS_UNUSED)
> +{
> +    uint32_t elements[5] = {0, 1, 2, 3, 4};
> +
> +    struct vector vec = VECTOR_EMPTY_INITIALIZER(uint32_t);
> +    ovs_assert(vector_capacity(&vec) == 0);
> +    ovs_assert(vector_len(&vec) == 0);
> +
> +    vector_push(&vec, &elements[1]);
> +    ovs_assert(vector_capacity(&vec) == 1);
> +    ovs_assert(vector_len(&vec) == 1);
> +    ovs_assert(vector_get(&vec, 0, uint32_t) == elements[1]);
> +
> +    vector_push(&vec, &elements[2]);
> +    ovs_assert(vector_capacity(&vec) == 2);
> +    ovs_assert(vector_len(&vec) == 2);
> +    ovs_assert(vector_get(&vec, 1, uint32_t) == elements[2]);
> +
> +    vector_push(&vec, &elements[3]);
> +    ovs_assert(vector_capacity(&vec) == 4);
> +    ovs_assert(vector_len(&vec) == 3);
> +    ovs_assert(vector_get(&vec, 2, uint32_t) == elements[3]);
> +
> +    vector_push(&vec, &elements[4]);
> +    ovs_assert(vector_capacity(&vec) == 4);
> +    ovs_assert(vector_len(&vec) == 4);
> +    ovs_assert(vector_get(&vec, 3, uint32_t) == elements[4]);
> +
> +    ovs_assert(vector_insert(&vec, 0, &elements[0]));
> +    ovs_assert(vector_capacity(&vec) == 8);
> +    ovs_assert(vector_len(&vec) == 5);
> +    ovs_assert(vector_get(&vec, 0, uint32_t) == elements[0]);
> +
> +    size_t i = 0;
> +    uint32_t *ptr;
> +    VECTOR_FOR_EACH_PTR (&vec, ptr) {
> +        ovs_assert(elements[i++] == *ptr);
> +    }
> +    ovs_assert(i == ARRAY_SIZE(elements));
> +
> +    i = 0;
> +    uint32_t num;
> +    VECTOR_FOR_EACH (&vec, num) {
> +        ovs_assert(elements[i++] == num);
> +    }
> +    ovs_assert(i == ARRAY_SIZE(elements));
> +
> +    vector_destroy(&vec);
> +
> +    vector_push_array(&vec, elements, 4);
> +    ovs_assert(vector_capacity(&vec) == 4);
> +    ovs_assert(vector_len(&vec) == 4);
> +
> +    i = 0;
> +    VECTOR_FOR_EACH (&vec, num) {
> +        ovs_assert(elements[i++] == num);
> +    }
> +    ovs_assert(i == 4);
> +
> +    vector_push(&vec, &elements[4]);
> +    ovs_assert(vector_capacity(&vec) == 8);
> +    ovs_assert(vector_len(&vec) == 5);
> +
> +    i = 0;
> +    VECTOR_FOR_EACH (&vec, num) {
> +        ovs_assert(elements[i++] == num);
> +    }
> +    ovs_assert(i == ARRAY_SIZE(elements));
> +
> +    vector_destroy(&vec);
> +}
> +
> +static void
> +test_remove(struct ovs_cmdl_context *ctx OVS_UNUSED)
> +{
> +    uint32_t elements[3] = {0, 1, 2};
> +    struct vector vec = VECTOR_EMPTY_INITIALIZER(uint32_t);
> +    vector_push_array(&vec, elements, ARRAY_SIZE(elements));
> +
> +    uint32_t element;
> +    vector_pop(&vec, &element);
> +    ovs_assert(vector_len(&vec) == 2);
> +    ovs_assert(element == 2);
> +
> +    ovs_assert(vector_remove(&vec, 0, &element));
> +    ovs_assert(vector_len(&vec) == 1);
> +    ovs_assert(element == 0);
> +    ovs_assert(vector_get(&vec, 0, uint32_t) == 1);
> +
> +    size_t j = 0;
> +    uint32_t num;
> +    VECTOR_FOR_EACH (&vec, num) {
> +        ovs_assert(num == 1);
> +        j++;
> +    }
> +    ovs_assert(j == 1);
> +
> +    ovs_assert(vector_remove(&vec, 0, &element));
> +    ovs_assert(vector_len(&vec) == 0);
> +    ovs_assert(element == 1);
> +
> +    vector_push_array(&vec, elements, ARRAY_SIZE(elements));
> +
> +    ovs_assert(vector_remove_fast(&vec, 0, &element));
> +    ovs_assert(vector_len(&vec) == 2);
> +    ovs_assert(element == 0);
> +
> +    uint32_t out_of_order[2] = {2, 1};
> +    j = 0;
> +    VECTOR_FOR_EACH (&vec, num) {
> +        ovs_assert(out_of_order[j] == num);
> +        j++;
> +    }
> +    ovs_assert(j == ARRAY_SIZE(out_of_order));
> +
> +    vector_destroy(&vec);
> +
> +    uint32_t elements2[5] = {0, 1, 2, 3, 4};
> +    vector_push_array(&vec, elements2, ARRAY_SIZE(elements2));
> +    ovs_assert(!vector_remove_block(&vec, 1, 1));
> +
> +    ovs_assert(vector_remove_block(&vec, 1, 3));
> +    ovs_assert(vector_len(&vec) == 3);
> +
> +    uint32_t block1[3] = {0, 3 ,4};
> +    j = 0;
> +    VECTOR_FOR_EACH (&vec, num) {
> +        ovs_assert(block1[j] == num);
> +        j++;
> +    }
> +    ovs_assert(j == ARRAY_SIZE(block1));
> +
> +    ovs_assert(vector_remove_block(&vec, 0, 2));
> +    ovs_assert(vector_len(&vec) == 1);
> +    ovs_assert(vector_get(&vec, 0, uint32_t) == 4);
> +
> +    vector_destroy(&vec);
> +}
> +
> +static void
> +test_out_of_bounds(struct ovs_cmdl_context *ctx OVS_UNUSED)
> +{
> +    uint32_t num = 1;
> +    struct vector vec = VECTOR_EMPTY_INITIALIZER(uint32_t);
> +
> +    ovs_assert(!vector_insert(&vec, 1, &num));
> +    ovs_assert(vector_capacity(&vec) == 0);
> +    ovs_assert(vector_len(&vec) == 0);
> +
> +    ovs_assert(!vector_remove(&vec, 0, &num));
> +    ovs_assert(vector_capacity(&vec) == 0);
> +    ovs_assert(vector_len(&vec) == 0);
> +
> +    uint32_t *ptr;
> +    VECTOR_FOR_EACH_PTR (&vec, ptr) {
> +        OVS_NOT_REACHED();
> +    }
> +
> +    VECTOR_FOR_EACH (&vec, num) {
> +        OVS_NOT_REACHED();
> +    }
> +
> +    vector_destroy(&vec);
> +}
> +
> +static void
> +test_shrink(struct ovs_cmdl_context *ctx OVS_UNUSED)
> +{
> +    uint32_t elements[3] = {0, 1, 2};
> +    struct vector vec = VECTOR_CAPACITY_INITIALIZER(uint32_t, 10);
> +    vector_push_array(&vec, elements, ARRAY_SIZE(elements));
> +
> +    ovs_assert(vector_capacity(&vec) == 10);
> +    ovs_assert(vector_len(&vec) == 3);
> +
> +    vector_shrink_to_fit(&vec);
> +    ovs_assert(vector_capacity(&vec) == 3);
> +    ovs_assert(vector_len(&vec) == 3);
> +
> +    uint32_t num;
> +    vector_pop(&vec, &num);
> +    ovs_assert(vector_capacity(&vec) == 3);
> +    ovs_assert(vector_len(&vec) == 2);
> +
> +    vector_shrink_to_fit(&vec);
> +    ovs_assert(vector_capacity(&vec) == 2);
> +    ovs_assert(vector_len(&vec) == 2);
> +
> +    vector_push(&vec, &num);
> +    ovs_assert(vector_capacity(&vec) == 4);
> +    ovs_assert(vector_len(&vec) == 3);
> +
> +    vector_clear(&vec);
> +    ovs_assert(vector_capacity(&vec) == 4);
> +    ovs_assert(vector_len(&vec) == 0);
> +
> +    vector_shrink_to_fit(&vec);
> +    ovs_assert(vector_capacity(&vec) == 0);
> +    ovs_assert(vector_len(&vec) == 0);
> +
> +    vector_destroy(&vec);
> +}
> +
> +static void
> +test_clone(struct ovs_cmdl_context *ctx OVS_UNUSED)
> +{
> +    uint32_t elements[3] = {0, 1, 2};
> +    struct vector vec = VECTOR_CAPACITY_INITIALIZER(uint32_t, 10);
> +    vector_push_array(&vec, elements, ARRAY_SIZE(elements));
> +
> +    struct vector clone = vector_clone(&vec);
> +    ovs_assert(vector_capacity(&vec) == 10);
> +    ovs_assert(vector_len(&vec) == 3);
> +
> +    size_t i = 0;
> +    uint32_t num;
> +    VECTOR_FOR_EACH (&clone, num) {
> +        ovs_assert(elements[i++] == num);
> +    }
> +    ovs_assert(i == ARRAY_SIZE(elements));
> +
> +    vector_shrink_to_fit(&clone);
> +    ovs_assert(vector_capacity(&clone) == 3);
> +    ovs_assert(vector_len(&clone) == 3);
> +    ovs_assert(vector_capacity(&vec) == 10);
> +    ovs_assert(vector_len(&vec) == 3);
> +
> +    vector_destroy(&vec);
> +    vector_destroy(&clone);
> +}
> +
> +static void
> +test_pointers(struct ovs_cmdl_context *ctx OVS_UNUSED)
> +{
> +    const char *elements[3] = {"a", "b", "c"};
> +    struct vector vec = VECTOR_EMPTY_INITIALIZER(const char *);
> +    vector_push_array(&vec, elements, ARRAY_SIZE(elements));
> +
> +    size_t i = 0;
> +    const char **ptr;
> +    VECTOR_FOR_EACH_PTR (&vec, ptr) {
> +        ovs_assert(!strcmp(elements[i++], *ptr));
> +    }
> +    ovs_assert(i == ARRAY_SIZE(elements));
> +
> +    i = 0;
> +    const char *str;
> +    VECTOR_FOR_EACH (&vec, str) {
> +        ovs_assert(!strcmp(elements[i++], str));
> +    }
> +    ovs_assert(i == ARRAY_SIZE(elements));
> +
> +    vector_destroy(&vec);
> +
> +    vec = VECTOR_EMPTY_INITIALIZER(char *);
> +    for (size_t j = 0; j < ARRAY_SIZE(elements); j++) {
> +        char *dup = xstrdup(elements[j]);
> +        vector_push(&vec, &dup);
> +    }
> +
> +    char *string;
> +    i = 0;
> +    VECTOR_FOR_EACH (&vec, string) {
> +        free(string);
> +        i++;
> +    }
> +    ovs_assert(i == ARRAY_SIZE(elements));
> +
> +    vector_destroy(&vec);
> +}
> +
> +static void
> +test_vector_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
> +{
> +    ovn_set_program_name(argv[0]);
> +    static const struct ovs_cmdl_command commands[] = {
> +        {"add", NULL, 0, 0, test_add, OVS_RO},
> +        {"remove", NULL, 0, 0, test_remove, OVS_RO},
> +        {"out-of-bounds", NULL, 0, 0, test_out_of_bounds, OVS_RO},
> +        {"shrink", NULL, 0, 0, test_shrink, OVS_RO},
> +        {"clone", NULL, 0, 0, test_clone, OVS_RO},
> +        {"pointers", NULL, 0, 0, test_pointers, OVS_RO},
> +        {NULL, NULL, 0, 0, NULL, OVS_RO},
> +    };
> +    struct ovs_cmdl_context ctx;
> +    ctx.argc = argc - 1;
> +    ctx.argv = argv + 1;
> +    ovs_cmdl_run_command(&ctx, commands);
> +}
> +
> +OVSTEST_REGISTER("test-vector", test_vector_main);
> --
> 2.49.0
>
>
Recheck-request: github-robot
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to