diff --git a/.idea/misc.xml b/.idea/misc.xml index 947f58d..aa82e4d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,6 +8,7 @@ + diff --git a/include/sparsemap.h b/include/sparsemap.h index 479ce8a..f89982c 100644 --- a/include/sparsemap.h +++ b/include/sparsemap.h @@ -125,7 +125,6 @@ sparsemap_t *sparsemap_copy(sparsemap_t *other); * 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 * \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 * 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); +/** @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 * the index. * @@ -279,10 +312,12 @@ int sparsemap_merge(sparsemap_t *map, sparsemap_t *other); * The \b other bitmap is expected to be empty. * * @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. + * @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. * diff --git a/lib/common.c b/lib/common.c index da80e41..962ed71 100644 --- a/lib/common.c +++ b/lib/common.c @@ -18,8 +18,8 @@ #endif #endif +#include "../include/common.h" #include "../include/sparsemap.h" -#include "common.h" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wvariadic-macros" @@ -277,17 +277,6 @@ is_set(const int array[], int bit) 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 whats_set_uint64(uint64_t number, int pos[64]) { @@ -302,19 +291,31 @@ whats_set_uint64(uint64_t number, int pos[64]) 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 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) { - int candidate; - do { - candidate = random_uint32() % (max_value + 1); // Generate a new value within the specified range - } while (!is_unique(a, i, candidate)); // Repeat until a unique value is found - a[i] = candidate; // Assign the unique value to the array + // Keep generating random numbers until we have l unique values. + int count = 0; + while (count < l) { + int random_number = random_uint32() % (max_value + 1); + if (unique_values[random_number] == 0) { + unique_values[random_number] = 1; + a[count] = random_number; + count++; + } } } diff --git a/src/sparsemap.c b/src/sparsemap.c index ce9e923..2877058 100644 --- a/src/sparsemap.c +++ b/src/sparsemap.c @@ -825,19 +825,17 @@ __sm_get_size_impl(sparsemap_t *map) return SM_SIZEOF_OVERHEAD + p - start; } -#ifdef SPARSEMAP_DIAGNOSTIC /** @brief Aligns to SM_BITS_PER_VECTOR a given index \b idx. * * @param[in] idx The index to align. * @returns the aligned offset (aligned to sm_bitvec_t capacity). */ 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; return (idx / capacity) * capacity; } -#endif /** @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) */ 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; 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++) { 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_init(&chunk, p + sizeof(sm_idx_t)); 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)); if (map) { - sparsemap_init(map, data, size); + map->m_data = data; + map->m_data_used = 0; + map->m_capacity = size; } 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)); 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); @@ -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 }; __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) { __sm_chunk_t chunk; __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); 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; @@ -1241,12 +1241,93 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value) sparsemap_idx_t sparsemap_get_starting_offset(sparsemap_t *map) { + sparsemap_idx_t offset = 0; size_t count = __sm_get_chunk_count(map); if (count == 0) { return 0; } - sm_idx_t *chunk = (sm_idx_t *)__sm_get_chunk_data(map, 0); - return (sparsemap_idx_t)*chunk; + uint8_t *p = __sm_get_chunk_data(map, 0); + 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 @@ -1259,6 +1340,41 @@ sparsemap_get_size(sparsemap_t *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 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++) { sm_idx_t src_start = *(sm_idx_t *)src; sm_idx_t dst_start = *(sm_idx_t *)dst; - src_start = __sm_get_fully_aligned_offset(src_start); - dst_start = __sm_get_fully_aligned_offset(dst_start); + src_start = __sm_get_chunk_aligned_offset(src_start); + dst_start = __sm_get_chunk_aligned_offset(dst_start); if (src_start > dst_start && dst_count > 0) { __sm_chunk_t dst_chunk; __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_init(&dst_chunk, dst + sizeof(sm_idx_t)); __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); dst += sizeof(sm_idx_t) + __sm_chunk_get_size(&dst_chunk); dst_count--; @@ -1347,10 +1463,23 @@ sparsemap_merge(sparsemap_t *map, sparsemap_t *other) return 0; } -void +sparsemap_idx_t 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 */ 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) { __sm_assert(sparsemap_get_size(map) > SM_SIZEOF_OVERHEAD); __sm_assert(sparsemap_get_size(other) > SM_SIZEOF_OVERHEAD); - return; + return offset; } /* 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 }; 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); /* 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++) { if (__sm_chunk_is_set(&s_chunk, j)) { 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++; /* 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. */ @@ -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(other) > SM_SIZEOF_OVERHEAD); + + return offset; } sparsemap_idx_t diff --git a/tests/soak.c b/tests/soak.c index b3c3945..595cd17 100644 --- a/tests/soak.c +++ b/tests/soak.c @@ -636,7 +636,7 @@ _sparsemap_merge(sparsemap_t **map, sparsemap_t *other) int retval = sparsemap_merge(*map, other); if (retval != 0) { 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); assert(*map != NULL); errno = 0; diff --git a/tests/test.c b/tests/test.c index 57fa712..61a9565 100644 --- a/tests/test.c +++ b/tests/test.c @@ -44,14 +44,17 @@ void populate_map(sparsemap_t *map, int size, int max_value) { int array[size]; + size_t before; setup_test_array(array, size, max_value); shuffle(array, size); + before = sparsemap_count(map); for (int i = 0; i < size; i++) { sparsemap_set(map, array[i], true); bool set = sparsemap_is_set(map, array[i]); - munit_assert_true(set); + assert_true(set); } + assert_true(sparsemap_count(map) == before + size); } static void * @@ -444,45 +447,6 @@ test_api_set(const MunitParameter params[], void *data) 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 * 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; } +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 * 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_init(map, buf, 1024); - for (int i = 0; i < 1024; i++) { - sparsemap_set(map, i, true); - } return (void *)map; } static void @@ -589,7 +718,15 @@ test_api_split(const MunitParameter params[], void *data) 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); for (int i = 0; i < 512; 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)); } + 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; } @@ -707,7 +883,7 @@ test_api_merge(const MunitParameter params[], void *data) assert(sparsemap_is_set(map, i)); } - free(other); + munit_free(other); 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 *)"/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 *)"/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 *)"/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 *)"/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 },