This commit is contained in:
Gregory Burd 2024-04-16 11:31:55 -04:00
parent 6645665d82
commit 20b81983ae
3 changed files with 65 additions and 76 deletions

View file

@ -114,7 +114,7 @@ typedef struct {
sm_bitvec_t *m_data; sm_bitvec_t *m_data;
} __sm_chunk_t; } __sm_chunk_t;
struct sparsemap { struct __attribute__((aligned(8))) sparsemap {
uint8_t *m_data; /* The serialized bitmap data */ uint8_t *m_data; /* The serialized bitmap data */
size_t m_capacity; /* The total size of m_data */ size_t m_capacity; /* The total size of m_data */
size_t m_data_used; /* The used 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. * 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) __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); uint8_t *p = __sm_get_chunk_map_data(map, offset);
memmove(p + buffer_size, p, map->m_data_used - offset); memmove(p + buffer_size, p, map->m_data_used - offset);
memcpy(p, buffer, buffer_size); memcpy(p, buffer, buffer_size);
map->m_data_used += 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); ssize_t offset = __sm_get_chunk_map_offset(map, idx);
bool dont_grow = false; 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 /* If there is no __sm_chunk_t and the bit is set to zero then return
immediately; otherwise create an initial __sm_chunk_t. */ immediately; otherwise create an initial __sm_chunk_t. */
if (offset == -1) { 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); __sm_set_chunk_map_count(map, 1);
/* We already inserted an additional sm_bitvec_t; later on there /* We already inserted an additional sm_bitvec_t; given that has happened
is no need to grow the vector even further. */ there is no need to grow the vector even further. */
dont_grow = true; dont_grow = true;
offset = 0; 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)); offset += (ssize_t)(sizeof(sm_idx_t) + position * sizeof(sm_bitvec_t));
__sm_insert_data(map, offset, (uint8_t *)&fill, 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_when_diag({
__sm_assert(code == SM_OK); code = __sm_chunk_map_set(&chunk, idx - start, value, &position, &fill, true);
__sm_assert(code == SM_OK);
});
break; break;
case SM_NEEDS_TO_SHRINK: case SM_NEEDS_TO_SHRINK:
/* If the __sm_chunk_t is empty then remove it. */ /* 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); p += __sm_chunk_map_get_size(&chunk);
} }
#ifdef DEBUG return SPARSEMAP_IDX_MAX; // TODO... shouldn't be here?
assert(!"shouldn't be here");
#endif
return SPARSEMAP_IDX_MAX;
} else { } 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
} }
} }

View file

@ -118,7 +118,7 @@ has_sequential_set(int a[], int l, int r)
int int
ensure_sequential_set(int a[], int l, int r) 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; return 0;
} }

View file

@ -314,20 +314,23 @@ test_api_remaining_capacity(const MunitParameter params[], void *data)
do { do {
sparsemap_set(map, i++, true); sparsemap_set(map, i++, true);
cap = sparsemap_capacity_remaining(map); cap = sparsemap_capacity_remaining(map);
} while (cap > 1.0); } while (cap > 1.0 && errno != ENOSPC);
// assert_true(i == 169985); when seed is 8675309 errno = 0;
assert_true(cap <= 1.0); assert_true(cap <= 2.0);
sparsemap_clear(map); sparsemap_clear(map);
cap = sparsemap_capacity_remaining(map);
assert_true(cap > 99);
i = 0; i = 0;
do { do {
int p = munit_rand_int_range(0, 150000); int p = munit_rand_int_range(0, 150000);
sparsemap_set(map, p, true); sparsemap_set(map, p, true);
i++; i++;
cap = sparsemap_capacity_remaining(map); cap = sparsemap_capacity_remaining(map);
} while (cap > 2.0); } while (cap > 1.0 && errno != ENOSPC);
// assert_true(i == 64); when seed is 8675309 errno = 0;
assert_true(cap <= 2.0); assert_true(cap <= 1.0);
return MUNIT_OK; return MUNIT_OK;
} }
@ -873,33 +876,32 @@ static MunitTest api_test_suite[] = {
static void * static void *
test_scale_lots_o_spans_setup(const MunitParameter params[], void *user_data) test_scale_lots_o_spans_setup(const MunitParameter params[], void *user_data)
{ {
uint8_t *buf = munit_calloc(10 * 1024, sizeof(uint8_t)); (void)params;
assert_ptr_not_null(buf); (void)user_data;
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_t *map = sparsemap(10 * 1024);
assert_ptr_not_null(map);
sparsemap_init(map, buf, 10 * 1024);
return (void *)map; return (void *)map;
} }
static void static void
test_scale_lots_o_spans_tear_down(void *fixture) test_scale_lots_o_spans_tear_down(void *fixture)
{ {
sparsemap_t *map = (sparsemap_t *)fixture; sparsemap_t *map = (sparsemap_t *)fixture;
assert_ptr_not_null(map->m_data); assert_ptr_not_null(map);
munit_free(map->m_data); munit_free(map);
test_api_tear_down(fixture);
} }
static MunitResult static MunitResult
test_scale_lots_o_spans(const MunitParameter params[], void *data) test_scale_lots_o_spans(const MunitParameter params[], void *data)
{ {
size_t amt = 897915; // 268435456
sparsemap_t *map = (sparsemap_t *)data; sparsemap_t *map = (sparsemap_t *)data;
(void)params; (void)params;
assert_ptr_not_null(map); assert_ptr_not_null(map);
for (int i = 0; i < 268435456;) { for (size_t i = 0; i < amt;) {
int l = i % 31 + 16; 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) { if (errno == ENOSPC) {
map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) * 2); map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) * 2);
errno = 0; 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 */ /* ANSI esc code to clear line, carrage return, then print on the same line */
// printf("\033[2K\r%d", i); // printf("\033[2K\r%d", i);
// printf("%d\t%d\n", l, i); // printf("%d\t%d\n", l, i);
// fflush(stdout);
} }
return MUNIT_OK; return MUNIT_OK;
@ -917,21 +918,18 @@ test_scale_lots_o_spans(const MunitParameter params[], void *data)
static void * static void *
test_scale_ondrej_setup(const MunitParameter params[], void *user_data) test_scale_ondrej_setup(const MunitParameter params[], void *user_data)
{ {
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t)); (void)params;
assert_ptr_not_null(buf); (void)user_data;
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_t *map = sparsemap(10 * 1024);
assert_ptr_not_null(map);
sparsemap_init(map, buf, 1024);
return (void *)map; return (void *)map;
} }
static void static void
test_scale_ondrej_tear_down(void *fixture) test_scale_ondrej_tear_down(void *fixture)
{ {
sparsemap_t *map = (sparsemap_t *)fixture; sparsemap_t *map = (sparsemap_t *)fixture;
assert_ptr_not_null(map->m_data); assert_ptr_not_null(map);
munit_free(map->m_data); munit_free(map);
test_api_tear_down(fixture);
} }
static MunitResult static MunitResult
test_scale_ondrej(const MunitParameter params[], void *data) 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++) { for (sparsemap_idx_t j = 0; j < stride; j++) {
bool set = (i != needle) ? (j < 10) : (j < 9); bool set = (i != needle) ? (j < 10) : (j < 9);
sparsemap_set(map, i, set); sparsemap_set(map, i, set);
if (errno == ENOSPC) {
map = sparsemap_set_data_size(map, sparsemap_get_capacity(map) * 2);
errno = 0;
}
} }
} }
#if 0 sparsemap_idx_t a = sparsemap_span(map, 0, 9, false);
// sm_add_span(map, 268435456, 9); assert_true((a / stride) == needle);
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
return MUNIT_OK; return MUNIT_OK;
} }
static void * static void *
test_scale_spans_come_spans_go_setup(const MunitParameter params[], void *user_data) test_scale_spans_come_spans_go_setup(const MunitParameter params[], void *user_data)
{ {
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t)); (void)params;
assert_ptr_not_null(buf); (void)user_data;
sparsemap_t *map = (sparsemap_t *)test_api_setup(params, user_data); sparsemap_t *map = sparsemap(10 * 1024);
assert_ptr_not_null(map);
sparsemap_init(map, buf, 1024);
return (void *)map; return (void *)map;
} }
static void static void
test_scale_spans_come_spans_go_tear_down(void *fixture) test_scale_spans_come_spans_go_tear_down(void *fixture)
{ {
sparsemap_t *map = (sparsemap_t *)fixture; sparsemap_t *map = (sparsemap_t *)fixture;
assert_ptr_not_null(map->m_data); assert_ptr_not_null(map);
munit_free(map->m_data); munit_free(map);
test_api_tear_down(fixture);
} }
static MunitResult static MunitResult
test_scale_spans_come_spans_go(const MunitParameter params[], void *data) 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; sparsemap_t *map = (sparsemap_t *)data;
(void)params; (void)params;
assert_ptr_not_null(map); assert_ptr_not_null(map);
/* ~5e7 interations due to 2e9 / avg(l) */ for (size_t i = 0; i < amt;) {
for (int i = 0; i < 268435456;) {
int l = i % 31 + 16; 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. */ /* After 10,000 spans are in there we consume a span every iteration. */
if (l > 10000) { if (l > 10000) {
@ -1069,7 +1057,6 @@ test_scale_best_case(const MunitParameter params[], void *data)
for (int i = 0; i < 172032; i++) { for (int i = 0; i < 172032; i++) {
/* ANSI esc code to clear line, carrage return, then print on the same line */ /* ANSI esc code to clear line, carrage return, then print on the same line */
// printf("\033[2K\r%d", i); // printf("\033[2K\r%d", i);
// fflush(stdout);
sparsemap_set(map, i, true); 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) { for (int i = 0; i < 7744; i += 2) {
/* ANSI esc code to clear line, carrage return, then print on the same line */ /* ANSI esc code to clear line, carrage return, then print on the same line */
// printf("\033[2K\r%d", i); // printf("\033[2K\r%d", i);
// fflush(stdout);
sparsemap_set(map, i, true); sparsemap_set(map, i, true);
} }
@ -1260,6 +1246,11 @@ int
main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)]) main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
{ {
struct user_data info; 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); return munit_suite_main(&main_test_suite, (void *)&info, argc, argv);
} }