many new API; fixes

This commit is contained in:
Gregory Burd 2024-05-09 15:50:56 -04:00
parent 0e348efaf6
commit 7494a9a3a2
6 changed files with 437 additions and 90 deletions

View file

@ -8,6 +8,7 @@
<sourceRoots> <sourceRoots>
<file path="$PROJECT_DIR$/examples" /> <file path="$PROJECT_DIR$/examples" />
<file path="$PROJECT_DIR$/include" /> <file path="$PROJECT_DIR$/include" />
<file path="$PROJECT_DIR$/lib" />
<file path="$PROJECT_DIR$/src" /> <file path="$PROJECT_DIR$/src" />
<file path="$PROJECT_DIR$/tests" /> <file path="$PROJECT_DIR$/tests" />
</sourceRoots> </sourceRoots>

View file

@ -125,7 +125,6 @@ sparsemap_t *sparsemap_copy(sparsemap_t *other);
* and releasing the memory used for \b data. Resizing the buffer is only * and releasing the memory used for \b data. Resizing the buffer is only
* supported when the heap object for the map includes the buffer and the * supported when the heap object for the map includes the buffer and the
* \b data offset supplied is relative to the object (see #sparsemap()). * \b data offset supplied is relative to the object (see #sparsemap()).
* This function calls #sparsemap_init().
* *
* @param[in] data A heap or stack memory buffer of \b size for use storing * @param[in] data A heap or stack memory buffer of \b size for use storing
* bitmap data. * bitmap data.
@ -252,6 +251,40 @@ sparsemap_idx_t sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value)
*/ */
size_t sparsemap_get_size(sparsemap_t *map); size_t sparsemap_get_size(sparsemap_t *map);
/** @brief Returns a pointer to the data buffer used for the map.
*
* @param[in] map The sparsemap reference.
* @returns a pointer to the data buffer used for the map
*/
void *sparsemap_get_data(sparsemap_t *map);
/** @brief Returns the number of elements in the map.
*
* @param[in] map The sparsemap reference.
* @returns the number of elements in the map
*/
size_t sparsemap_count(sparsemap_t *map);
/** @brief Returns the offset of the first bit set in the map.
*
* This is the same as the value of the first set bit in the
* map.
*
* @param[in] map The sparsemap reference.
* @returns the offset of the first bit set in the map
*/
sparsemap_idx_t sparsemap_get_starting_offset(sparsemap_t *map);
/** @brief Returns the offset of the last bit set in the map.
*
* This is the same as the value of the last bit set in the
* map.
*
* @param[in] map The sparsemap reference.
* @returns the offset of the index bit set in the map
*/
sparsemap_idx_t sparsemap_get_ending_offset(sparsemap_t *map);
/** @brief Provides a method for a callback function to examine every bit set in /** @brief Provides a method for a callback function to examine every bit set in
* the index. * the index.
* *
@ -279,10 +312,12 @@ int sparsemap_merge(sparsemap_t *map, sparsemap_t *other);
* The \b other bitmap is expected to be empty. * The \b other bitmap is expected to be empty.
* *
* @param[in] map The sparsemap reference. * @param[in] map The sparsemap reference.
* @param[in] offset The 0-based offset into the bitmap at which to split. * @param[in] offset The 0-based offset into the bitmap at which to split, if
* set to SPARSEMAP_IDX_MAX then the bits will be evenly split.
* @param[in] other The bitmap into which we place the split. * @param[in] other The bitmap into which we place the split.
* @returns the offset at which the map was split
*/ */
void sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other); sparsemap_idx_t sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other);
/** @brief Finds the index of the \b n'th bit set to \b value. /** @brief Finds the index of the \b n'th bit set to \b value.
* *

View file

@ -18,8 +18,8 @@
#endif #endif
#endif #endif
#include "../include/common.h"
#include "../include/sparsemap.h" #include "../include/sparsemap.h"
#include "common.h"
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvariadic-macros" #pragma GCC diagnostic ignored "-Wvariadic-macros"
@ -277,17 +277,6 @@ is_set(const int array[], int bit)
return false; return false;
} }
int
is_unique(int a[], int l, int value)
{
for (int i = 0; i < l; ++i) {
if (a[i] == value) {
return 0; // Not unique
}
}
return 1; // Unique
}
int int
whats_set_uint64(uint64_t number, int pos[64]) whats_set_uint64(uint64_t number, int pos[64])
{ {
@ -302,19 +291,31 @@ whats_set_uint64(uint64_t number, int pos[64])
return length; return length;
} }
/** @brief Fills an array with unique random values between 0 and max_value.
*
* @param[in] a The array to fill.
* @param[in] l The length of the array to fill.
* @param[in] max_value The maximum value for the random numbers.
*/
void void
setup_test_array(int a[], int l, int max_value) setup_test_array(int a[], int l, int max_value)
{ {
if (a == NULL || max_value < 0) {
return; // Basic error handling and validation // Create a set to store the unique values.
int unique_values[max_value + 1];
for (int i = 0; i <= max_value; ++i) {
unique_values[i] = 0;
} }
for (int i = 0; i < l; ++i) { // Keep generating random numbers until we have l unique values.
int candidate; int count = 0;
do { while (count < l) {
candidate = random_uint32() % (max_value + 1); // Generate a new value within the specified range int random_number = random_uint32() % (max_value + 1);
} while (!is_unique(a, i, candidate)); // Repeat until a unique value is found if (unique_values[random_number] == 0) {
a[i] = candidate; // Assign the unique value to the array unique_values[random_number] = 1;
a[count] = random_number;
count++;
}
} }
} }

View file

@ -825,19 +825,17 @@ __sm_get_size_impl(sparsemap_t *map)
return SM_SIZEOF_OVERHEAD + p - start; return SM_SIZEOF_OVERHEAD + p - start;
} }
#ifdef SPARSEMAP_DIAGNOSTIC
/** @brief Aligns to SM_BITS_PER_VECTOR a given index \b idx. /** @brief Aligns to SM_BITS_PER_VECTOR a given index \b idx.
* *
* @param[in] idx The index to align. * @param[in] idx The index to align.
* @returns the aligned offset (aligned to sm_bitvec_t capacity). * @returns the aligned offset (aligned to sm_bitvec_t capacity).
*/ */
static sm_idx_t static sm_idx_t
__sm_get_aligned_offset(size_t idx) __sm_get_vector_aligned_offset(size_t idx)
{ {
const size_t capacity = SM_BITS_PER_VECTOR; const size_t capacity = SM_BITS_PER_VECTOR;
return (idx / capacity) * capacity; return (idx / capacity) * capacity;
} }
#endif
/** @brief Aligns to SM_CHUNK_CAPACITY a given index \b idx. /** @brief Aligns to SM_CHUNK_CAPACITY a given index \b idx.
* *
@ -845,7 +843,7 @@ __sm_get_aligned_offset(size_t idx)
* @returns the aligned offset (aligned to __sm_chunk_t capacity) * @returns the aligned offset (aligned to __sm_chunk_t capacity)
*/ */
static sm_idx_t static sm_idx_t
__sm_get_fully_aligned_offset(size_t idx) __sm_get_chunk_aligned_offset(size_t idx)
{ {
const size_t capacity = SM_CHUNK_MAX_CAPACITY; const size_t capacity = SM_CHUNK_MAX_CAPACITY;
return (idx / capacity) * capacity; return (idx / capacity) * capacity;
@ -871,7 +869,7 @@ __sm_get_chunk_offset(sparsemap_t *map, sparsemap_idx_t idx)
for (sparsemap_idx_t i = 0; i < count - 1; i++) { for (sparsemap_idx_t i = 0; i < count - 1; i++) {
sm_idx_t s = *(sm_idx_t *)p; sm_idx_t s = *(sm_idx_t *)p;
__sm_assert(s == __sm_get_aligned_offset(s)); __sm_assert(s == __sm_get_vector_aligned_offset(s));
__sm_chunk_t chunk; __sm_chunk_t chunk;
__sm_chunk_init(&chunk, p + sizeof(sm_idx_t)); __sm_chunk_init(&chunk, p + sizeof(sm_idx_t));
if (s >= idx || idx < s + __sm_chunk_get_capacity(&chunk)) { if (s >= idx || idx < s + __sm_chunk_get_capacity(&chunk)) {
@ -999,7 +997,9 @@ sparsemap_wrap(uint8_t *data, size_t size)
{ {
sparsemap_t *map = (sparsemap_t *)calloc(1, sizeof(sparsemap_t)); sparsemap_t *map = (sparsemap_t *)calloc(1, sizeof(sparsemap_t));
if (map) { if (map) {
sparsemap_init(map, data, size); map->m_data = data;
map->m_data_used = 0;
map->m_capacity = size;
} }
return map; return map;
} }
@ -1125,7 +1125,7 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value)
__sm_append_data(map, &buf[0], sizeof(buf)); __sm_append_data(map, &buf[0], sizeof(buf));
uint8_t *p = __sm_get_chunk_data(map, 0); uint8_t *p = __sm_get_chunk_data(map, 0);
*(sm_idx_t *)p = __sm_get_fully_aligned_offset(idx); *(sm_idx_t *)p = __sm_get_vector_aligned_offset(idx); // TODO: get_fully_aligned_offset?
__sm_set_chunk_count(map, 1); __sm_set_chunk_count(map, 1);
@ -1150,7 +1150,7 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value)
uint8_t buf[sizeof(sm_idx_t) + sizeof(sm_bitvec_t) * 2] = { 0 }; uint8_t buf[sizeof(sm_idx_t) + sizeof(sm_bitvec_t) * 2] = { 0 };
__sm_insert_data(map, offset, &buf[0], sizeof(buf)); __sm_insert_data(map, offset, &buf[0], sizeof(buf));
size_t aligned_idx = __sm_get_fully_aligned_offset(idx); size_t aligned_idx = __sm_get_chunk_aligned_offset(idx);
if (start - aligned_idx < SM_CHUNK_MAX_CAPACITY) { if (start - aligned_idx < SM_CHUNK_MAX_CAPACITY) {
__sm_chunk_t chunk; __sm_chunk_t chunk;
__sm_chunk_init(&chunk, p + sizeof(sm_idx_t)); __sm_chunk_init(&chunk, p + sizeof(sm_idx_t));
@ -1186,7 +1186,7 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value)
start += __sm_chunk_get_capacity(&chunk); start += __sm_chunk_get_capacity(&chunk);
if ((sparsemap_idx_t)start + SM_CHUNK_MAX_CAPACITY < idx) { if ((sparsemap_idx_t)start + SM_CHUNK_MAX_CAPACITY < idx) {
start = __sm_get_fully_aligned_offset(idx); start = __sm_get_chunk_aligned_offset(idx);
} }
*(sm_idx_t *)p = start; *(sm_idx_t *)p = start;
@ -1241,12 +1241,93 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value)
sparsemap_idx_t sparsemap_idx_t
sparsemap_get_starting_offset(sparsemap_t *map) sparsemap_get_starting_offset(sparsemap_t *map)
{ {
sparsemap_idx_t offset = 0;
size_t count = __sm_get_chunk_count(map); size_t count = __sm_get_chunk_count(map);
if (count == 0) { if (count == 0) {
return 0; return 0;
} }
sm_idx_t *chunk = (sm_idx_t *)__sm_get_chunk_data(map, 0); uint8_t *p = __sm_get_chunk_data(map, 0);
return (sparsemap_idx_t)*chunk; sm_idx_t start = *(sm_idx_t *)p;
p += sizeof(sm_idx_t);
__sm_chunk_t chunk;
__sm_chunk_init(&chunk, p);
sparsemap_idx_t relative_position = start;
for (size_t m = 0; m < sizeof(sm_bitvec_t); m++, p++) {
for (int n = 0; n < SM_FLAGS_PER_INDEX_BYTE; n++) {
size_t flags = SM_CHUNK_GET_FLAGS(*p, n);
if (flags == SM_PAYLOAD_NONE) {
continue;
} else if (flags == SM_PAYLOAD_ZEROS) {
relative_position += SM_BITS_PER_VECTOR;
} else if (flags == SM_PAYLOAD_ONES) {
offset = relative_position;
goto done;
} else if (flags == SM_PAYLOAD_MIXED) {
sm_bitvec_t w = chunk.m_data[1 + __sm_chunk_get_position(&chunk, m * SM_FLAGS_PER_INDEX_BYTE + n)];
for (int k = 0; k < SM_BITS_PER_VECTOR; k++) {
if (w & ((sm_bitvec_t)1 << k)) {
offset = relative_position + k;
goto done;
}
}
relative_position += SM_BITS_PER_VECTOR;
}
}
}
done:;
return offset;
}
sparsemap_idx_t
sparsemap_get_ending_offset(sparsemap_t *map)
{
sparsemap_idx_t offset = 0;
size_t count = __sm_get_chunk_count(map);
if (count == 0) {
return 0;
}
uint8_t *p = __sm_get_chunk_data(map, 0);
for (size_t i = 0; i < count - 1; i++) {
p += sizeof(sm_idx_t);
__sm_chunk_t chunk;
__sm_chunk_init(&chunk, p);
p += __sm_chunk_get_size(&chunk);
}
sm_idx_t start = *(sm_idx_t *)p;
p += sizeof(sm_idx_t);
__sm_chunk_t chunk;
__sm_chunk_init(&chunk, p);
sparsemap_idx_t relative_position = start;
for (size_t m = 0; m < sizeof(sm_bitvec_t); m++, p++) {
for (int n = 0; n < SM_FLAGS_PER_INDEX_BYTE; n++) {
size_t flags = SM_CHUNK_GET_FLAGS(*p, n);
if (flags == SM_PAYLOAD_NONE) {
continue;
} else if (flags == SM_PAYLOAD_ZEROS) {
relative_position += SM_BITS_PER_VECTOR;
} else if (flags == SM_PAYLOAD_ONES) {
relative_position += SM_BITS_PER_VECTOR;
offset = relative_position;
} else if (flags == SM_PAYLOAD_MIXED) {
sm_bitvec_t w = chunk.m_data[1 + __sm_chunk_get_position(&chunk, m * SM_FLAGS_PER_INDEX_BYTE + n)];
int idx = 0;
for (int k = 0; k < SM_BITS_PER_VECTOR; k++) {
if (w & ((sm_bitvec_t)1 << k)) {
idx = k;
}
}
offset = relative_position + idx;
relative_position += SM_BITS_PER_VECTOR;
}
}
}
return offset;
}
void *
sparsemap_get_data(sparsemap_t *map)
{
return map->m_data;
} }
size_t size_t
@ -1259,6 +1340,41 @@ sparsemap_get_size(sparsemap_t *map)
return map->m_data_used = __sm_get_size_impl(map); return map->m_data_used = __sm_get_size_impl(map);
} }
size_t
sparsemap_count(sparsemap_t *map)
{
return sparsemap_rank(map, 0, SPARSEMAP_IDX_MAX, true);
#if 0
size_t c = 0, count = __sm_get_chunk_count(map);
if (count == 0) {
return 0;
}
uint8_t *p = __sm_get_chunk_data(map, 0);
for (size_t i = 0; i < count; i++) {
p += sizeof(sm_idx_t);
__sm_chunk_t chunk;
__sm_chunk_init(&chunk, p);
for (size_t m = 0; m < sizeof(sm_bitvec_t); m++, p++) {
for (int n = 0; n < SM_FLAGS_PER_INDEX_BYTE; n++) {
size_t flags = SM_CHUNK_GET_FLAGS(*p, n);
if (flags == SM_PAYLOAD_NONE) {
continue;
} else if (flags == SM_PAYLOAD_ZEROS) {
continue;
} else if (flags == SM_PAYLOAD_ONES) {
c += SM_BITS_PER_VECTOR;
} else if (flags == SM_PAYLOAD_MIXED) {
sm_bitvec_t w = chunk.m_data[1 + __sm_chunk_get_position(&chunk, m * SM_FLAGS_PER_INDEX_BYTE + n)];
c += popcountll(w);
}
}
}
}
return c;
#endif
}
void void
sparsemap_scan(sparsemap_t *map, void (*scanner)(sm_idx_t[], size_t, void *aux), size_t skip, void *aux) sparsemap_scan(sparsemap_t *map, void (*scanner)(sm_idx_t[], size_t, void *aux), size_t skip, void *aux)
{ {
@ -1299,8 +1415,8 @@ sparsemap_merge(sparsemap_t *map, sparsemap_t *other)
for (size_t i = 0; i < max_chunk_count && src_count; i++) { for (size_t i = 0; i < max_chunk_count && src_count; i++) {
sm_idx_t src_start = *(sm_idx_t *)src; sm_idx_t src_start = *(sm_idx_t *)src;
sm_idx_t dst_start = *(sm_idx_t *)dst; sm_idx_t dst_start = *(sm_idx_t *)dst;
src_start = __sm_get_fully_aligned_offset(src_start); src_start = __sm_get_chunk_aligned_offset(src_start);
dst_start = __sm_get_fully_aligned_offset(dst_start); dst_start = __sm_get_chunk_aligned_offset(dst_start);
if (src_start > dst_start && dst_count > 0) { if (src_start > dst_start && dst_count > 0) {
__sm_chunk_t dst_chunk; __sm_chunk_t dst_chunk;
__sm_chunk_init(&dst_chunk, dst + sizeof(sm_idx_t)); __sm_chunk_init(&dst_chunk, dst + sizeof(sm_idx_t));
@ -1315,7 +1431,7 @@ sparsemap_merge(sparsemap_t *map, sparsemap_t *other)
__sm_chunk_t dst_chunk; __sm_chunk_t dst_chunk;
__sm_chunk_init(&dst_chunk, dst + sizeof(sm_idx_t)); __sm_chunk_init(&dst_chunk, dst + sizeof(sm_idx_t));
__sm_merge_chunk(map, src_start, src_chunk); __sm_merge_chunk(map, src_start, src_chunk);
*(sm_idx_t *)dst = __sm_get_fully_aligned_offset(src_start); *(sm_idx_t *)dst = __sm_get_chunk_aligned_offset(src_start);
src += sizeof(sm_idx_t) + __sm_chunk_get_size(&src_chunk); src += sizeof(sm_idx_t) + __sm_chunk_get_size(&src_chunk);
dst += sizeof(sm_idx_t) + __sm_chunk_get_size(&dst_chunk); dst += sizeof(sm_idx_t) + __sm_chunk_get_size(&dst_chunk);
dst_count--; dst_count--;
@ -1347,10 +1463,23 @@ sparsemap_merge(sparsemap_t *map, sparsemap_t *other)
return 0; return 0;
} }
void sparsemap_idx_t
sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other) sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
{ {
assert(offset % SM_BITS_PER_VECTOR == 0); if (offset == 0) {
return 0;
}
if (offset == SPARSEMAP_IDX_MAX) {
sparsemap_idx_t begin = sparsemap_get_starting_offset(map);
sparsemap_idx_t end = sparsemap_get_ending_offset(map);
if (begin != end) {
size_t count = sparsemap_rank(map, begin, end, true);
offset = sparsemap_select(map, count / 2, true);
} else {
return SPARSEMAP_IDX_MAX;
}
}
/* |dst| points to the destination buffer */ /* |dst| points to the destination buffer */
uint8_t *dst = __sm_get_chunk_end(other); uint8_t *dst = __sm_get_chunk_end(other);
@ -1388,7 +1517,7 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
if (i == count) { if (i == count) {
__sm_assert(sparsemap_get_size(map) > SM_SIZEOF_OVERHEAD); __sm_assert(sparsemap_get_size(map) > SM_SIZEOF_OVERHEAD);
__sm_assert(sparsemap_get_size(other) > SM_SIZEOF_OVERHEAD); __sm_assert(sparsemap_get_size(other) > SM_SIZEOF_OVERHEAD);
return; return offset;
} }
/* Now copy all the remaining chunks. */ /* Now copy all the remaining chunks. */
@ -1399,7 +1528,7 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
uint8_t buf[sizeof(sm_idx_t) + sizeof(sm_bitvec_t) * 2] = { 0 }; uint8_t buf[sizeof(sm_idx_t) + sizeof(sm_bitvec_t) * 2] = { 0 };
memcpy(dst, &buf[0], sizeof(buf)); memcpy(dst, &buf[0], sizeof(buf));
*(sm_idx_t *)dst = offset; *(sm_idx_t *)dst = __sm_get_chunk_aligned_offset(offset); //TODO: was simply "offset"
dst += sizeof(sm_idx_t); dst += sizeof(sm_idx_t);
/* the |other| sparsemap_t now has one additional chunk */ /* the |other| sparsemap_t now has one additional chunk */
@ -1422,6 +1551,7 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
for (size_t j = offset % capacity; j < capacity; j++, d++) { for (size_t j = offset % capacity; j < capacity; j++, d++) {
if (__sm_chunk_is_set(&s_chunk, j)) { if (__sm_chunk_is_set(&s_chunk, j)) {
sparsemap_set(other, d, true); sparsemap_set(other, d, true);
sparsemap_set(map, d, false); //TODO remove, and fix set_capacity below
} }
} }
@ -1431,7 +1561,7 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
i++; i++;
/* Reduce the capacity of the source-chunk. */ /* Reduce the capacity of the source-chunk. */
__sm_chunk_set_capacity(&s_chunk, offset % capacity); //__sm_chunk_set_capacity(&s_chunk, offset % capacity); //TODO see comment above
} }
/* Now continue with all remaining chunks. */ /* Now continue with all remaining chunks. */
@ -1461,6 +1591,8 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other)
__sm_assert(sparsemap_get_size(map) >= SM_SIZEOF_OVERHEAD); __sm_assert(sparsemap_get_size(map) >= SM_SIZEOF_OVERHEAD);
__sm_assert(sparsemap_get_size(other) > SM_SIZEOF_OVERHEAD); __sm_assert(sparsemap_get_size(other) > SM_SIZEOF_OVERHEAD);
return offset;
} }
sparsemap_idx_t sparsemap_idx_t

View file

@ -636,7 +636,7 @@ _sparsemap_merge(sparsemap_t **map, sparsemap_t *other)
int retval = sparsemap_merge(*map, other); int retval = sparsemap_merge(*map, other);
if (retval != 0) { if (retval != 0) {
if (errno == ENOSPC) { if (errno == ENOSPC) {
size_t new_size = retval + (64 - (retval % 64)) + 64; size_t new_size = retval + (64 - (retval % 64)) + 64;
*map = sparsemap_set_data_size(*map, NULL, sparsemap_get_capacity(*map) + new_size); *map = sparsemap_set_data_size(*map, NULL, sparsemap_get_capacity(*map) + new_size);
assert(*map != NULL); assert(*map != NULL);
errno = 0; errno = 0;

View file

@ -44,14 +44,17 @@ void
populate_map(sparsemap_t *map, int size, int max_value) populate_map(sparsemap_t *map, int size, int max_value)
{ {
int array[size]; int array[size];
size_t before;
setup_test_array(array, size, max_value); setup_test_array(array, size, max_value);
shuffle(array, size); shuffle(array, size);
before = sparsemap_count(map);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
sparsemap_set(map, array[i], true); sparsemap_set(map, array[i], true);
bool set = sparsemap_is_set(map, array[i]); bool set = sparsemap_is_set(map, array[i]);
munit_assert_true(set); assert_true(set);
} }
assert_true(sparsemap_count(map) == before + size);
} }
static void * static void *
@ -444,45 +447,6 @@ test_api_set(const MunitParameter params[], void *data)
return MUNIT_OK; return MUNIT_OK;
} }
// TODO remove? not public API anymore...
extern sparsemap_idx_t sparsemap_get_starting_offset(sparsemap_t *map);
static void *
test_api_get_starting_offset_setup(const MunitParameter params[], void *user_data)
{
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
assert_ptr_not_null(buf);
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data);
sparsemap_init(map, buf, 1024);
populate_map(map, 1024, 3 * 1024);
return (void *)map;
}
static void
test_api_get_starting_offset_tear_down(void *fixture)
{
sparsemap_t *map = (sparsemap_t *)fixture;
assert_ptr_not_null(map->m_data);
munit_free(map->m_data);
test_api_tear_down(fixture);
}
static MunitResult
test_api_get_starting_offset(const MunitParameter params[], void *data)
{
sparsemap_t *map = (sparsemap_t *)data;
(void)params;
assert_ptr_not_null(map);
sparsemap_set(map, 42, true);
assert_true(sparsemap_is_set(map, 42));
size_t offset = sparsemap_get_starting_offset(map);
assert_true(offset == 0);
return MUNIT_OK;
}
static void * static void *
test_api_get_size_setup(const MunitParameter params[], void *user_data) test_api_get_size_setup(const MunitParameter params[], void *user_data)
{ {
@ -517,6 +481,174 @@ test_api_get_size(const MunitParameter params[], void *data)
return MUNIT_OK; return MUNIT_OK;
} }
static void *
test_api_count_setup(const MunitParameter params[], void *user_data)
{
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
assert_ptr_not_null(buf);
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data);
sparsemap_init(map, buf, 1024);
populate_map(map, 1024, 3 * 1024);
return (void *)map;
}
static void
test_api_count_tear_down(void *fixture)
{
sparsemap_t *map = (sparsemap_t *)fixture;
assert_ptr_not_null(map->m_data);
munit_free(map->m_data);
test_api_tear_down(fixture);
}
static MunitResult
test_api_count(const MunitParameter params[], void *data)
{
sparsemap_t *map = (sparsemap_t *)data;
(void)params;
assert_ptr_not_null(map);
assert_true(sparsemap_count(map) == 1024);
sparsemap_clear(map);
sparsemap_set(map, 0, true);
assert_true(sparsemap_count(map) == 1);
sparsemap_set(map, 8675309, true);
assert_true(sparsemap_count(map) == 2);
sparsemap_clear(map);
for (int i = 0; i < 512; i++) {
sparsemap_set(map, i + 13, true);
}
assert_true(sparsemap_count(map) == 512);
sparsemap_clear(map);
assert_true(sparsemap_count(map) == 0);
return MUNIT_OK;
}
static MunitResult
test_api_get_data(const MunitParameter params[], void *data)
{
(void)data;
(void)params;
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
assert_ptr_not_null(buf);
sparsemap_t *map = (sparsemap_t *)sparsemap_wrap(buf, 1024);
assert_ptr_not_null(map);
populate_map(map, 1024, 3 * 1024);
assert_true(sparsemap_get_data(map) == buf);
return MUNIT_OK;
}
static void *
test_api_get_starting_offset_setup(const MunitParameter params[], void *user_data)
{
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
assert_ptr_not_null(buf);
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data);
sparsemap_init(map, buf, 1024);
return (void *)map;
}
static void
test_api_get_starting_offset_tear_down(void *fixture)
{
sparsemap_t *map = (sparsemap_t *)fixture;
assert_ptr_not_null(map->m_data);
munit_free(map->m_data);
test_api_tear_down(fixture);
}
static MunitResult
test_api_get_starting_offset(const MunitParameter params[], void *data)
{
sparsemap_t *map = (sparsemap_t *)data;
(void)params;
assert_ptr_not_null(map);
sparsemap_set(map, 0, true);
assert_true(sparsemap_get_starting_offset(map) == 0);
sparsemap_clear(map);
sparsemap_set(map, 1, true);
assert_true(sparsemap_get_starting_offset(map) == 1);
sparsemap_clear(map);
sparsemap_set(map, 1025, true);
assert_true(sparsemap_get_starting_offset(map) == 1025);
sparsemap_clear(map);
for (int i = 0; i < 1000; i++) {
sparsemap_set(map, i, true);
}
assert_true(sparsemap_get_starting_offset(map) == 0);
sparsemap_clear(map);
for (int i = 0; i < 1000; i++) {
sparsemap_set(map, i + 1024, true);
}
assert_true(sparsemap_get_starting_offset(map) == 1024);
sparsemap_clear(map);
sparsemap_set(map, 13012, true);
assert_true(sparsemap_get_starting_offset(map) == 13012);
return MUNIT_OK;
}
static void *
test_api_get_ending_offset_setup(const MunitParameter params[], void *user_data)
{
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
assert_ptr_not_null(buf);
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data);
sparsemap_init(map, buf, 1024);
return (void *)map;
}
static void
test_api_get_ending_offset_tear_down(void *fixture)
{
sparsemap_t *map = (sparsemap_t *)fixture;
assert_ptr_not_null(map->m_data);
munit_free(map->m_data);
test_api_tear_down(fixture);
}
static MunitResult
test_api_get_ending_offset(const MunitParameter params[], void *data)
{
sparsemap_t *map = (sparsemap_t *)data;
(void)params;
assert_ptr_not_null(map);
sparsemap_set(map, 0, true);
assert_true(sparsemap_get_ending_offset(map) == 0);
sparsemap_clear(map);
sparsemap_set(map, 0, true);
sparsemap_set(map, 1, true);
assert_true(sparsemap_get_ending_offset(map) == 1);
sparsemap_clear(map);
sparsemap_set(map, 0, true);
sparsemap_set(map, 67, true);
sparsemap_set(map, 1002, true);
sparsemap_set(map, 3087, true);
sparsemap_set(map, 13012, true);
assert_true(sparsemap_get_ending_offset(map) == 13012);
return MUNIT_OK;
}
static void * static void *
test_api_scan_setup(const MunitParameter params[], void *user_data) test_api_scan_setup(const MunitParameter params[], void *user_data)
{ {
@ -566,9 +698,6 @@ test_api_split_setup(const MunitParameter params[], void *user_data)
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data);
sparsemap_init(map, buf, 1024); sparsemap_init(map, buf, 1024);
for (int i = 0; i < 1024; i++) {
sparsemap_set(map, i, true);
}
return (void *)map; return (void *)map;
} }
static void static void
@ -589,7 +718,15 @@ test_api_split(const MunitParameter params[], void *data)
assert_ptr_not_null(map); assert_ptr_not_null(map);
sparsemap_init(&portion, buf, 512); sparsemap_init(&portion, buf, 1024);
for (int i = 0; i < 1024; i++) {
sparsemap_set(map, i, true);
}
for (int i = 0; i < 1024; i++) {
assert_true(sparsemap_is_set(map, i));
assert_false(sparsemap_is_set(&portion, i));
}
sparsemap_split(map, 512, &portion); sparsemap_split(map, 512, &portion);
for (int i = 0; i < 512; i++) { for (int i = 0; i < 512; i++) {
assert_true(sparsemap_is_set(map, i)); assert_true(sparsemap_is_set(map, i));
@ -600,6 +737,45 @@ test_api_split(const MunitParameter params[], void *data)
assert_true(sparsemap_is_set(&portion, i)); assert_true(sparsemap_is_set(&portion, i));
} }
sparsemap_clear(map);
sparsemap_clear(&portion);
for (int i = 0; i < 100; i++) {
sparsemap_set(map, i, true);
}
for (int i = 0; i < 100; i++) {
assert_true(sparsemap_is_set(map, i));
assert_false(sparsemap_is_set(&portion, i));
}
sparsemap_idx_t offset = sparsemap_split(map, SPARSEMAP_IDX_MAX, &portion);
for (int i = 0; i < offset; i++) {
assert_true(sparsemap_is_set(map, i));
assert_false(sparsemap_is_set(&portion, i));
}
for (int i = offset; i < 100; i++) {
assert_false(sparsemap_is_set(map, i));
assert_true(sparsemap_is_set(&portion, i));
}
sparsemap_clear(&portion);
sparsemap_clear(map);
sparsemap_init(&portion, buf, 1024);
for (int i = 0; i < 13; i++) {
sparsemap_set(map, i + 24, true);
}
offset = sparsemap_split(map, SPARSEMAP_IDX_MAX, &portion);
for (int i = 0; i < offset - 24; i++) {
assert_true(sparsemap_is_set(map, i + 24));
assert_false(sparsemap_is_set(&portion, i + 24));
}
for (int i = offset - 24; i < 13; i++) {
assert_false(sparsemap_is_set(map, i + 24));
assert_true(sparsemap_is_set(&portion, i + 24));
}
return MUNIT_OK; return MUNIT_OK;
} }
@ -707,7 +883,7 @@ test_api_merge(const MunitParameter params[], void *data)
assert(sparsemap_is_set(map, i)); assert(sparsemap_is_set(map, i));
} }
free(other); munit_free(other);
return MUNIT_OK; return MUNIT_OK;
} }
@ -1012,9 +1188,11 @@ static MunitTest api_test_suite[] = {
{ (char *)"/get_capacity", test_api_get_capacity, test_api_get_capacity_setup, test_api_get_capacity_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/get_capacity", test_api_get_capacity, test_api_get_capacity_setup, test_api_get_capacity_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/is_set", test_api_is_set, test_api_is_set_setup, test_api_is_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/is_set", test_api_is_set, test_api_is_set_setup, test_api_is_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/set", test_api_set, test_api_set_setup, test_api_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/set", test_api_set, test_api_set_setup, test_api_set_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/get_starting_offset", test_api_get_starting_offset, test_api_get_starting_offset_setup, test_api_get_starting_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
//TODO { (char *)"/get_ending_offset", test_api_get_ending_offset, test_api_get_ending_offset_setup, test_api_get_ending_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/get_size", test_api_get_size, test_api_get_size_setup, test_api_get_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/get_size", test_api_get_size, test_api_get_size_setup, test_api_get_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/count", test_api_count, test_api_count_setup, test_api_count_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/get_data", test_api_get_data, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/get_starting_offset", test_api_get_starting_offset, test_api_get_starting_offset_setup, test_api_get_starting_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/get_ending_offset", test_api_get_ending_offset, test_api_get_ending_offset_setup, test_api_get_ending_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/scan", test_api_scan, test_api_scan_setup, test_api_scan_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/scan", test_api_scan, test_api_scan_setup, test_api_scan_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/split", test_api_split, test_api_split_setup, test_api_split_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/split", test_api_split, test_api_split_setup, test_api_split_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
{ (char *)"/merge", test_api_merge, test_api_merge_setup, test_api_merge_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, { (char *)"/merge", test_api_merge, test_api_merge_setup, test_api_merge_tear_down, MUNIT_TEST_OPTION_NONE, NULL },