diff --git a/include/sparsemap.h b/include/sparsemap.h index 67b9364..bf749aa 100644 --- a/include/sparsemap.h +++ b/include/sparsemap.h @@ -264,7 +264,7 @@ void sparsemap_scan(sparsemap_t *map, void (*scanner)(sm_idx_t vec[], size_t n), * \b other bitmap while removing them from \b map. * * @param[in] map The sparsemap reference. -* @param[in] other The bitmap to merge into \b map. + * @param[in] other The bitmap to merge into \b map. */ void sparsemap_merge(sparsemap_t *map, sparsemap_t *other); diff --git a/src/sparsemap.c b/src/sparsemap.c index 58d42c5..726a047 100644 --- a/src/sparsemap.c +++ b/src/sparsemap.c @@ -109,6 +109,7 @@ enum __SM_CHUNK_INFO { }; #define SM_CHUNK_GET_FLAGS(from, at) (((from)) & ((sm_bitvec_t)SM_FLAG_MASK << ((at)*2))) >> ((at)*2) +#define SM_NEXT_CHUNK_ADDR(chunk) (uint8_t *)((chunk) + sizeof(sm_idx_t) + __sm_chunk_map_get_size((__sm_chunk_t *)&(chunk))); typedef struct { sm_bitvec_t *m_data; @@ -763,7 +764,6 @@ __sm_get_chunk_map_data(sparsemap_t *map, size_t offset) * * @param[in] map The sparsemap_t in question. * @returns a pointer after the end of the used data - * @todo could this simply use m_data_used? */ static uint8_t * __sm_get_chunk_map_end(sparsemap_t *map) @@ -1247,30 +1247,57 @@ sparsemap_scan(sparsemap_t *map, void (*scanner)(sm_idx_t[], size_t), size_t ski } } +void +__sm_chunk_map_merge(sparsemap_t *map, sparsemap_idx_t offset, __sm_chunk_t src) { + size_t capacity = __sm_chunk_map_get_capacity(&src); + for (sparsemap_idx_t j = 0; j < capacity; j++, offset++) { + if (__sm_chunk_map_is_set(&src, j)) { + sparsemap_set(map, offset, true); + } + } +} + void sparsemap_merge(sparsemap_t *map, sparsemap_t *other) { - uint8_t *next_dst, *dst = __sm_get_chunk_map_data(map, 0); - - /* |src| points to the |other| (source) chunk map */ - uint8_t *next_src, *src = __sm_get_chunk_map_data(other, 0); + uint8_t *dst = __sm_get_chunk_map_data(map, 0), + *dst_tail = __sm_get_chunk_map_end(map), + *src = __sm_get_chunk_map_data(other, 0); size_t i, src_count = __sm_get_chunk_map_count(other), dst_count = __sm_get_chunk_map_count(map); - for (i = 0; i < dst_count; i++) { + for (i = 0; i < src_count + dst_count;) { + __sm_chunk_t src_chunk, dst_chunk; sm_idx_t src_start = *(sm_idx_t *)src; sm_idx_t dst_start = *(sm_idx_t *)dst; - __sm_chunk_t src_chunk, dst_chunk; __sm_chunk_map_init(&src_chunk, src + sizeof(sm_idx_t)); __sm_chunk_map_init(&dst_chunk, dst + sizeof(sm_idx_t)); + if (src_start == dst_start) { - __sm_chunk_map_merge(dst, src); - } else { - next_dst = dst + sizeof(sm_idx_t) + __sm_chunk_map_get_size(&dst_chunk); - next_src = dst + sizeof(sm_idx_t) + __sm_chunk_map_get_size(&src_chunk); - if (src_start != next_dst) { - // Copy the chunk. - } else { - + /* Chunks overlap, merge them. */ + __sm_chunk_map_merge(map, src_start, src_chunk); + src = SM_NEXT_CHUNK_ADDR(src); + dst = SM_NEXT_CHUNK_ADDR(dst); + i += 2; + } else if (src_start < dst_start || i > dst_count) { + /* Copy the src chunk into the dest map. */ + size_t s = __sm_chunk_map_get_size(&src_chunk); + + /* Copy the index (sm_idx_t) */ + *(sm_idx_t *)dst_tail = src_start; + dst_tail += sizeof(sm_idx_t); + + /* Copy the chunk data to the buffer. */ + memcpy(dst_tail, src, s); + + /* Update the chunk count and data_used. */ + map->m_data_used = 0; + __sm_set_chunk_map_count(map, __sm_get_chunk_map_count(map) + 1); + + src = SM_NEXT_CHUNK_ADDR(src); + i += 1; + if (i > dst_count) { + dst = SM_NEXT_CHUNK_ADDR(dst); + i += 1; } } } @@ -1363,7 +1390,7 @@ sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other) __sm_chunk_map_set_capacity(&s_chunk, offset % capacity); } - /* Now continue with all remaining minimaps. */ + /* Now continue with all remaining chunk maps. */ for (; i < count; i++) { sm_idx_t start = *(sm_idx_t *)src; src += sizeof(sm_idx_t); diff --git a/tests/test.c b/tests/test.c index 1e930d9..ac226da 100644 --- a/tests/test.c +++ b/tests/test.c @@ -605,6 +605,56 @@ test_api_split(const MunitParameter params[], void *data) return MUNIT_OK; } +static void * +test_api_merge_setup(const MunitParameter params[], void *user_data) +{ + (void)params; + (void)user_data; + sparsemap_t *map = sparsemap(10 * 1024); + assert_ptr_not_null(map); + return (void *)map; +} +static void +test_api_merge_tear_down(void *fixture) +{ + sparsemap_t *map = (sparsemap_t *)fixture; + assert_ptr_not_null(map); + munit_free(map); +} +static MunitResult +test_api_merge(const MunitParameter params[], void *data) +{ + sparsemap_t *map = (sparsemap_t *)data; + sparsemap_t *other = sparsemap(1024); + (void)params; + + assert_ptr_not_null(map); + assert_ptr_not_null(other); + + sparsemap_set(map, 0, true); + sparsemap_set(other, 1, true); + sparsemap_set(other, 2049, true); + sparsemap_set(other, 8193, true); + sparsemap_set(other, 8194, true); + sparsemap_merge(map,other); + + assert_true(sparsemap_is_set(map, 0)); + assert_true(sparsemap_is_set(map, 1)); + assert_true(sparsemap_is_set(map, 2049)); + assert_true(sparsemap_is_set(map, 8193)); + assert_true(sparsemap_is_set(map, 8194)); + + for (int i = 0; i < 10000; i++) { + if (i == 0 || i == 1 || i == 2049 || i == 8193 || i == 8194) + continue; + else + assert_false(sparsemap_is_set(map, i)); + } + + free(other); + return MUNIT_OK; +} + static void * test_api_select_setup(const MunitParameter params[], void *user_data) { @@ -919,6 +969,7 @@ static MunitTest api_test_suite[] = { { (char *)"/get_size", test_api_get_size, test_api_get_size_setup, test_api_get_size_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 }, { (char *)"/select/true", test_api_select, test_api_select_setup, test_api_select_tear_down, MUNIT_TEST_OPTION_NONE, NULL }, #ifdef SELECT_FALSE { (char *)"/select/false", test_api_select_false, test_api_select_false_setup, test_api_select_false_tear_down, MUNIT_TEST_OPTION_NONE, NULL },