diff --git a/src/sparsemap.c b/src/sparsemap.c index 67e8b14..34c57a3 100644 --- a/src/sparsemap.c +++ b/src/sparsemap.c @@ -114,7 +114,7 @@ typedef struct { sm_bitvec_t *m_data; } __sm_chunk_t; -struct sparsemap { +struct __attribute__((aligned(8))) sparsemap { uint8_t *m_data; /* The serialized bitmap data */ size_t m_capacity; /* The total size of m_data */ size_t m_data_used; /* The used size of m_data */ @@ -764,19 +764,13 @@ __sm_append_data(sparsemap_t *map, uint8_t *buffer, size_t buffer_size) /** * Inserts data somewhere in the middle of m_data. */ -static int +void __sm_insert_data(sparsemap_t *map, size_t offset, uint8_t *buffer, size_t buffer_size) { - if (map->m_data_used + buffer_size > map->m_capacity) { - __sm_assert(!"buffer overflow"); - abort(); - } - uint8_t *p = __sm_get_chunk_map_data(map, offset); memmove(p + buffer_size, p, map->m_data_used - offset); memcpy(p, buffer, buffer_size); map->m_data_used += buffer_size; - return 0; } /** @@ -944,6 +938,11 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value) ssize_t offset = __sm_get_chunk_map_offset(map, idx); bool dont_grow = false; + if (map->m_data_used + sizeof(sm_idx_t) + sizeof(sm_bitvec_t) * 2 > map->m_capacity) { + errno = ENOSPC; + return SPARSEMAP_IDX_MAX; + } + /* If there is no __sm_chunk_t and the bit is set to zero then return immediately; otherwise create an initial __sm_chunk_t. */ if (offset == -1) { @@ -959,8 +958,8 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value) __sm_set_chunk_map_count(map, 1); - /* We already inserted an additional sm_bitvec_t; later on there - is no need to grow the vector even further. */ + /* We already inserted an additional sm_bitvec_t; given that has happened + there is no need to grow the vector even further. */ dont_grow = true; offset = 0; } @@ -1044,8 +1043,10 @@ sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value) offset += (ssize_t)(sizeof(sm_idx_t) + position * sizeof(sm_bitvec_t)); __sm_insert_data(map, offset, (uint8_t *)&fill, sizeof(sm_bitvec_t)); } - code = __sm_chunk_map_set(&chunk, idx - start, value, &position, &fill, true); - __sm_assert(code == SM_OK); + __sm_when_diag({ + code = __sm_chunk_map_set(&chunk, idx - start, value, &position, &fill, true); + __sm_assert(code == SM_OK); + }); break; case SM_NEEDS_TO_SHRINK: /* If the __sm_chunk_t is empty then remove it. */ @@ -1259,12 +1260,9 @@ sparsemap_select(sparsemap_t *map, sparsemap_idx_t n, bool value) p += __sm_chunk_map_get_size(&chunk); } -#ifdef DEBUG - assert(!"shouldn't be here"); -#endif - return SPARSEMAP_IDX_MAX; + return SPARSEMAP_IDX_MAX; // TODO... shouldn't be here? } else { - return SPARSEMAP_IDX_MIN; // TODO... sparsemap_select(map, -n, value); + return SPARSEMAP_IDX_MIN; // TODO... sparsemap_select(map, -n, value); seek from end, not start } } diff --git a/tests/common.c b/tests/common.c index 01b2ae7..4a9baed 100644 --- a/tests/common.c +++ b/tests/common.c @@ -118,7 +118,7 @@ has_sequential_set(int a[], int l, int r) int ensure_sequential_set(int a[], int l, int r) { - if (!a || l == 0 || r < 1 || r > l) { + if (!a || l == 0 || r < 1 || r > l - 1) { return 0; } diff --git a/tests/test.c b/tests/test.c index 2fc93f2..6166e8a 100644 --- a/tests/test.c +++ b/tests/test.c @@ -314,20 +314,23 @@ test_api_remaining_capacity(const MunitParameter params[], void *data) do { sparsemap_set(map, i++, true); cap = sparsemap_capacity_remaining(map); - } while (cap > 1.0); - // assert_true(i == 169985); when seed is 8675309 - assert_true(cap <= 1.0); + } while (cap > 1.0 && errno != ENOSPC); + errno = 0; + assert_true(cap <= 2.0); sparsemap_clear(map); + cap = sparsemap_capacity_remaining(map); + assert_true(cap > 99); + i = 0; do { int p = munit_rand_int_range(0, 150000); sparsemap_set(map, p, true); i++; cap = sparsemap_capacity_remaining(map); - } while (cap > 2.0); - // assert_true(i == 64); when seed is 8675309 - assert_true(cap <= 2.0); + } while (cap > 1.0 && errno != ENOSPC); + errno = 0; + assert_true(cap <= 1.0); return MUNIT_OK; } @@ -873,33 +876,32 @@ static MunitTest api_test_suite[] = { static void * test_scale_lots_o_spans_setup(const MunitParameter params[], void *user_data) { - uint8_t *buf = munit_calloc(10 * 1024, sizeof(uint8_t)); - assert_ptr_not_null(buf); - sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); - - sparsemap_init(map, buf, 10 * 1024); - + (void)params; + (void)user_data; + sparsemap_t *map = sparsemap(10 * 1024); + assert_ptr_not_null(map); return (void *)map; } static void test_scale_lots_o_spans_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); + assert_ptr_not_null(map); + munit_free(map); } static MunitResult test_scale_lots_o_spans(const MunitParameter params[], void *data) { + size_t amt = 897915; // 268435456 sparsemap_t *map = (sparsemap_t *)data; (void)params; assert_ptr_not_null(map); - for (int i = 0; i < 268435456;) { + for (size_t i = 0; i < amt;) { int l = i % 31 + 16; - sm_add_span(map, 268435456, l); + // TODO: sm_add_span(map, amt, l); + sm_add_span(map, 10000, l); if (errno == ENOSPC) { map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) * 2); errno = 0; @@ -908,7 +910,6 @@ test_scale_lots_o_spans(const MunitParameter params[], void *data) /* ANSI esc code to clear line, carrage return, then print on the same line */ // printf("\033[2K\r%d", i); // printf("%d\t%d\n", l, i); - // fflush(stdout); } return MUNIT_OK; @@ -917,21 +918,18 @@ test_scale_lots_o_spans(const MunitParameter params[], void *data) static void * test_scale_ondrej_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); - + (void)params; + (void)user_data; + sparsemap_t *map = sparsemap(10 * 1024); + assert_ptr_not_null(map); return (void *)map; } static void test_scale_ondrej_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); + assert_ptr_not_null(map); + munit_free(map); } static MunitResult test_scale_ondrej(const MunitParameter params[], void *data) @@ -948,59 +946,49 @@ test_scale_ondrej(const MunitParameter params[], void *data) for (sparsemap_idx_t j = 0; j < stride; j++) { bool set = (i != needle) ? (j < 10) : (j < 9); sparsemap_set(map, i, set); + if (errno == ENOSPC) { + map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) * 2); + errno = 0; + } } } -#if 0 - // sm_add_span(map, 268435456, 9); - - size_t b = sparsemap_span(map, 0, 8, true); - if (SPARSEMAP_NOT_FOUND(b)) { - printf("%ld\n", b); - printf("%ld / %ld == %ld %ld\n", b, stride, b / stride, needle); - } else { - printf("not found\n"); - } - /* - size_t b = sparsemap_span(map, 0, 9, false); TODO - printf("%ld (%d) / %d == %d", b, (int)b, stride, needle); - fflush(stdout); - assert_true(((int)b / stride) == needle); - */ -#endif + sparsemap_idx_t a = sparsemap_span(map, 0, 9, false); + assert_true((a / stride) == needle); return MUNIT_OK; } static void * test_scale_spans_come_spans_go_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); - + (void)params; + (void)user_data; + sparsemap_t *map = sparsemap(10 * 1024); + assert_ptr_not_null(map); return (void *)map; } static void test_scale_spans_come_spans_go_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); + assert_ptr_not_null(map); + munit_free(map); } static MunitResult test_scale_spans_come_spans_go(const MunitParameter params[], void *data) { + size_t amt = 897915; // 268435456, ~5e7 interations due to 2e9 / avg(l) sparsemap_t *map = (sparsemap_t *)data; (void)params; assert_ptr_not_null(map); - /* ~5e7 interations due to 2e9 / avg(l) */ - for (int i = 0; i < 268435456;) { + for (size_t i = 0; i < amt;) { int l = i % 31 + 16; - sm_add_span(map, 268435456, l); + sm_add_span(map, amt, l); + if (errno == ENOSPC) { + map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) * 2); + errno = 0; + } /* After 10,000 spans are in there we consume a span every iteration. */ if (l > 10000) { @@ -1069,7 +1057,6 @@ test_scale_best_case(const MunitParameter params[], void *data) for (int i = 0; i < 172032; i++) { /* ANSI esc code to clear line, carrage return, then print on the same line */ // printf("\033[2K\r%d", i); - // fflush(stdout); sparsemap_set(map, i, true); } @@ -1116,7 +1103,6 @@ test_scale_worst_case(const MunitParameter params[], void *data) for (int i = 0; i < 7744; i += 2) { /* ANSI esc code to clear line, carrage return, then print on the same line */ // printf("\033[2K\r%d", i); - // fflush(stdout); sparsemap_set(map, i, true); } @@ -1260,6 +1246,11 @@ int main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)]) { struct user_data info; + + /* Disable buffering on std{out,err} to avoid having to call fflush(). */ + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); + return munit_suite_main(&main_test_suite, (void *)&info, argc, argv); }