mirror of
https://github.com/VCMP-SqMod/SqMod.git
synced 2024-11-14 03:37:16 +01:00
375 lines
11 KiB
C
375 lines
11 KiB
C
|
#include <assert.h>
|
||
|
#include <data-pool.h>
|
||
|
#include <inttypes.h>
|
||
|
#include "libtap/tap.h"
|
||
|
#include <math.h>
|
||
|
#include "maxminddb_test_helper.h"
|
||
|
|
||
|
static void test_data_pool_new(void);
|
||
|
static void test_data_pool_destroy(void);
|
||
|
static void test_data_pool_alloc(void);
|
||
|
static void test_data_pool_to_list(void);
|
||
|
static bool create_and_check_list(size_t const,
|
||
|
size_t const);
|
||
|
static void check_block_count(MMDB_entry_data_list_s const *const,
|
||
|
size_t const);
|
||
|
|
||
|
int main(void)
|
||
|
{
|
||
|
plan(NO_PLAN);
|
||
|
test_data_pool_new();
|
||
|
test_data_pool_destroy();
|
||
|
test_data_pool_alloc();
|
||
|
test_data_pool_to_list();
|
||
|
done_testing();
|
||
|
}
|
||
|
|
||
|
static void test_data_pool_new(void)
|
||
|
{
|
||
|
{
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(0);
|
||
|
ok(!pool, "size 0 is not valid");
|
||
|
}
|
||
|
|
||
|
{
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(SIZE_MAX - 10);
|
||
|
ok(!pool, "very large size is not valid");
|
||
|
}
|
||
|
|
||
|
{
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(512);
|
||
|
ok(pool != NULL, "size 512 is valid");
|
||
|
cmp_ok(pool->size, "==", 512, "size is 512");
|
||
|
cmp_ok(pool->used, "==", 0, "used size is 0");
|
||
|
data_pool_destroy(pool);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void test_data_pool_destroy(void)
|
||
|
{
|
||
|
{
|
||
|
data_pool_destroy(NULL);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(512);
|
||
|
ok(pool != NULL, "created pool");
|
||
|
data_pool_destroy(pool);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void test_data_pool_alloc(void)
|
||
|
{
|
||
|
{
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(1);
|
||
|
ok(pool != NULL, "created pool");
|
||
|
cmp_ok(pool->used, "==", 0, "used size starts at 0");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
|
||
|
ok(entry1 != NULL, "allocated first entry");
|
||
|
// Arbitrary so that we can recognize it.
|
||
|
entry1->entry_data.offset = (uint32_t)123;
|
||
|
|
||
|
cmp_ok(pool->size, "==", 1, "size is still 1");
|
||
|
cmp_ok(pool->used, "==", 1, "used size is 1 after taking one");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
|
||
|
ok(entry2 != NULL, "got another entry");
|
||
|
ok(entry1 != entry2, "second entry is different from first entry");
|
||
|
|
||
|
cmp_ok(pool->size, "==", 2, "size is 2 (new block)");
|
||
|
cmp_ok(pool->used, "==", 1, "used size is 1 in current block");
|
||
|
|
||
|
ok(entry1->entry_data.offset == 123,
|
||
|
"accessing the original entry's memory is ok");
|
||
|
|
||
|
data_pool_destroy(pool);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
size_t const initial_size = 10;
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
|
||
|
ok(pool != NULL, "created pool");
|
||
|
|
||
|
MMDB_entry_data_list_s *entry1 = NULL;
|
||
|
for (size_t i = 0; i < initial_size; i++) {
|
||
|
MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
|
||
|
ok(entry != NULL, "got an entry");
|
||
|
// Give each a unique number so we can check it.
|
||
|
entry->entry_data.offset = (uint32_t)i;
|
||
|
if (i == 0) {
|
||
|
entry1 = entry;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
cmp_ok(pool->size, "==", initial_size, "size is the initial size");
|
||
|
cmp_ok(pool->used, "==", initial_size, "used size is as expected");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
|
||
|
ok(entry != NULL, "got an entry");
|
||
|
entry->entry_data.offset = (uint32_t)initial_size;
|
||
|
|
||
|
cmp_ok(pool->size, "==", initial_size * 2,
|
||
|
"size is the initial size*2");
|
||
|
cmp_ok(pool->used, "==", 1, "used size is as expected");
|
||
|
|
||
|
MMDB_entry_data_list_s *const list = data_pool_to_list(pool);
|
||
|
|
||
|
MMDB_entry_data_list_s *element = list;
|
||
|
for (size_t i = 0; i < initial_size + 1; i++) {
|
||
|
ok(
|
||
|
element->entry_data.offset == (uint32_t)i,
|
||
|
"found offset %" PRIu32 ", should have %zu",
|
||
|
element->entry_data.offset,
|
||
|
i
|
||
|
);
|
||
|
element = element->next;
|
||
|
}
|
||
|
|
||
|
ok(entry1->entry_data.offset == (uint32_t)0,
|
||
|
"accessing entry1's original memory is ok after growing the pool");
|
||
|
|
||
|
data_pool_destroy(pool);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void test_data_pool_to_list(void)
|
||
|
{
|
||
|
{
|
||
|
size_t const initial_size = 16;
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
|
||
|
ok(pool != NULL, "created pool");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
|
||
|
ok(entry1 != NULL, "got an entry");
|
||
|
|
||
|
MMDB_entry_data_list_s *const list_one_element
|
||
|
= data_pool_to_list(pool);
|
||
|
ok(list_one_element != NULL, "got a list");
|
||
|
ok(list_one_element == entry1,
|
||
|
"list's first element is the first we retrieved");
|
||
|
ok(list_one_element->next == NULL, "list is one element in size");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
|
||
|
ok(entry2 != NULL, "got another entry");
|
||
|
|
||
|
MMDB_entry_data_list_s *const list_two_elements
|
||
|
= data_pool_to_list(pool);
|
||
|
ok(list_two_elements != NULL, "got a list");
|
||
|
ok(list_two_elements == entry1,
|
||
|
"list's first element is the first we retrieved");
|
||
|
ok(list_two_elements->next != NULL, "list has a second element");
|
||
|
|
||
|
MMDB_entry_data_list_s *const second_element = list_two_elements->next;
|
||
|
ok(second_element == entry2,
|
||
|
"second item in list is second we retrieved");
|
||
|
ok(second_element->next == NULL, "list ends with the second element");
|
||
|
|
||
|
data_pool_destroy(pool);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
size_t const initial_size = 1;
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
|
||
|
ok(pool != NULL, "created pool");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
|
||
|
ok(entry1 != NULL, "got an entry");
|
||
|
|
||
|
MMDB_entry_data_list_s *const list_one_element
|
||
|
= data_pool_to_list(pool);
|
||
|
ok(list_one_element != NULL, "got a list");
|
||
|
ok(list_one_element == entry1,
|
||
|
"list's first element is the first we retrieved");
|
||
|
ok(list_one_element->next == NULL, "list ends with this element");
|
||
|
|
||
|
data_pool_destroy(pool);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
size_t const initial_size = 2;
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
|
||
|
ok(pool != NULL, "created pool");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry1 = data_pool_alloc(pool);
|
||
|
ok(entry1 != NULL, "got an entry");
|
||
|
|
||
|
MMDB_entry_data_list_s *const entry2 = data_pool_alloc(pool);
|
||
|
ok(entry2 != NULL, "got an entry");
|
||
|
ok(entry1 != entry2, "second entry is different from the first");
|
||
|
|
||
|
MMDB_entry_data_list_s *const list_element1 = data_pool_to_list(pool);
|
||
|
ok(list_element1 != NULL, "got a list");
|
||
|
ok(list_element1 == entry1,
|
||
|
"list's first element is the first we retrieved");
|
||
|
|
||
|
MMDB_entry_data_list_s *const list_element2 = list_element1->next;
|
||
|
ok(list_element2 == entry2,
|
||
|
"second element is the second we retrieved");
|
||
|
ok(list_element2->next == NULL, "list ends with this element");
|
||
|
|
||
|
data_pool_destroy(pool);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill one block save for one spot");
|
||
|
ok(
|
||
|
create_and_check_list(3, 2),
|
||
|
"fill one block save for one spot"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill one block");
|
||
|
ok(
|
||
|
create_and_check_list(3, 3),
|
||
|
"fill one block"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill one block and use one spot in the next block");
|
||
|
ok(
|
||
|
create_and_check_list(3, 3 + 1),
|
||
|
"fill one block and use one spot in the next block"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill two blocks save for one spot");
|
||
|
ok(
|
||
|
create_and_check_list(3, 3 + 3 * 2 - 1),
|
||
|
"fill two blocks save for one spot"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill two blocks");
|
||
|
ok(
|
||
|
create_and_check_list(3, 3 + 3 * 2),
|
||
|
"fill two blocks"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill two blocks and use one spot in the next");
|
||
|
ok(
|
||
|
create_and_check_list(3, 3 + 3 * 2 + 1),
|
||
|
"fill two blocks and use one spot in the next"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill three blocks save for one spot");
|
||
|
ok(
|
||
|
create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2 - 1),
|
||
|
"fill three blocks save for one spot"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
{
|
||
|
diag("starting test: fill three blocks");
|
||
|
ok(
|
||
|
create_and_check_list(3, 3 + 3 * 2 + 3 * 2 * 2),
|
||
|
"fill three blocks"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// It would be nice to have a larger number of these, but it's expensive to
|
||
|
// run many. We currently hardcode what this will be anyway, so varying
|
||
|
// this is not very interesting.
|
||
|
size_t const initial_sizes[] = { 1, 2, 32, 64, 128, 256 };
|
||
|
|
||
|
size_t const max_element_count = 4096;
|
||
|
|
||
|
for (size_t i = 0; i < sizeof(initial_sizes) / sizeof(initial_sizes[0]);
|
||
|
i++) {
|
||
|
size_t const initial_size = initial_sizes[i];
|
||
|
|
||
|
for (size_t element_count = 0; element_count < max_element_count;
|
||
|
element_count++) {
|
||
|
assert(create_and_check_list(initial_size, element_count));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Use assert() rather than libtap as libtap is significantly slower and we run
|
||
|
// this frequently.
|
||
|
static bool create_and_check_list(size_t const initial_size,
|
||
|
size_t const element_count)
|
||
|
{
|
||
|
MMDB_data_pool_s *const pool = data_pool_new(initial_size);
|
||
|
assert(pool != NULL);
|
||
|
|
||
|
assert(pool->used == 0);
|
||
|
|
||
|
// Hold on to the pointers as we initially see them so that we can check
|
||
|
// they are still valid after building the list.
|
||
|
MMDB_entry_data_list_s **const entry_array
|
||
|
= calloc(element_count, sizeof(MMDB_entry_data_list_s *));
|
||
|
assert(entry_array != NULL);
|
||
|
|
||
|
for (size_t i = 0; i < element_count; i++) {
|
||
|
MMDB_entry_data_list_s *const entry = data_pool_alloc(pool);
|
||
|
assert(entry != NULL);
|
||
|
|
||
|
entry->entry_data.offset = (uint32_t)i;
|
||
|
|
||
|
entry_array[i] = entry;
|
||
|
}
|
||
|
|
||
|
MMDB_entry_data_list_s *const list = data_pool_to_list(pool);
|
||
|
|
||
|
if (element_count == 0) {
|
||
|
assert(list == NULL);
|
||
|
data_pool_destroy(pool);
|
||
|
free(entry_array);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
assert(list != NULL);
|
||
|
|
||
|
MMDB_entry_data_list_s *element = list;
|
||
|
for (size_t i = 0; i < element_count; i++) {
|
||
|
assert(element->entry_data.offset == (uint32_t)i);
|
||
|
|
||
|
assert(element == entry_array[i]);
|
||
|
|
||
|
element = element->next;
|
||
|
}
|
||
|
assert(element == NULL);
|
||
|
|
||
|
check_block_count(list, initial_size);
|
||
|
|
||
|
data_pool_destroy(pool);
|
||
|
free(entry_array);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Use assert() rather than libtap as libtap is significantly slower and we run
|
||
|
// this frequently.
|
||
|
static void check_block_count(MMDB_entry_data_list_s const *const list,
|
||
|
size_t const initial_size)
|
||
|
{
|
||
|
size_t got_block_count = 0;
|
||
|
size_t got_element_count = 0;
|
||
|
|
||
|
MMDB_entry_data_list_s const *element = list;
|
||
|
while (element) {
|
||
|
got_element_count++;
|
||
|
|
||
|
if (element->pool) {
|
||
|
got_block_count++;
|
||
|
}
|
||
|
|
||
|
element = element->next;
|
||
|
}
|
||
|
|
||
|
// Because <number of elements> = <initial size> * 2^(number of blocks)
|
||
|
double const a = ceil((double)got_element_count / (double)initial_size);
|
||
|
double const b = log2(a);
|
||
|
size_t const expected_block_count = ((size_t)b) + 1;
|
||
|
|
||
|
assert(got_block_count == expected_block_count);
|
||
|
}
|