2024-01-24 03:20:07 +03:00
|
|
|
|
#include "btest-overrides.h"
|
2023-12-07 02:17:51 +03:00
|
|
|
|
#include "btree.h"
|
|
|
|
|
#include "btree.c"
|
|
|
|
|
|
2023-12-14 05:32:45 +03:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stdio.h>
|
2023-12-07 02:17:51 +03:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
_test_nodeinteg(BT_state *state, BT_findpath *path,
|
|
|
|
|
vaof_t lo, vaof_t hi, pgno_t pg)
|
|
|
|
|
{
|
|
|
|
|
size_t childidx = 0;
|
|
|
|
|
BT_page *parent = 0;
|
|
|
|
|
|
|
|
|
|
assert(SUCC(_bt_find(state, path, lo, hi)));
|
|
|
|
|
parent = path->path[path->depth];
|
|
|
|
|
childidx = path->idx[path->depth];
|
|
|
|
|
assert(parent->datk[childidx].fo == pg);
|
|
|
|
|
assert(parent->datk[childidx].va == lo);
|
|
|
|
|
assert(parent->datk[childidx+1].va == hi);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-15 14:36:53 +03:00
|
|
|
|
static size_t
|
|
|
|
|
_mlist_sizep(BT_mlistnode *head)
|
|
|
|
|
/* calculate the size of the mlist in pages */
|
|
|
|
|
{
|
|
|
|
|
size_t sz = 0;
|
|
|
|
|
while (head) {
|
2023-12-19 06:02:58 +03:00
|
|
|
|
size_t sz_p = addr2off(head->hi) - addr2off(head->lo);
|
|
|
|
|
sz += sz_p;
|
2023-12-15 14:36:53 +03:00
|
|
|
|
head = head->next;
|
|
|
|
|
}
|
|
|
|
|
return sz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static size_t
|
|
|
|
|
_flist_sizep(BT_flistnode *head)
|
|
|
|
|
/* calculate the size of the flist in pages */
|
|
|
|
|
{
|
|
|
|
|
size_t sz = 0;
|
|
|
|
|
while (head) {
|
2023-12-19 06:02:58 +03:00
|
|
|
|
size_t sz_p = head->hi - head->lo;
|
|
|
|
|
sz += sz_p;
|
2023-12-15 14:36:53 +03:00
|
|
|
|
head = head->next;
|
|
|
|
|
}
|
|
|
|
|
return sz;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-18 04:05:03 +03:00
|
|
|
|
static BT_mlistnode *
|
|
|
|
|
_mlist_copy(BT_state *state)
|
|
|
|
|
{
|
|
|
|
|
BT_mlistnode *head = state->mlist;
|
|
|
|
|
BT_mlistnode *ret, *prev;
|
|
|
|
|
ret = prev = calloc(1, sizeof *ret);
|
|
|
|
|
memcpy(ret, head, sizeof *head);
|
|
|
|
|
ret->next = 0;
|
|
|
|
|
head = head->next;
|
|
|
|
|
while (head) {
|
|
|
|
|
BT_mlistnode *copy = calloc(1, sizeof *copy);
|
|
|
|
|
memcpy(copy, head, sizeof *head);
|
|
|
|
|
prev->next = copy;
|
|
|
|
|
prev = copy;
|
|
|
|
|
head = head->next;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BT_nlistnode *
|
|
|
|
|
_nlist_copy(BT_state *state)
|
|
|
|
|
{
|
|
|
|
|
BT_nlistnode *head = state->nlist;
|
|
|
|
|
BT_nlistnode *ret, *prev;
|
|
|
|
|
ret = prev = calloc(1, sizeof *ret);
|
|
|
|
|
memcpy(ret, head, sizeof *head);
|
|
|
|
|
ret->next = 0;
|
|
|
|
|
head = head->next;
|
|
|
|
|
while (head) {
|
|
|
|
|
BT_nlistnode *copy = calloc(1, sizeof *copy);
|
|
|
|
|
memcpy(copy, head, sizeof *head);
|
|
|
|
|
prev->next = copy;
|
|
|
|
|
prev = copy;
|
|
|
|
|
head = head->next;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static BT_flistnode *
|
|
|
|
|
_flist_copy(BT_state *state)
|
|
|
|
|
{
|
|
|
|
|
BT_flistnode *head = state->flist;
|
|
|
|
|
BT_flistnode *ret, *prev;
|
|
|
|
|
ret = prev = calloc(1, sizeof *ret);
|
|
|
|
|
memcpy(ret, head, sizeof *head);
|
|
|
|
|
ret->next = 0;
|
|
|
|
|
head = head->next;
|
|
|
|
|
while (head) {
|
|
|
|
|
BT_flistnode *copy = calloc(1, sizeof *copy);
|
|
|
|
|
memcpy(copy, head, sizeof *head);
|
|
|
|
|
prev->next = copy;
|
|
|
|
|
prev = copy;
|
|
|
|
|
head = head->next;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
_mlist_eq(BT_mlistnode *l, BT_mlistnode *r)
|
|
|
|
|
{
|
|
|
|
|
while (l && r) {
|
2023-12-19 06:02:58 +03:00
|
|
|
|
if (l->lo != r->lo)
|
2023-12-18 04:05:03 +03:00
|
|
|
|
bp(0);
|
2023-12-19 06:02:58 +03:00
|
|
|
|
if (l->hi != r->hi)
|
2023-12-18 04:05:03 +03:00
|
|
|
|
bp(0);
|
|
|
|
|
l = l->next; r = r->next;
|
|
|
|
|
}
|
|
|
|
|
if (l == 0 && r == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
bp(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
_nlist_eq(BT_nlistnode *l, BT_nlistnode *r)
|
|
|
|
|
{
|
|
|
|
|
while (l && r) {
|
2023-12-19 06:02:58 +03:00
|
|
|
|
if (l->lo != r->lo)
|
2023-12-18 04:05:03 +03:00
|
|
|
|
bp(0);
|
2023-12-19 06:02:58 +03:00
|
|
|
|
if (l->hi != r->hi)
|
2023-12-18 04:05:03 +03:00
|
|
|
|
bp(0);
|
|
|
|
|
l = l->next; r = r->next;
|
|
|
|
|
}
|
|
|
|
|
if (l == 0 && r == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
bp(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
_flist_eq(BT_flistnode *l, BT_flistnode *r)
|
|
|
|
|
{
|
|
|
|
|
while (l && r) {
|
2023-12-19 06:02:58 +03:00
|
|
|
|
if (l->lo != r->lo)
|
2023-12-18 04:05:03 +03:00
|
|
|
|
bp(0);
|
2023-12-19 06:02:58 +03:00
|
|
|
|
if (l->hi != r->hi)
|
2023-12-18 04:05:03 +03:00
|
|
|
|
bp(0);
|
|
|
|
|
l = l->next; r = r->next;
|
|
|
|
|
}
|
|
|
|
|
if (l == 0 && r == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
bp(0);
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-07 02:17:51 +03:00
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
|
{
|
2023-12-20 05:55:57 +03:00
|
|
|
|
DPRINTF("PMA Max Storage: %lld", ((uint64_t)UINT32_MAX * BT_PAGESIZE) - BLK_BASE_LEN_TOTAL);
|
2023-12-07 02:17:51 +03:00
|
|
|
|
DPUTS("PMA Tests");
|
|
|
|
|
|
2023-12-09 02:15:31 +03:00
|
|
|
|
BT_state *state1;
|
2023-12-07 02:17:51 +03:00
|
|
|
|
BT_findpath path = {0};
|
|
|
|
|
int rc = 0;
|
|
|
|
|
|
2023-12-09 02:15:31 +03:00
|
|
|
|
|
|
|
|
|
DPUTS("== test 2: malloc");
|
|
|
|
|
BT_state *state2;
|
|
|
|
|
|
|
|
|
|
bt_state_new(&state2);
|
2023-12-14 02:25:35 +03:00
|
|
|
|
if (mkdir("./pmatest2", 0774) == -1)
|
|
|
|
|
return errno;
|
2023-12-09 02:15:31 +03:00
|
|
|
|
assert(SUCC(bt_state_open(state2, "./pmatest2", 0, 0644)));
|
|
|
|
|
|
|
|
|
|
void *t2a = bt_malloc(state2, 10);
|
|
|
|
|
bt_free(state2, t2a, (BT_page*)t2a + 10);
|
2023-12-09 02:40:15 +03:00
|
|
|
|
void *t2b = bt_malloc(state2, 10);
|
|
|
|
|
/* should have pulled the same pointer due to eager mlist coalescing */
|
2023-12-09 02:58:24 +03:00
|
|
|
|
assert(t2a == t2b);
|
2023-12-09 02:40:15 +03:00
|
|
|
|
ZERO(&path, sizeof path);
|
|
|
|
|
_bt_find(state2, &path, addr2off(t2b), addr2off((BT_page *)t2b + 10));
|
2023-12-09 03:40:37 +03:00
|
|
|
|
#define T2P1_PRNT0 (path.path[path.depth])
|
|
|
|
|
#define T2P1_CIDX0 (path.idx[path.depth])
|
|
|
|
|
#define T2P1_CIDX1 (path.idx[path.depth] + 1)
|
|
|
|
|
/* check length as represented in btree */
|
|
|
|
|
assert(T2P1_PRNT0->datk[T2P1_CIDX1].va
|
|
|
|
|
- T2P1_PRNT0->datk[T2P1_CIDX0].va
|
|
|
|
|
== 10);
|
2023-12-09 02:40:15 +03:00
|
|
|
|
bt_free(state2, t2b, (BT_page*)t2b + 10);
|
|
|
|
|
ZERO(&path, sizeof path);
|
|
|
|
|
_bt_find(state2, &path, addr2off(t2b), addr2off((BT_page *)t2b + 10));
|
2023-12-09 03:40:37 +03:00
|
|
|
|
/* fo should be zero (free) */
|
|
|
|
|
assert(path.path[path.depth]->datk[path.idx[path.depth]].fo == 0);
|
2023-12-09 02:40:15 +03:00
|
|
|
|
/* should invoke deletion coalescing - 10 page free range in btree */
|
|
|
|
|
void *t2c = bt_malloc(state2, 20);
|
2023-12-09 02:15:31 +03:00
|
|
|
|
|
2023-12-14 05:32:45 +03:00
|
|
|
|
bt_state_close(state2);
|
|
|
|
|
|
2024-01-24 03:20:07 +03:00
|
|
|
|
#if 0
|
2024-01-25 03:09:26 +03:00
|
|
|
|
/*
|
|
|
|
|
test 3 pairs poorly with an overridden BT_DAT_MAKEYS=10 leading to huge
|
|
|
|
|
persistent file growth. Disabling for now. Is there some way we can easily
|
|
|
|
|
override these values at a per-test level without altering a code?
|
|
|
|
|
*/
|
2024-01-24 03:20:07 +03:00
|
|
|
|
|
2023-12-14 05:32:45 +03:00
|
|
|
|
|
|
|
|
|
DPUTS("== test 3: ephemeral structure restoration");
|
|
|
|
|
BT_state *state3;
|
|
|
|
|
|
|
|
|
|
bt_state_new(&state3);
|
|
|
|
|
if (mkdir("./pmatest3", 0774) == -1)
|
|
|
|
|
return errno;
|
|
|
|
|
assert(SUCC(bt_state_open(state3, "./pmatest3", 0, 0644)));
|
|
|
|
|
|
|
|
|
|
typedef struct lohi_pair lohi_pair;
|
|
|
|
|
struct lohi_pair
|
|
|
|
|
{
|
|
|
|
|
BT_page *lo;
|
|
|
|
|
BT_page *hi;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define ITERATIONS 1000
|
|
|
|
|
#define MAXALLOCPG 0xFF
|
|
|
|
|
lohi_pair allocs[ITERATIONS] = {0};
|
2023-12-15 14:36:53 +03:00
|
|
|
|
size_t alloc_sizp = 0;
|
|
|
|
|
size_t flist_sizp = _flist_sizep(state3->flist);
|
|
|
|
|
size_t mlist_sizp = _mlist_sizep(state3->mlist);
|
2023-12-20 01:48:04 +03:00
|
|
|
|
BT_meta *meta = state3->meta_pages[state3->which];
|
|
|
|
|
BT_page *root = _node_get(state3, meta->root);
|
|
|
|
|
size_t N;
|
2023-12-14 05:32:45 +03:00
|
|
|
|
for (size_t i = 0; i < ITERATIONS; i++) {
|
2023-12-14 05:36:34 +03:00
|
|
|
|
/* malloc a random number of pages <= 256 and store in the allocs array */
|
2023-12-15 08:50:26 +03:00
|
|
|
|
int pages = random();
|
2023-12-14 05:32:45 +03:00
|
|
|
|
pages &= MAXALLOCPG;
|
2023-12-14 05:36:34 +03:00
|
|
|
|
pages += 1;
|
2023-12-14 05:32:45 +03:00
|
|
|
|
allocs[i].lo = bt_malloc(state3, pages);
|
|
|
|
|
allocs[i].hi = allocs[i].lo + pages;
|
2023-12-15 14:36:53 +03:00
|
|
|
|
alloc_sizp += pages;
|
|
|
|
|
/* validate size changes to mlist and flist */
|
2024-01-13 02:11:42 +03:00
|
|
|
|
/* ;;: no longer a valid comparison since the flist may have grown */
|
|
|
|
|
/* assert(_flist_sizep(state3->flist) */
|
|
|
|
|
/* == (flist_sizp - alloc_sizp)); */
|
2023-12-15 14:36:53 +03:00
|
|
|
|
assert(_mlist_sizep(state3->mlist)
|
|
|
|
|
== (mlist_sizp - alloc_sizp));
|
2023-12-20 01:48:04 +03:00
|
|
|
|
N = _bt_numkeys(root);
|
2024-01-25 03:09:26 +03:00
|
|
|
|
/* assert(root->datk[N-2].fo == 0); */
|
2023-12-14 05:32:45 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* sync the state */
|
2023-12-15 08:50:26 +03:00
|
|
|
|
/* bt_sync(state3); */
|
|
|
|
|
|
|
|
|
|
/* TODO: close and reopen state. validate ephemeral structures */
|
|
|
|
|
|
2023-12-15 14:36:53 +03:00
|
|
|
|
flist_sizp = _flist_sizep(state3->flist);
|
|
|
|
|
mlist_sizp = _mlist_sizep(state3->mlist);
|
|
|
|
|
alloc_sizp = 0;
|
2023-12-20 01:48:04 +03:00
|
|
|
|
/* for (size_t i = 0; i < ITERATIONS / 2; i++) { */
|
|
|
|
|
/* /\* free half of the allocations *\/ */
|
|
|
|
|
/* bt_free(state3, allocs[i].lo, allocs[i].hi); */
|
|
|
|
|
/* alloc_sizp += allocs[i].hi - allocs[i].lo; */
|
|
|
|
|
/* /\* validate size changes to mlist *\/ */
|
|
|
|
|
/* assert(_mlist_sizep(state3->mlist) */
|
|
|
|
|
/* == (mlist_sizp + alloc_sizp)); */
|
|
|
|
|
/* } */
|
2023-12-14 05:32:45 +03:00
|
|
|
|
|
2023-12-18 04:05:03 +03:00
|
|
|
|
/* copy ephemeral structures */
|
|
|
|
|
BT_mlistnode *mlist_copy = _mlist_copy(state3);
|
|
|
|
|
BT_nlistnode *nlist_copy = _nlist_copy(state3);
|
|
|
|
|
BT_flistnode *flist_copy = _flist_copy(state3);
|
|
|
|
|
assert(_mlist_eq(mlist_copy, state3->mlist));
|
|
|
|
|
assert(_nlist_eq(nlist_copy, state3->nlist));
|
|
|
|
|
assert(_flist_eq(flist_copy, state3->flist));
|
|
|
|
|
|
2023-12-20 01:48:04 +03:00
|
|
|
|
meta = state3->meta_pages[state3->which];
|
|
|
|
|
BT_meta metacopy = {0};
|
|
|
|
|
memcpy(&metacopy, meta, sizeof metacopy);
|
2024-01-12 02:26:33 +03:00
|
|
|
|
|
2023-12-16 02:38:52 +03:00
|
|
|
|
bt_state_close(state3);
|
|
|
|
|
|
|
|
|
|
bt_state_new(&state3);
|
|
|
|
|
|
|
|
|
|
assert(SUCC(bt_state_open(state3, "./pmatest3", 0, 0644)));
|
2023-12-18 04:05:03 +03:00
|
|
|
|
|
|
|
|
|
/* compare for equality copies of ephemeral structures with restored ephemeral
|
|
|
|
|
structures */
|
2023-12-20 01:48:04 +03:00
|
|
|
|
meta = state3->meta_pages[state3->which];
|
2024-01-12 02:26:33 +03:00
|
|
|
|
/* ;;: fixme */
|
|
|
|
|
/* assert(meta->root == metacopy.root); */
|
|
|
|
|
/* assert(_mlist_eq(mlist_copy, state3->mlist)); */
|
|
|
|
|
/* assert(_nlist_eq(nlist_copy, state3->nlist)); */
|
|
|
|
|
/* assert(_flist_eq(flist_copy, state3->flist)); */
|
|
|
|
|
|
|
|
|
|
bt_state_close(state3);
|
|
|
|
|
|
2024-01-25 03:09:26 +03:00
|
|
|
|
#endif
|
|
|
|
|
|
2024-01-24 03:20:07 +03:00
|
|
|
|
|
2024-01-12 02:26:33 +03:00
|
|
|
|
DPUTS("== test 4: backing file extension");
|
|
|
|
|
BT_state *state4;
|
|
|
|
|
|
|
|
|
|
bt_state_new(&state4);
|
|
|
|
|
if (mkdir("./pmatest4", 0774) == -1)
|
|
|
|
|
return errno;
|
|
|
|
|
assert(SUCC(bt_state_open(state4, "./pmatest4", 0, 0644)));
|
|
|
|
|
|
2024-01-13 04:02:59 +03:00
|
|
|
|
#define PMA_INITIAL_SIZE_p PMA_GROW_SIZE_p
|
2024-01-12 02:26:33 +03:00
|
|
|
|
BYTE *t4a = bt_malloc(state4, PMA_GROW_SIZE_p * 2);
|
2024-04-02 21:28:58 +03:00
|
|
|
|
BYTE *t4a_copy = malloc(PMA_GROW_SIZE_b * 2);
|
2024-01-12 02:26:33 +03:00
|
|
|
|
BYTE *t4b = t4a;
|
|
|
|
|
for (size_t i = 0; i < PMA_GROW_SIZE_b * 2; i++) {
|
|
|
|
|
*t4b++ = rand();
|
|
|
|
|
}
|
2023-12-15 08:50:26 +03:00
|
|
|
|
|
2024-01-13 04:02:59 +03:00
|
|
|
|
assert(state4->file_size_p == PMA_INITIAL_SIZE_p + PMA_GROW_SIZE_p * 2);
|
|
|
|
|
/* given the allocation pattern the head of the flist should also be the
|
|
|
|
|
tail. The hi page here should match the file size */
|
|
|
|
|
assert(state4->flist->hi == state4->file_size_p);
|
|
|
|
|
|
2024-04-02 21:28:58 +03:00
|
|
|
|
memcpy(t4a_copy, t4a, PMA_GROW_SIZE_b * 2);
|
2024-01-13 04:02:59 +03:00
|
|
|
|
bt_state_close(state4);
|
|
|
|
|
|
|
|
|
|
bt_state_new(&state4);
|
|
|
|
|
|
|
|
|
|
assert(SUCC(bt_state_open(state4, "./pmatest4", 0, 0644)));
|
|
|
|
|
|
|
|
|
|
assert(state4->file_size_p == PMA_INITIAL_SIZE_p + PMA_GROW_SIZE_p * 2);
|
2024-04-13 01:28:32 +03:00
|
|
|
|
assert(state4->flist->hi == state4->file_size_p);
|
2024-01-13 04:02:59 +03:00
|
|
|
|
|
2024-04-02 21:28:58 +03:00
|
|
|
|
for (size_t i = 0; i < PMA_GROW_SIZE_b * 2; i++)
|
|
|
|
|
assert(t4a_copy[i] == t4a[i]);
|
|
|
|
|
|
|
|
|
|
void *t4c = bt_malloc(state4, 10);
|
|
|
|
|
bt_sync(state4);
|
|
|
|
|
void *t4d = bt_malloc(state4, 10);
|
|
|
|
|
bt_sync(state4);
|
|
|
|
|
void *t4e = bt_malloc(state4, 10);
|
|
|
|
|
bt_sync(state4);
|
|
|
|
|
|
|
|
|
|
|
2024-01-24 03:20:07 +03:00
|
|
|
|
|
|
|
|
|
DPUTS("== test 5: partition striping");
|
|
|
|
|
BT_state *state5;
|
|
|
|
|
|
|
|
|
|
bt_state_new(&state5);
|
|
|
|
|
if (mkdir("./pmatest5", 0774) == -1)
|
|
|
|
|
return errno;
|
|
|
|
|
assert(SUCC(bt_state_open(state5, "./pmatest5", 0, 0644)));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define NODEPART0_MAX_NODES B2PAGES(BLK_BASE_LEN0_b)
|
|
|
|
|
|
|
|
|
|
/* the maximum number of allocations is dependent on the allocation
|
|
|
|
|
pattern. So this is approximate, but that's fine */
|
|
|
|
|
#define NODEPART0_MAX_ALLOCS (NODEPART0_MAX_NODES * BT_DAT_MAXKEYS)
|
|
|
|
|
|
|
|
|
|
/* ;;: ^ this is 254016, is that right? In that case, we may need to
|
|
|
|
|
artificially decrease the partition sizes. We could also decrease the
|
|
|
|
|
page size. Oh wait, why don't we just decrease the maxkeys constant? */
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < NODEPART0_MAX_ALLOCS; i++) {
|
|
|
|
|
bt_malloc(state5, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* we should be using the second partition */
|
|
|
|
|
pgno_t t4partoff0 = state5->meta_pages[state5->which]->blk_base[1];
|
|
|
|
|
assert(t4partoff0 != 0);
|
|
|
|
|
|
|
|
|
|
/* close and reopen the state */
|
|
|
|
|
bt_state_close(state5);
|
|
|
|
|
bt_state_new(&state5);
|
|
|
|
|
assert(SUCC(bt_state_open(state5, "./pmatest5", 0, 0644)));
|
|
|
|
|
|
|
|
|
|
/* the partition offset should be the same */
|
|
|
|
|
assert(t4partoff0 == state5->meta_pages[state5->which]->blk_base[1]);
|
|
|
|
|
|
2023-12-07 02:17:51 +03:00
|
|
|
|
return 0;
|
|
|
|
|
}
|