Add sc_vector
Adapt vlc_vector [1], that I initially wrote while implementing the VLC
playlist [2].
Change the implementation to use "statement expressions" [3], which are
forbidden in VLC because "non-standard", but:
- they are supported by gcc and clang;
- they are already used in the scrcpy codebase;
- they avoid implementation hacks (VLC_VECTOR_FAILFLAG_);
- they allow a better API (sc_vector_index_of() may return the result
without an output parameter).
PR #3035 <https://github.com/Genymobile/scrcpy/pull/3035>
[1]: 0857947aba/include/vlc_vector.h
[2]: https://blog.rom1v.com/2019/05/a-new-core-playlist-for-vlc-4
[3]: https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
This commit is contained in:
parent
36c75e15b8
commit
c070723bc8
3 changed files with 963 additions and 0 deletions
|
@ -282,6 +282,9 @@ if get_option('buildtype') == 'debug'
|
||||||
'src/util/str.c',
|
'src/util/str.c',
|
||||||
'src/util/strbuf.c',
|
'src/util/strbuf.c',
|
||||||
]],
|
]],
|
||||||
|
['test_vector', [
|
||||||
|
'tests/test_vector.c',
|
||||||
|
]],
|
||||||
]
|
]
|
||||||
|
|
||||||
foreach t : tests
|
foreach t : tests
|
||||||
|
|
539
app/src/util/vector.h
Normal file
539
app/src/util/vector.h
Normal file
|
@ -0,0 +1,539 @@
|
||||||
|
#ifndef SC_VECTOR_H
|
||||||
|
#define SC_VECTOR_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// Adapted from vlc_vector:
|
||||||
|
// <https://code.videolan.org/videolan/vlc/-/blob/0857947abaed9c89810cd96353aaa1b7e6ba3b0d/include/vlc_vector.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vector struct body
|
||||||
|
*
|
||||||
|
* A vector is a dynamic array, managed by the sc_vector_* helpers.
|
||||||
|
*
|
||||||
|
* It is generic over the type of its items, so it is implemented as macros.
|
||||||
|
*
|
||||||
|
* To use a vector, a new type must be defined:
|
||||||
|
*
|
||||||
|
* struct vec_int SC_VECTOR(int);
|
||||||
|
*
|
||||||
|
* The struct may be anonymous:
|
||||||
|
*
|
||||||
|
* struct SC_VECTOR(const char *) names;
|
||||||
|
*
|
||||||
|
* Vector size is accessible via `vec.size`, and items are intended to be
|
||||||
|
* accessed directly, via `vec.data[i]`.
|
||||||
|
*
|
||||||
|
* Functions and macros having name ending with '_' are private.
|
||||||
|
*/
|
||||||
|
#define SC_VECTOR(type) \
|
||||||
|
{ \
|
||||||
|
size_t cap; \
|
||||||
|
size_t size; \
|
||||||
|
type *data; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static initializer for a vector
|
||||||
|
*/
|
||||||
|
#define SC_VECTOR_INITIALIZER { 0, 0, NULL }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize an empty vector
|
||||||
|
*/
|
||||||
|
#define sc_vector_init(pv) \
|
||||||
|
({ \
|
||||||
|
(pv)->cap = 0; \
|
||||||
|
(pv)->size = 0; \
|
||||||
|
(pv)->data = NULL; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a vector
|
||||||
|
*
|
||||||
|
* The vector may not be used anymore unless sc_vector_init() is called.
|
||||||
|
*/
|
||||||
|
#define sc_vector_destroy(pv) \
|
||||||
|
free((pv)->data)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a vector
|
||||||
|
*
|
||||||
|
* Remove all items from the vector.
|
||||||
|
*/
|
||||||
|
#define sc_vector_clear(pv) \
|
||||||
|
({ \
|
||||||
|
sc_vector_destroy(pv); \
|
||||||
|
sc_vector_init(pv);\
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The minimal allocation size, in number of items
|
||||||
|
*
|
||||||
|
* Private.
|
||||||
|
*/
|
||||||
|
#define SC_VECTOR_MINCAP_ ((size_t) 10)
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
sc_vector_min_(size_t a, size_t b)
|
||||||
|
{
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
sc_vector_max_(size_t a, size_t b)
|
||||||
|
{
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
sc_vector_clamp_(size_t x, size_t min, size_t max)
|
||||||
|
{
|
||||||
|
return sc_vector_max_(min, sc_vector_min_(max, x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realloc data and update vector fields
|
||||||
|
*
|
||||||
|
* On reallocation success, update the vector capacity (*pcap) and size
|
||||||
|
* (*psize), and return the reallocated data.
|
||||||
|
*
|
||||||
|
* On reallocation failure, return NULL without any change.
|
||||||
|
*
|
||||||
|
* Private.
|
||||||
|
*
|
||||||
|
* \param ptr the current `data` field of the vector to realloc
|
||||||
|
* \param count the requested capacity, in number of items
|
||||||
|
* \param size the size of one item
|
||||||
|
* \param pcap a pointer to the `cap` field of the vector [IN/OUT]
|
||||||
|
* \param psize a pointer to the `size` field of the vector [IN/OUT]
|
||||||
|
* \return the new ptr on success, NULL on error
|
||||||
|
*/
|
||||||
|
static inline void *
|
||||||
|
sc_vector_reallocdata_(void *ptr, size_t count, size_t size,
|
||||||
|
size_t *restrict pcap, size_t *restrict psize)
|
||||||
|
{
|
||||||
|
void *p = realloc(ptr, count * size);
|
||||||
|
if (!p) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*pcap = count;
|
||||||
|
*psize = sc_vector_min_(*psize, count);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define sc_vector_realloc_(pv, newcap) \
|
||||||
|
({ \
|
||||||
|
void *p = sc_vector_reallocdata_((pv)->data, newcap, sizeof(*(pv)->data), \
|
||||||
|
&(pv)->cap, &(pv)->size); \
|
||||||
|
if (p) { \
|
||||||
|
(pv)->data = p; \
|
||||||
|
} \
|
||||||
|
(bool) p; \
|
||||||
|
});
|
||||||
|
|
||||||
|
#define sc_vector_resize_(pv, newcap) \
|
||||||
|
({ \
|
||||||
|
bool ok; \
|
||||||
|
if ((pv)->cap == (newcap)) { \
|
||||||
|
ok = true; \
|
||||||
|
} else if ((newcap) > 0) { \
|
||||||
|
ok = sc_vector_realloc_(pv, (newcap)); \
|
||||||
|
} else { \
|
||||||
|
sc_vector_clear(pv); \
|
||||||
|
ok = true; \
|
||||||
|
} \
|
||||||
|
ok; \
|
||||||
|
})
|
||||||
|
|
||||||
|
static inline size_t
|
||||||
|
sc_vector_growsize_(size_t value)
|
||||||
|
{
|
||||||
|
/* integer multiplication by 1.5 */
|
||||||
|
return value + (value >> 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SIZE_MAX/2 to fit in ssize_t, and so that cap*1.5 does not overflow. */
|
||||||
|
#define sc_vector_max_cap_(pv) (SIZE_MAX / 2 / sizeof(*(pv)->data))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase the capacity of the vector to at least `mincap`
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param mincap (size_t) the requested capacity
|
||||||
|
* \retval true if no allocation failed
|
||||||
|
* \retval false on allocation failure (the vector is left untouched)
|
||||||
|
*/
|
||||||
|
#define sc_vector_reserve(pv, mincap) \
|
||||||
|
({ \
|
||||||
|
bool ok; \
|
||||||
|
/* avoid to allocate tiny arrays (< SC_VECTOR_MINCAP_) */ \
|
||||||
|
size_t mincap_ = sc_vector_max_(mincap, SC_VECTOR_MINCAP_); \
|
||||||
|
if (mincap_ <= (pv)->cap) { \
|
||||||
|
/* nothing to do */ \
|
||||||
|
ok = true; \
|
||||||
|
} else if (mincap_ <= sc_vector_max_cap_(pv)) { \
|
||||||
|
/* not too big */ \
|
||||||
|
size_t newsize = sc_vector_growsize_((pv)->cap); \
|
||||||
|
newsize = sc_vector_clamp_(newsize, mincap_, sc_vector_max_cap_(pv)); \
|
||||||
|
ok = sc_vector_realloc_(pv, newsize); \
|
||||||
|
} else { \
|
||||||
|
ok = false; \
|
||||||
|
} \
|
||||||
|
ok; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define sc_vector_shrink_to_fit(pv) \
|
||||||
|
/* decreasing the size may not fail */ \
|
||||||
|
(void) sc_vector_resize_(pv, (pv)->size)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the vector down automatically
|
||||||
|
*
|
||||||
|
* Shrink only when necessary (in practice when cap > (size+5)*1.5)
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
*/
|
||||||
|
#define sc_vector_autoshrink(pv) \
|
||||||
|
({ \
|
||||||
|
bool must_shrink = \
|
||||||
|
/* do not shrink to tiny size */ \
|
||||||
|
(pv)->cap > SC_VECTOR_MINCAP_ && \
|
||||||
|
/* no need to shrink */ \
|
||||||
|
(pv)->cap >= sc_vector_growsize_((pv)->size + 5); \
|
||||||
|
if (must_shrink) { \
|
||||||
|
size_t newsize = sc_vector_max_((pv)->size + 5, SC_VECTOR_MINCAP_); \
|
||||||
|
sc_vector_resize_(pv, newsize); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define sc_vector_check_same_ptr_type_(a, b) \
|
||||||
|
(void) ((a) == (b)) /* warn on type mismatch */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Push an item at the end of the vector
|
||||||
|
*
|
||||||
|
* The amortized complexity is O(1).
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param item the item to append
|
||||||
|
* \retval true if no allocation failed
|
||||||
|
* \retval false on allocation failure (the vector is left untouched)
|
||||||
|
*/
|
||||||
|
#define sc_vector_push(pv, item) \
|
||||||
|
({ \
|
||||||
|
bool ok = sc_vector_reserve(pv, (pv)->size + 1); \
|
||||||
|
if (ok) { \
|
||||||
|
(pv)->data[(pv)->size++] = (item); \
|
||||||
|
} \
|
||||||
|
ok; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append `count` items at the end of the vector
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param items the items array to append
|
||||||
|
* \param count the number of items in the array
|
||||||
|
* \retval true if no allocation failed
|
||||||
|
* \retval false on allocation failure (the vector is left untouched)
|
||||||
|
*/
|
||||||
|
#define sc_vector_push_all(pv, items, count) \
|
||||||
|
sc_vector_push_all_(pv, items, (size_t) count)
|
||||||
|
|
||||||
|
#define sc_vector_push_all_(pv, items, count) \
|
||||||
|
({ \
|
||||||
|
sc_vector_check_same_ptr_type_((pv)->data, items); \
|
||||||
|
bool ok = sc_vector_reserve(pv, (pv)->size + (count)); \
|
||||||
|
if (ok) { \
|
||||||
|
memcpy(&(pv)->data[(pv)->size], items, (count) * sizeof(*(pv)->data)); \
|
||||||
|
(pv)->size += count; \
|
||||||
|
} \
|
||||||
|
ok; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an hole of size `count` to the given index
|
||||||
|
*
|
||||||
|
* The items in range [index; size-1] will be moved. The items in the hole are
|
||||||
|
* left uninitialized.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index where the hole is to be inserted
|
||||||
|
* \param count the number of items in the hole
|
||||||
|
* \retval true if no allocation failed
|
||||||
|
* \retval false on allocation failure (the vector is left untouched)
|
||||||
|
*/
|
||||||
|
#define sc_vector_insert_hole(pv, index, count) \
|
||||||
|
sc_vector_insert_hole_(pv, (size_t) index, (size_t) count);
|
||||||
|
|
||||||
|
#define sc_vector_insert_hole_(pv, index, count) \
|
||||||
|
({ \
|
||||||
|
bool ok = sc_vector_reserve(pv, (pv)->size + (count)); \
|
||||||
|
if (ok) { \
|
||||||
|
if ((index) < (pv)->size) { \
|
||||||
|
memmove(&(pv)->data[(index) + (count)], \
|
||||||
|
&(pv)->data[(index)], \
|
||||||
|
((pv)->size - (index)) * sizeof(*(pv)->data)); \
|
||||||
|
} \
|
||||||
|
(pv)->size += count; \
|
||||||
|
} \
|
||||||
|
ok; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an item at the given index
|
||||||
|
*
|
||||||
|
* The items in range [index; size-1] will be moved.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index where the item is to be inserted
|
||||||
|
* \param item the item to append
|
||||||
|
* \retval true if no allocation failed
|
||||||
|
* \retval false on allocation failure (the vector is left untouched)
|
||||||
|
*/
|
||||||
|
#define sc_vector_insert(pv, index, item) \
|
||||||
|
sc_vector_insert_(pv, (size_t) index, (size_t) item);
|
||||||
|
|
||||||
|
#define sc_vector_insert_(pv, index, item) \
|
||||||
|
({ \
|
||||||
|
bool ok = sc_vector_insert_hole_(pv, index, 1); \
|
||||||
|
if (ok) { \
|
||||||
|
(pv)->data[index] = (item); \
|
||||||
|
} \
|
||||||
|
ok; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert `count` items at the given index
|
||||||
|
*
|
||||||
|
* The items in range [index; size-1] will be moved.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index where the items are to be inserted
|
||||||
|
* \param items the items array to append
|
||||||
|
* \param count the number of items in the array
|
||||||
|
* \retval true if no allocation failed
|
||||||
|
* \retval false on allocation failure (the vector is left untouched)
|
||||||
|
*/
|
||||||
|
#define sc_vector_insert_all(pv, index, items, count) \
|
||||||
|
sc_vector_insert_all_(pv, (size_t) index, items, (size_t) count)
|
||||||
|
|
||||||
|
#define sc_vector_insert_all_(pv, index, items, count) \
|
||||||
|
({ \
|
||||||
|
sc_vector_check_same_ptr_type_((pv)->data, items); \
|
||||||
|
bool ok = sc_vector_insert_hole_(pv, index, count); \
|
||||||
|
if (ok) { \
|
||||||
|
memcpy(&(pv)->data[index], items, count * sizeof(*(pv)->data)); \
|
||||||
|
} \
|
||||||
|
ok; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/** Reverse a char array in place */
|
||||||
|
static inline void
|
||||||
|
sc_char_array_reverse(char *array, size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < len / 2; ++i)
|
||||||
|
{
|
||||||
|
char c = array[i];
|
||||||
|
array[i] = array[len - i - 1];
|
||||||
|
array[len - i - 1] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right-rotate a (char) array in place
|
||||||
|
*
|
||||||
|
* For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
|
||||||
|
* distance 4 will result in {5, 6, 1, 2, 3, 4}.
|
||||||
|
*
|
||||||
|
* Private.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
sc_char_array_rotate_left(char *array, size_t len, size_t distance)
|
||||||
|
{
|
||||||
|
sc_char_array_reverse(array, distance);
|
||||||
|
sc_char_array_reverse(&array[distance], len - distance);
|
||||||
|
sc_char_array_reverse(array, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Right-rotate a (char) array in place
|
||||||
|
*
|
||||||
|
* For example, left-rotating a char array containing {1, 2, 3, 4, 5, 6} with
|
||||||
|
* distance 2 will result in {5, 6, 1, 2, 3, 4}.
|
||||||
|
*
|
||||||
|
* Private.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
sc_char_array_rotate_right(char *array, size_t len, size_t distance)
|
||||||
|
{
|
||||||
|
sc_char_array_rotate_left(array, len, len - distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move items in a (char) array in place
|
||||||
|
*
|
||||||
|
* Move slice [index, count] to target.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
sc_char_array_move(char *array, size_t idx, size_t count, size_t target)
|
||||||
|
{
|
||||||
|
if (idx < target) {
|
||||||
|
sc_char_array_rotate_left(&array[idx], target - idx + count, count);
|
||||||
|
} else {
|
||||||
|
sc_char_array_rotate_right(&array[target], idx - target + count, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move a slice of items to a given target index
|
||||||
|
*
|
||||||
|
* The items in range [index; count] will be moved so that the *new* position
|
||||||
|
* of the first item is `target`.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index of the first item to move
|
||||||
|
* \param count the number of items to move
|
||||||
|
* \param target the new index of the moved slice
|
||||||
|
*/
|
||||||
|
#define sc_vector_move_slice(pv, index, count, target) \
|
||||||
|
sc_vector_move_slice_(pv, (size_t) index, count, (size_t) target);
|
||||||
|
|
||||||
|
#define sc_vector_move_slice_(pv, index, count, target) \
|
||||||
|
({ \
|
||||||
|
sc_char_array_move((char *) (pv)->data, \
|
||||||
|
(index) * sizeof(*(pv)->data), \
|
||||||
|
(count) * sizeof(*(pv)->data), \
|
||||||
|
(target) * sizeof(*(pv)->data)); \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Move an item to a given target index
|
||||||
|
*
|
||||||
|
* The items will be moved so that its *new* position is `target`.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index of the item to move
|
||||||
|
* \param target the new index of the moved item
|
||||||
|
*/
|
||||||
|
#define sc_vector_move(pv, index, target) \
|
||||||
|
sc_vector_move_slice(pv, index, 1, target)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a slice of items, without shrinking the array
|
||||||
|
*
|
||||||
|
* If you have no good reason to use the _noshrink() version, use
|
||||||
|
* sc_vector_remove_slice() instead.
|
||||||
|
*
|
||||||
|
* The items in range [index+count; size-1] will be moved.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index of the first item to remove
|
||||||
|
* \param count the number of items to remove
|
||||||
|
*/
|
||||||
|
#define sc_vector_remove_slice_noshrink(pv, index, count) \
|
||||||
|
sc_vector_remove_slice_noshrink_(pv, (size_t) index, (size_t) count)
|
||||||
|
|
||||||
|
#define sc_vector_remove_slice_noshrink_(pv, index, count) \
|
||||||
|
({ \
|
||||||
|
if ((index) + (count) < (pv)->size) { \
|
||||||
|
memmove(&(pv)->data[index], \
|
||||||
|
&(pv)->data[(index) + (count)], \
|
||||||
|
((pv)->size - (index) - (count)) * sizeof(*(pv)->data)); \
|
||||||
|
} \
|
||||||
|
(pv)->size -= count; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a slice of items
|
||||||
|
*
|
||||||
|
* The items in range [index+count; size-1] will be moved.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index of the first item to remove
|
||||||
|
* \param count the number of items to remove
|
||||||
|
*/
|
||||||
|
#define sc_vector_remove_slice(pv, index, count) \
|
||||||
|
({ \
|
||||||
|
sc_vector_remove_slice_noshrink(pv, index, count); \
|
||||||
|
sc_vector_autoshrink(pv); \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item, without shrinking the array
|
||||||
|
*
|
||||||
|
* If you have no good reason to use the _noshrink() version, use
|
||||||
|
* sc_vector_remove() instead.
|
||||||
|
*
|
||||||
|
* The items in range [index+1; size-1] will be moved.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index of item to remove
|
||||||
|
*/
|
||||||
|
#define sc_vector_remove_noshrink(pv, index) \
|
||||||
|
sc_vector_remove_slice_noshrink(pv, index, 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item
|
||||||
|
*
|
||||||
|
* The items in range [index+1; size-1] will be moved.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index of item to remove
|
||||||
|
*/
|
||||||
|
#define sc_vector_remove(pv, index) \
|
||||||
|
({ \
|
||||||
|
sc_vector_remove_noshrink(pv, index); \
|
||||||
|
sc_vector_autoshrink(pv); \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an item
|
||||||
|
*
|
||||||
|
* The removed item is replaced by the last item of the vector.
|
||||||
|
*
|
||||||
|
* This does not preserve ordering, but is O(1). This is useful when the order
|
||||||
|
* of items is not meaningful.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param index the index of item to remove
|
||||||
|
*/
|
||||||
|
#define sc_vector_swap_remove(pv, index) \
|
||||||
|
sc_vector_swap_remove_(pv, (size_t) index);
|
||||||
|
|
||||||
|
#define sc_vector_swap_remove_(pv, index) \
|
||||||
|
({ \
|
||||||
|
(pv)->data[index] = (pv)->data[(pv)->size-1]; \
|
||||||
|
(pv)->size--; \
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of an item
|
||||||
|
*
|
||||||
|
* Iterate over all items to find a given item.
|
||||||
|
*
|
||||||
|
* Use only for vectors of primitive types or pointers.
|
||||||
|
*
|
||||||
|
* Return the index, or -1 if not found.
|
||||||
|
*
|
||||||
|
* \param pv a pointer to the vector
|
||||||
|
* \param item the item to find (compared with ==)
|
||||||
|
*/
|
||||||
|
#define sc_vector_index_of(pv, item) \
|
||||||
|
({ \
|
||||||
|
ssize_t idx = -1; \
|
||||||
|
for (size_t i = 0; i < (pv)->size; ++i) { \
|
||||||
|
if ((pv)->data[i] == (item)) { \
|
||||||
|
idx = (ssize_t) i; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
idx; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif
|
421
app/tests/test_vector.c
Normal file
421
app/tests/test_vector.c
Normal file
|
@ -0,0 +1,421 @@
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "util/vector.h"
|
||||||
|
|
||||||
|
static void test_vector_insert_remove(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
ok = sc_vector_push(&vec, 42);
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.data[0] == 42);
|
||||||
|
assert(vec.size == 1);
|
||||||
|
|
||||||
|
ok = sc_vector_push(&vec, 37);
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.size == 2);
|
||||||
|
assert(vec.data[0] == 42);
|
||||||
|
assert(vec.data[1] == 37);
|
||||||
|
|
||||||
|
ok = sc_vector_insert(&vec, 1, 100);
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.size == 3);
|
||||||
|
assert(vec.data[0] == 42);
|
||||||
|
assert(vec.data[1] == 100);
|
||||||
|
assert(vec.data[2] == 37);
|
||||||
|
|
||||||
|
ok = sc_vector_push(&vec, 77);
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.size == 4);
|
||||||
|
assert(vec.data[0] == 42);
|
||||||
|
assert(vec.data[1] == 100);
|
||||||
|
assert(vec.data[2] == 37);
|
||||||
|
assert(vec.data[3] == 77);
|
||||||
|
|
||||||
|
sc_vector_remove(&vec, 1);
|
||||||
|
assert(vec.size == 3);
|
||||||
|
assert(vec.data[0] == 42);
|
||||||
|
assert(vec.data[1] == 37);
|
||||||
|
assert(vec.data[2] == 77);
|
||||||
|
|
||||||
|
sc_vector_clear(&vec);
|
||||||
|
assert(vec.size == 0);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_push_array(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
ok = sc_vector_push(&vec, 3); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 14); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 15); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 92); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 65); assert(ok);
|
||||||
|
assert(vec.size == 5);
|
||||||
|
|
||||||
|
int items[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
|
ok = sc_vector_push_all(&vec, items, 8);
|
||||||
|
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.size == 13);
|
||||||
|
assert(vec.data[0] == 3);
|
||||||
|
assert(vec.data[1] == 14);
|
||||||
|
assert(vec.data[2] == 15);
|
||||||
|
assert(vec.data[3] == 92);
|
||||||
|
assert(vec.data[4] == 65);
|
||||||
|
assert(vec.data[5] == 1);
|
||||||
|
assert(vec.data[6] == 2);
|
||||||
|
assert(vec.data[7] == 3);
|
||||||
|
assert(vec.data[8] == 4);
|
||||||
|
assert(vec.data[9] == 5);
|
||||||
|
assert(vec.data[10] == 6);
|
||||||
|
assert(vec.data[11] == 7);
|
||||||
|
assert(vec.data[12] == 8);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_insert_array(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
ok = sc_vector_push(&vec, 3); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 14); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 15); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 92); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 65); assert(ok);
|
||||||
|
assert(vec.size == 5);
|
||||||
|
|
||||||
|
int items[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
|
||||||
|
ok = sc_vector_insert_all(&vec, 3, items, 8);
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.size == 13);
|
||||||
|
assert(vec.data[0] == 3);
|
||||||
|
assert(vec.data[1] == 14);
|
||||||
|
assert(vec.data[2] == 15);
|
||||||
|
assert(vec.data[3] == 1);
|
||||||
|
assert(vec.data[4] == 2);
|
||||||
|
assert(vec.data[5] == 3);
|
||||||
|
assert(vec.data[6] == 4);
|
||||||
|
assert(vec.data[7] == 5);
|
||||||
|
assert(vec.data[8] == 6);
|
||||||
|
assert(vec.data[9] == 7);
|
||||||
|
assert(vec.data[10] == 8);
|
||||||
|
assert(vec.data[11] == 92);
|
||||||
|
assert(vec.data[12] == 65);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_remove_slice(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(vec.size == 100);
|
||||||
|
|
||||||
|
sc_vector_remove_slice(&vec, 32, 60);
|
||||||
|
assert(vec.size == 40);
|
||||||
|
assert(vec.data[31] == 31);
|
||||||
|
assert(vec.data[32] == 92);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_swap_remove(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
ok = sc_vector_push(&vec, 3); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 14); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 15); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 92); assert(ok);
|
||||||
|
ok = sc_vector_push(&vec, 65); assert(ok);
|
||||||
|
assert(vec.size == 5);
|
||||||
|
|
||||||
|
sc_vector_swap_remove(&vec, 1);
|
||||||
|
assert(vec.size == 4);
|
||||||
|
assert(vec.data[0] == 3);
|
||||||
|
assert(vec.data[1] == 65);
|
||||||
|
assert(vec.data[2] == 15);
|
||||||
|
assert(vec.data[3] == 92);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_index_of(void) {
|
||||||
|
struct SC_VECTOR(int) vec;
|
||||||
|
sc_vector_init(&vec);
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t idx;
|
||||||
|
|
||||||
|
idx = sc_vector_index_of(&vec, 0);
|
||||||
|
assert(idx == 0);
|
||||||
|
|
||||||
|
idx = sc_vector_index_of(&vec, 1);
|
||||||
|
assert(idx == 1);
|
||||||
|
|
||||||
|
idx = sc_vector_index_of(&vec, 4);
|
||||||
|
assert(idx == 4);
|
||||||
|
|
||||||
|
idx = sc_vector_index_of(&vec, 9);
|
||||||
|
assert(idx == 9);
|
||||||
|
|
||||||
|
idx = sc_vector_index_of(&vec, 12);
|
||||||
|
assert(idx == -1);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_grow() {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
for (int i = 0; i < 50; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_push(&vec, i); /* append */
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(vec.cap >= 50);
|
||||||
|
assert(vec.size == 50);
|
||||||
|
|
||||||
|
for (int i = 0; i < 25; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_insert(&vec, 20, i); /* insert in the middle */
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(vec.cap >= 75);
|
||||||
|
assert(vec.size == 75);
|
||||||
|
|
||||||
|
for (int i = 0; i < 25; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_insert(&vec, 0, i); /* prepend */
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(vec.cap >= 100);
|
||||||
|
assert(vec.size == 100);
|
||||||
|
|
||||||
|
for (int i = 0; i < 50; ++i)
|
||||||
|
sc_vector_remove(&vec, 20); /* remove from the middle */
|
||||||
|
|
||||||
|
assert(vec.cap >= 50);
|
||||||
|
assert(vec.size == 50);
|
||||||
|
|
||||||
|
for (int i = 0; i < 25; ++i)
|
||||||
|
sc_vector_remove(&vec, 0); /* remove from the head */
|
||||||
|
|
||||||
|
assert(vec.cap >= 25);
|
||||||
|
assert(vec.size == 25);
|
||||||
|
|
||||||
|
for (int i = 24; i >=0; --i)
|
||||||
|
sc_vector_remove(&vec, i); /* remove from the tail */
|
||||||
|
|
||||||
|
assert(vec.size == 0);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_exp_growth(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
size_t oldcap = vec.cap;
|
||||||
|
int realloc_count = 0;
|
||||||
|
bool ok;
|
||||||
|
for (int i = 0; i < 10000; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
if (vec.cap != oldcap)
|
||||||
|
{
|
||||||
|
realloc_count++;
|
||||||
|
oldcap = vec.cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test speciically for an expected growth factor of 1.5. In practice, the
|
||||||
|
* result is even lower (19) due to the first alloc of size 10 */
|
||||||
|
assert(realloc_count <= 23); /* ln(10000) / ln(1.5) ~= 23 */
|
||||||
|
|
||||||
|
realloc_count = 0;
|
||||||
|
for (int i = 9999; i >= 0; --i)
|
||||||
|
{
|
||||||
|
sc_vector_remove(&vec, i);
|
||||||
|
if (vec.cap != oldcap)
|
||||||
|
{
|
||||||
|
realloc_count++;
|
||||||
|
oldcap = vec.cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(realloc_count <= 23); /* same expectations for removals */
|
||||||
|
assert(realloc_count > 0); /* sc_vector_remove() must autoshrink */
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_reserve(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
ok = sc_vector_reserve(&vec, 800);
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.cap >= 800);
|
||||||
|
assert(vec.size == 0);
|
||||||
|
|
||||||
|
size_t initial_cap = vec.cap;
|
||||||
|
|
||||||
|
for (int i = 0; i < 800; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
assert(vec.cap == initial_cap); /* no realloc */
|
||||||
|
}
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_shrink_to_fit(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
ok = sc_vector_reserve(&vec, 800);
|
||||||
|
assert(ok);
|
||||||
|
for (int i = 0; i < 250; ++i)
|
||||||
|
{
|
||||||
|
ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(vec.cap >= 800);
|
||||||
|
assert(vec.size == 250);
|
||||||
|
|
||||||
|
sc_vector_shrink_to_fit(&vec);
|
||||||
|
assert(vec.cap == 250);
|
||||||
|
assert(vec.size == 250);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_move(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; ++i)
|
||||||
|
{
|
||||||
|
bool ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move item at 1 so that its new position is 4 */
|
||||||
|
sc_vector_move(&vec, 1, 4);
|
||||||
|
|
||||||
|
assert(vec.size == 7);
|
||||||
|
assert(vec.data[0] == 0);
|
||||||
|
assert(vec.data[1] == 2);
|
||||||
|
assert(vec.data[2] == 3);
|
||||||
|
assert(vec.data[3] == 4);
|
||||||
|
assert(vec.data[4] == 1);
|
||||||
|
assert(vec.data[5] == 5);
|
||||||
|
assert(vec.data[6] == 6);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_move_slice_forward(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
{
|
||||||
|
bool ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move slice {2, 3, 4, 5} so that its new position is 5 */
|
||||||
|
sc_vector_move_slice(&vec, 2, 4, 5);
|
||||||
|
|
||||||
|
assert(vec.size == 10);
|
||||||
|
assert(vec.data[0] == 0);
|
||||||
|
assert(vec.data[1] == 1);
|
||||||
|
assert(vec.data[2] == 6);
|
||||||
|
assert(vec.data[3] == 7);
|
||||||
|
assert(vec.data[4] == 8);
|
||||||
|
assert(vec.data[5] == 2);
|
||||||
|
assert(vec.data[6] == 3);
|
||||||
|
assert(vec.data[7] == 4);
|
||||||
|
assert(vec.data[8] == 5);
|
||||||
|
assert(vec.data[9] == 9);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_vector_move_slice_backward(void) {
|
||||||
|
struct SC_VECTOR(int) vec = SC_VECTOR_INITIALIZER;
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
{
|
||||||
|
bool ok = sc_vector_push(&vec, i);
|
||||||
|
assert(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move slice {5, 6, 7} so that its new position is 2 */
|
||||||
|
sc_vector_move_slice(&vec, 5, 3, 2);
|
||||||
|
|
||||||
|
assert(vec.size == 10);
|
||||||
|
assert(vec.data[0] == 0);
|
||||||
|
assert(vec.data[1] == 1);
|
||||||
|
assert(vec.data[2] == 5);
|
||||||
|
assert(vec.data[3] == 6);
|
||||||
|
assert(vec.data[4] == 7);
|
||||||
|
assert(vec.data[5] == 2);
|
||||||
|
assert(vec.data[6] == 3);
|
||||||
|
assert(vec.data[7] == 4);
|
||||||
|
assert(vec.data[8] == 8);
|
||||||
|
assert(vec.data[9] == 9);
|
||||||
|
|
||||||
|
sc_vector_destroy(&vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
(void) argc;
|
||||||
|
(void) argv;
|
||||||
|
|
||||||
|
test_vector_insert_remove();
|
||||||
|
test_vector_push_array();
|
||||||
|
test_vector_insert_array();
|
||||||
|
test_vector_remove_slice();
|
||||||
|
test_vector_swap_remove();
|
||||||
|
test_vector_move();
|
||||||
|
test_vector_move_slice_forward();
|
||||||
|
test_vector_move_slice_backward();
|
||||||
|
test_vector_index_of();
|
||||||
|
test_vector_grow();
|
||||||
|
test_vector_exp_growth();
|
||||||
|
test_vector_reserve();
|
||||||
|
test_vector_shrink_to_fit();
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in a new issue