#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(_WIN32) && defined(SAKURA_BPTREE_BUILDING_DLL)
#define SAKURA_BPTREE_API __declspec(dllexport)
#elif defined(_WIN32)
#define SAKURA_BPTREE_API __declspec(dllimport)
#else
#define SAKURA_BPTREE_API
#endif
#define SAKURA_SHA32_KEY_SIZE 32u
typedef struct sakura_bptree sakura_bptree;
typedef enum sakura_bptree_status {
SAKURA_BPTREE_OK = 0,
SAKURA_BPTREE_NOT_FOUND = 1,
SAKURA_BPTREE_INVALID_ARGUMENT = 2,
SAKURA_BPTREE_OUT_OF_MEMORY = 3,
SAKURA_BPTREE_IO_ERROR = 4,
SAKURA_BPTREE_CORRUPT_FILE = 5
} sakura_bptree_status;
typedef void (*sakura_bptree_iter_cb)(const uint8_t key[SAKURA_SHA32_KEY_SIZE],
const void *value,
uint64_t value_len,
void *user_data);
#define BPTREE_ORDER 32
#define BPTREE_MAX_KEYS (BPTREE_ORDER - 1)
#define BPTREE_MAGIC "SKBPT1\0\0"
#define BPTREE_MAGIC_SIZE 8u
#define BPTREE_VERSION 1u
typedef struct bptree_value {
uint8_t *data;
uint64_t len;
} bptree_value;
typedef struct bptree_node {
int is_leaf;
int key_count;
uint8_t keys[BPTREE_ORDER][SAKURA_SHA32_KEY_SIZE];
struct bptree_node *parent;
struct bptree_node *next;
union {
struct bptree_node *children[BPTREE_ORDER + 1];
bptree_value values[BPTREE_ORDER];
} slots;
} bptree_node;
struct sakura_bptree {
bptree_node *root;
bptree_node *first_leaf;
uint64_t len;
};
static int key_cmp(const uint8_t a[SAKURA_SHA32_KEY_SIZE],
const uint8_t b[SAKURA_SHA32_KEY_SIZE])
{
return memcmp(a, b, SAKURA_SHA32_KEY_SIZE);
}
static void key_copy(uint8_t dst[SAKURA_SHA32_KEY_SIZE],
const uint8_t src[SAKURA_SHA32_KEY_SIZE])
{
memcpy(dst, src, SAKURA_SHA32_KEY_SIZE);
}
static bptree_node *node_create(int is_leaf)
{
bptree_node *node = (bptree_node *)calloc(1, sizeof(bptree_node));
if (node == NULL) {
return NULL;
}
node->is_leaf = is_leaf;
return node;
}
static void node_free(bptree_node *node)
{
int i;
if (node == NULL) {
return;
}
if (node->is_leaf) {
for (i = 0; i < node->key_count; i++) {
free(node->slots.values[i].data);
}
} else {
for (i = 0; i <= node->key_count; i++) {
node_free(node->slots.children[i]);
}
}
free(node);
}
static int lower_bound_keys(const uint8_t keys[][SAKURA_SHA32_KEY_SIZE],
int count,
const uint8_t key[SAKURA_SHA32_KEY_SIZE])
{
int lo = 0;
int hi = count;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (key_cmp(keys[mid], key) < 0) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
static int upper_bound_keys(const uint8_t keys[][SAKURA_SHA32_KEY_SIZE],
int count,
const uint8_t key[SAKURA_SHA32_KEY_SIZE])
{
int lo = 0;
int hi = count;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (key_cmp(keys[mid], key) <= 0) {
lo = mid + 1;
} else {
hi = mid;
}
}
return lo;
}
static bptree_node *find_leaf(const sakura_bptree *tree,
const uint8_t key[SAKURA_SHA32_KEY_SIZE])
{
bptree_node *node;
if (tree == NULL || tree->root == NULL) {
return NULL;
}
node = tree->root;
while (!node->is_leaf) {
int idx = upper_bound_keys(node->keys, node->key_count, key);
node = node->slots.children[idx];
}
return node;
}
static void refresh_first_leaf(sakura_bptree *tree)
{
bptree_node *node;
if (tree == NULL || tree->root == NULL) {
return;
}
node = tree->root;
while (!node->is_leaf) {
node = node->slots.children[0];
}
tree->first_leaf = node;
}
static int subtree_first_key(const bptree_node *node,
uint8_t out[SAKURA_SHA32_KEY_SIZE])
{
while (node != NULL && !node->is_leaf) {
node = node->slots.children[0];
}
if (node == NULL || node->key_count == 0) {
return 0;
}
key_copy(out, node->keys[0]);
return 1;
}
static void update_parent_separator(bptree_node *node)
{
bptree_node *parent;
uint8_t first_key[SAKURA_SHA32_KEY_SIZE];
while (node != NULL && node->parent != NULL) {
int i;
parent = node->parent;
for (i = 1; i <= parent->key_count; i++) {
if (parent->slots.children[i] == node) {
if (subtree_first_key(node, first_key)) {
key_copy(parent->keys[i - 1], first_key);
}
return;
}
}
node = parent;
}
}
static sakura_bptree_status value_make(bptree_value *slot,
const void *value,
uint64_t value_len)
{
uint8_t *copy = NULL;
if (value_len > 0 && value == NULL) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
if (value_len > 0) {
if ((uint64_t)((size_t)value_len) != value_len) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
copy = (uint8_t *)malloc((size_t)value_len);
if (copy == NULL) {
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
memcpy(copy, value, (size_t)value_len);
}
slot->data = copy;
slot->len = value_len;
return SAKURA_BPTREE_OK;
}
static sakura_bptree_status value_replace(bptree_value *slot,
const void *value,
uint64_t value_len)
{
bptree_value next;
sakura_bptree_status status;
next.data = NULL;
next.len = 0;
status = value_make(&next, value, value_len);
if (status != SAKURA_BPTREE_OK) {
return status;
}
free(slot->data);
*slot = next;
return SAKURA_BPTREE_OK;
}
static sakura_bptree_status insert_into_parent(sakura_bptree *tree,
bptree_node *left,
const uint8_t key[SAKURA_SHA32_KEY_SIZE],
bptree_node *right);
static sakura_bptree_status split_internal(sakura_bptree *tree, bptree_node *node)
{
bptree_node *right;
int split;
int right_keys;
int i;
uint8_t promote[SAKURA_SHA32_KEY_SIZE];
right = node_create(0);
if (right == NULL) {
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
split = BPTREE_ORDER / 2;
right_keys = node->key_count - split - 1;
key_copy(promote, node->keys[split]);
for (i = 0; i < right_keys; i++) {
key_copy(right->keys[i], node->keys[split + 1 + i]);
}
for (i = 0; i <= right_keys; i++) {
right->slots.children[i] = node->slots.children[split + 1 + i];
if (right->slots.children[i] != NULL) {
right->slots.children[i]->parent = right;
}
node->slots.children[split + 1 + i] = NULL;
}
right->key_count = right_keys;
node->key_count = split;
right->parent = node->parent;
return insert_into_parent(tree, node, promote, right);
}
static sakura_bptree_status insert_into_parent(sakura_bptree *tree,
bptree_node *left,
const uint8_t key[SAKURA_SHA32_KEY_SIZE],
bptree_node *right)
{
bptree_node *parent;
int left_index;
int i;
if (left->parent == NULL) {
bptree_node *root = node_create(0);
if (root == NULL) {
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
key_copy(root->keys[0], key);
root->slots.children[0] = left;
root->slots.children[1] = right;
root->key_count = 1;
left->parent = root;
right->parent = root;
tree->root = root;
return SAKURA_BPTREE_OK;
}
parent = left->parent;
left_index = 0;
while (left_index <= parent->key_count && parent->slots.children[left_index] != left) {
left_index++;
}
if (left_index > parent->key_count) {
return SAKURA_BPTREE_CORRUPT_FILE;
}
for (i = parent->key_count; i > left_index; i--) {
key_copy(parent->keys[i], parent->keys[i - 1]);
}
for (i = parent->key_count + 1; i > left_index + 1; i--) {
parent->slots.children[i] = parent->slots.children[i - 1];
}
key_copy(parent->keys[left_index], key);
parent->slots.children[left_index + 1] = right;
right->parent = parent;
parent->key_count++;
if (parent->key_count <= BPTREE_MAX_KEYS) {
return SAKURA_BPTREE_OK;
}
return split_internal(tree, parent);
}
static sakura_bptree_status split_leaf_and_insert(sakura_bptree *tree,
bptree_node *leaf,
const uint8_t key[SAKURA_SHA32_KEY_SIZE],
const void *value,
uint64_t value_len)
{
uint8_t temp_keys[BPTREE_ORDER][SAKURA_SHA32_KEY_SIZE];
bptree_value temp_values[BPTREE_ORDER];
bptree_value new_value;
bptree_node *right;
int insert_at;
int split;
int right_count;
int i;
sakura_bptree_status status;
memset(temp_values, 0, sizeof(temp_values));
new_value.data = NULL;
new_value.len = 0;
status = value_make(&new_value, value, value_len);
if (status != SAKURA_BPTREE_OK) {
return status;
}
right = node_create(1);
if (right == NULL) {
free(new_value.data);
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
insert_at = lower_bound_keys(leaf->keys, leaf->key_count, key);
for (i = 0; i < insert_at; i++) {
key_copy(temp_keys[i], leaf->keys[i]);
temp_values[i] = leaf->slots.values[i];
leaf->slots.values[i].data = NULL;
leaf->slots.values[i].len = 0;
}
key_copy(temp_keys[insert_at], key);
temp_values[insert_at] = new_value;
for (i = insert_at; i < leaf->key_count; i++) {
key_copy(temp_keys[i + 1], leaf->keys[i]);
temp_values[i + 1] = leaf->slots.values[i];
leaf->slots.values[i].data = NULL;
leaf->slots.values[i].len = 0;
}
split = BPTREE_ORDER / 2;
leaf->key_count = split;
right_count = BPTREE_ORDER - split;
right->key_count = right_count;
right->parent = leaf->parent;
right->next = leaf->next;
leaf->next = right;
for (i = 0; i < split; i++) {
key_copy(leaf->keys[i], temp_keys[i]);
leaf->slots.values[i] = temp_values[i];
}
for (i = 0; i < right_count; i++) {
key_copy(right->keys[i], temp_keys[split + i]);
right->slots.values[i] = temp_values[split + i];
}
tree->len++;
return insert_into_parent(tree, leaf, right->keys[0], right);
}
SAKURA_BPTREE_API sakura_bptree *sakura_bptree_create(void)
{
sakura_bptree *tree = (sakura_bptree *)calloc(1, sizeof(sakura_bptree));
if (tree == NULL) {
return NULL;
}
tree->root = node_create(1);
if (tree->root == NULL) {
free(tree);
return NULL;
}
tree->first_leaf = tree->root;
return tree;
}
SAKURA_BPTREE_API void sakura_bptree_free(sakura_bptree *tree)
{
if (tree == NULL) {
return;
}
node_free(tree->root);
free(tree);
}
SAKURA_BPTREE_API uint64_t sakura_bptree_len(const sakura_bptree *tree)
{
return tree == NULL ? 0 : tree->len;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_put(sakura_bptree *tree,
const uint8_t key[SAKURA_SHA32_KEY_SIZE],
const void *value,
uint64_t value_len)
{
bptree_node *leaf;
int pos;
int i;
sakura_bptree_status status;
if (tree == NULL || key == NULL || (value == NULL && value_len > 0)) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
leaf = find_leaf(tree, key);
if (leaf == NULL) {
return SAKURA_BPTREE_CORRUPT_FILE;
}
pos = lower_bound_keys(leaf->keys, leaf->key_count, key);
if (pos < leaf->key_count && key_cmp(leaf->keys[pos], key) == 0) {
return value_replace(&leaf->slots.values[pos], value, value_len);
}
if (leaf->key_count == BPTREE_MAX_KEYS) {
return split_leaf_and_insert(tree, leaf, key, value, value_len);
}
for (i = leaf->key_count; i > pos; i--) {
key_copy(leaf->keys[i], leaf->keys[i - 1]);
leaf->slots.values[i] = leaf->slots.values[i - 1];
}
key_copy(leaf->keys[pos], key);
leaf->slots.values[pos].data = NULL;
leaf->slots.values[pos].len = 0;
status = value_make(&leaf->slots.values[pos], value, value_len);
if (status != SAKURA_BPTREE_OK) {
return status;
}
leaf->key_count++;
tree->len++;
if (pos == 0) {
update_parent_separator(leaf);
}
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_get(const sakura_bptree *tree,
const uint8_t key[SAKURA_SHA32_KEY_SIZE],
const void **value,
uint64_t *value_len)
{
bptree_node *leaf;
int pos;
if (tree == NULL || key == NULL || value == NULL || value_len == NULL) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
leaf = find_leaf(tree, key);
if (leaf == NULL) {
return SAKURA_BPTREE_NOT_FOUND;
}
pos = lower_bound_keys(leaf->keys, leaf->key_count, key);
if (pos >= leaf->key_count || key_cmp(leaf->keys[pos], key) != 0) {
return SAKURA_BPTREE_NOT_FOUND;
}
*value = leaf->slots.values[pos].data;
*value_len = leaf->slots.values[pos].len;
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_copy_value(const sakura_bptree *tree,
const uint8_t key[SAKURA_SHA32_KEY_SIZE],
void *buffer,
uint64_t buffer_len,
uint64_t *value_len)
{
const void *value;
uint64_t len;
sakura_bptree_status status;
if (value_len == NULL || (buffer == NULL && buffer_len > 0)) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
status = sakura_bptree_get(tree, key, &value, &len);
if (status != SAKURA_BPTREE_OK) {
return status;
}
*value_len = len;
if (buffer_len < len) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
if (len > 0) {
memcpy(buffer, value, (size_t)len);
}
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_remove(sakura_bptree *tree,
const uint8_t key[SAKURA_SHA32_KEY_SIZE])
{
bptree_node *leaf;
int pos;
int i;
if (tree == NULL || key == NULL) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
leaf = find_leaf(tree, key);
if (leaf == NULL) {
return SAKURA_BPTREE_NOT_FOUND;
}
pos = lower_bound_keys(leaf->keys, leaf->key_count, key);
if (pos >= leaf->key_count || key_cmp(leaf->keys[pos], key) != 0) {
return SAKURA_BPTREE_NOT_FOUND;
}
free(leaf->slots.values[pos].data);
for (i = pos; i < leaf->key_count - 1; i++) {
key_copy(leaf->keys[i], leaf->keys[i + 1]);
leaf->slots.values[i] = leaf->slots.values[i + 1];
}
leaf->slots.values[leaf->key_count - 1].data = NULL;
leaf->slots.values[leaf->key_count - 1].len = 0;
leaf->key_count--;
tree->len--;
if (leaf->key_count > 0 && pos == 0) {
update_parent_separator(leaf);
}
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API void sakura_bptree_iterate(const sakura_bptree *tree,
sakura_bptree_iter_cb callback,
void *user_data)
{
bptree_node *node;
if (tree == NULL || callback == NULL) {
return;
}
node = tree->first_leaf;
while (node != NULL) {
int i;
for (i = 0; i < node->key_count; i++) {
callback(node->keys[i], node->slots.values[i].data,
node->slots.values[i].len, user_data);
}
node = node->next;
}
}
typedef struct buf_writer {
uint8_t *data;
uint64_t len;
uint64_t cap;
} buf_writer;
static int buf_writer_init(buf_writer *w)
{
w->data = (uint8_t *)malloc(256);
if (w->data == NULL) {
return 0;
}
w->len = 0;
w->cap = 256;
return 1;
}
static int buf_writer_write(buf_writer *w, const void *src, uint64_t n)
{
uint64_t needed = w->len + n;
if (needed > w->cap) {
uint64_t new_cap = w->cap * 2;
uint8_t *p;
while (new_cap < needed) {
new_cap *= 2;
}
p = (uint8_t *)realloc(w->data, (size_t)new_cap);
if (p == NULL) {
return 0;
}
w->data = p;
w->cap = new_cap;
}
memcpy(w->data + w->len, src, (size_t)n);
w->len += n;
return 1;
}
typedef struct buf_reader {
const uint8_t *data;
uint64_t len;
uint64_t pos;
} buf_reader;
static int buf_reader_read(buf_reader *r, void *dst, uint64_t n)
{
if (r->pos + n > r->len) {
return 0;
}
memcpy(dst, r->data + r->pos, (size_t)n);
r->pos += n;
return 1;
}
static int buf_write_u32(buf_writer *w, uint32_t value)
{
uint8_t b[4];
b[0] = (uint8_t)(value & 0xffu);
b[1] = (uint8_t)((value >> 8) & 0xffu);
b[2] = (uint8_t)((value >> 16) & 0xffu);
b[3] = (uint8_t)((value >> 24) & 0xffu);
return buf_writer_write(w, b, sizeof(b));
}
static int buf_write_u64(buf_writer *w, uint64_t value)
{
uint8_t b[8];
int i;
for (i = 0; i < 8; i++) {
b[i] = (uint8_t)((value >> (8 * i)) & 0xffu);
}
return buf_writer_write(w, b, sizeof(b));
}
static int buf_read_u32(buf_reader *r, uint32_t *value)
{
uint8_t b[4];
if (!buf_reader_read(r, b, sizeof(b))) {
return 0;
}
*value = ((uint32_t)b[0]) | ((uint32_t)b[1] << 8) |
((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
return 1;
}
static int buf_read_u64(buf_reader *r, uint64_t *value)
{
uint8_t b[8];
int i;
if (!buf_reader_read(r, b, sizeof(b))) {
return 0;
}
*value = 0;
for (i = 0; i < 8; i++) {
*value |= ((uint64_t)b[i]) << (8 * i);
}
return 1;
}
static int write_u32(FILE *file, uint32_t value)
{
uint8_t b[4];
b[0] = (uint8_t)(value & 0xffu);
b[1] = (uint8_t)((value >> 8) & 0xffu);
b[2] = (uint8_t)((value >> 16) & 0xffu);
b[3] = (uint8_t)((value >> 24) & 0xffu);
return fwrite(b, 1, sizeof(b), file) == sizeof(b);
}
static int write_u64(FILE *file, uint64_t value)
{
uint8_t b[8];
int i;
for (i = 0; i < 8; i++) {
b[i] = (uint8_t)((value >> (8 * i)) & 0xffu);
}
return fwrite(b, 1, sizeof(b), file) == sizeof(b);
}
static int read_u32(FILE *file, uint32_t *value)
{
uint8_t b[4];
if (fread(b, 1, sizeof(b), file) != sizeof(b)) {
return 0;
}
*value = ((uint32_t)b[0]) | ((uint32_t)b[1] << 8) |
((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
return 1;
}
static int read_u64(FILE *file, uint64_t *value)
{
uint8_t b[8];
int i;
if (fread(b, 1, sizeof(b), file) != sizeof(b)) {
return 0;
}
*value = 0;
for (i = 0; i < 8; i++) {
*value |= ((uint64_t)b[i]) << (8 * i);
}
return 1;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_save(const sakura_bptree *tree,
const char *path)
{
FILE *file;
bptree_node *node;
if (tree == NULL || path == NULL) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
file = fopen(path, "wb");
if (file == NULL) {
return SAKURA_BPTREE_IO_ERROR;
}
if (fwrite(BPTREE_MAGIC, 1, BPTREE_MAGIC_SIZE, file) != BPTREE_MAGIC_SIZE ||
!write_u32(file, BPTREE_VERSION) ||
!write_u32(file, SAKURA_SHA32_KEY_SIZE) ||
!write_u64(file, tree->len)) {
fclose(file);
return SAKURA_BPTREE_IO_ERROR;
}
node = tree->first_leaf;
while (node != NULL) {
int i;
for (i = 0; i < node->key_count; i++) {
if (node->slots.values[i].len > 0 &&
(uint64_t)((size_t)node->slots.values[i].len) != node->slots.values[i].len) {
fclose(file);
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
if (fwrite(node->keys[i], 1, SAKURA_SHA32_KEY_SIZE, file) != SAKURA_SHA32_KEY_SIZE ||
!write_u64(file, node->slots.values[i].len) ||
(node->slots.values[i].len > 0 &&
fwrite(node->slots.values[i].data, 1, (size_t)node->slots.values[i].len, file) !=
(size_t)node->slots.values[i].len)) {
fclose(file);
return SAKURA_BPTREE_IO_ERROR;
}
}
node = node->next;
}
if (fclose(file) != 0) {
return SAKURA_BPTREE_IO_ERROR;
}
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_load(const char *path,
sakura_bptree **tree_out)
{
FILE *file;
uint8_t magic[BPTREE_MAGIC_SIZE];
uint32_t version;
uint32_t key_size;
uint64_t count;
uint64_t i;
sakura_bptree *tree;
if (path == NULL || tree_out == NULL) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
*tree_out = NULL;
file = fopen(path, "rb");
if (file == NULL) {
return SAKURA_BPTREE_IO_ERROR;
}
if (fread(magic, 1, sizeof(magic), file) != sizeof(magic) ||
memcmp(magic, BPTREE_MAGIC, BPTREE_MAGIC_SIZE) != 0 ||
!read_u32(file, &version) ||
!read_u32(file, &key_size) ||
!read_u64(file, &count) ||
version != BPTREE_VERSION ||
key_size != SAKURA_SHA32_KEY_SIZE) {
fclose(file);
return SAKURA_BPTREE_CORRUPT_FILE;
}
tree = sakura_bptree_create();
if (tree == NULL) {
fclose(file);
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
for (i = 0; i < count; i++) {
uint8_t key[SAKURA_SHA32_KEY_SIZE];
uint64_t value_len;
uint8_t *value = NULL;
sakura_bptree_status status;
if (fread(key, 1, SAKURA_SHA32_KEY_SIZE, file) != SAKURA_SHA32_KEY_SIZE ||
!read_u64(file, &value_len) ||
(value_len > 0 && (uint64_t)((size_t)value_len) != value_len)) {
sakura_bptree_free(tree);
fclose(file);
return SAKURA_BPTREE_CORRUPT_FILE;
}
if (value_len > 0) {
value = (uint8_t *)malloc((size_t)value_len);
if (value == NULL) {
sakura_bptree_free(tree);
fclose(file);
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
if (fread(value, 1, (size_t)value_len, file) != (size_t)value_len) {
free(value);
sakura_bptree_free(tree);
fclose(file);
return SAKURA_BPTREE_CORRUPT_FILE;
}
}
status = sakura_bptree_put(tree, key, value, value_len);
free(value);
if (status != SAKURA_BPTREE_OK) {
sakura_bptree_free(tree);
fclose(file);
return status;
}
}
if (fgetc(file) != EOF) {
sakura_bptree_free(tree);
fclose(file);
return SAKURA_BPTREE_CORRUPT_FILE;
}
fclose(file);
refresh_first_leaf(tree);
*tree_out = tree;
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_serialize(const sakura_bptree *tree,
uint8_t **out,
uint64_t *out_len)
{
buf_writer w;
bptree_node *node;
if (tree == NULL || out == NULL || out_len == NULL) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
if (!buf_writer_init(&w)) {
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
if (!buf_writer_write(&w, BPTREE_MAGIC, BPTREE_MAGIC_SIZE) ||
!buf_write_u32(&w, BPTREE_VERSION) ||
!buf_write_u32(&w, SAKURA_SHA32_KEY_SIZE) ||
!buf_write_u64(&w, tree->len)) {
free(w.data);
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
node = tree->first_leaf;
while (node != NULL) {
int i;
for (i = 0; i < node->key_count; i++) {
uint64_t vlen = node->slots.values[i].len;
if (!buf_writer_write(&w, node->keys[i], SAKURA_SHA32_KEY_SIZE) ||
!buf_write_u64(&w, vlen) ||
(vlen > 0 && !buf_writer_write(&w, node->slots.values[i].data, vlen))) {
free(w.data);
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
}
node = node->next;
}
*out = w.data;
*out_len = w.len;
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API sakura_bptree_status sakura_bptree_deserialize(const uint8_t *data,
uint64_t data_len,
sakura_bptree **tree_out)
{
buf_reader r;
uint8_t magic[BPTREE_MAGIC_SIZE];
uint32_t version;
uint32_t key_size;
uint64_t count;
uint64_t i;
sakura_bptree *tree;
if (data == NULL || tree_out == NULL) {
return SAKURA_BPTREE_INVALID_ARGUMENT;
}
*tree_out = NULL;
r.data = data;
r.len = data_len;
r.pos = 0;
if (!buf_reader_read(&r, magic, BPTREE_MAGIC_SIZE) ||
memcmp(magic, BPTREE_MAGIC, BPTREE_MAGIC_SIZE) != 0 ||
!buf_read_u32(&r, &version) ||
!buf_read_u32(&r, &key_size) ||
!buf_read_u64(&r, &count) ||
version != BPTREE_VERSION ||
key_size != SAKURA_SHA32_KEY_SIZE) {
return SAKURA_BPTREE_CORRUPT_FILE;
}
tree = sakura_bptree_create();
if (tree == NULL) {
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
for (i = 0; i < count; i++) {
uint8_t key[SAKURA_SHA32_KEY_SIZE];
uint64_t value_len;
uint8_t *value = NULL;
sakura_bptree_status status;
if (!buf_reader_read(&r, key, SAKURA_SHA32_KEY_SIZE) ||
!buf_read_u64(&r, &value_len) ||
(value_len > 0 && (uint64_t)((size_t)value_len) != value_len)) {
sakura_bptree_free(tree);
return SAKURA_BPTREE_CORRUPT_FILE;
}
if (value_len > 0) {
value = (uint8_t *)malloc((size_t)value_len);
if (value == NULL) {
sakura_bptree_free(tree);
return SAKURA_BPTREE_OUT_OF_MEMORY;
}
if (!buf_reader_read(&r, value, value_len)) {
free(value);
sakura_bptree_free(tree);
return SAKURA_BPTREE_CORRUPT_FILE;
}
}
status = sakura_bptree_put(tree, key, value, value_len);
free(value);
if (status != SAKURA_BPTREE_OK) {
sakura_bptree_free(tree);
return status;
}
}
if (r.pos != r.len) {
sakura_bptree_free(tree);
return SAKURA_BPTREE_CORRUPT_FILE;
}
refresh_first_leaf(tree);
*tree_out = tree;
return SAKURA_BPTREE_OK;
}
SAKURA_BPTREE_API const char *sakura_bptree_status_string(sakura_bptree_status status)
{
switch (status) {
case SAKURA_BPTREE_OK:
return "ok";
case SAKURA_BPTREE_NOT_FOUND:
return "not found";
case SAKURA_BPTREE_INVALID_ARGUMENT:
return "invalid argument";
case SAKURA_BPTREE_OUT_OF_MEMORY:
return "out of memory";
case SAKURA_BPTREE_IO_ERROR:
return "io error";
case SAKURA_BPTREE_CORRUPT_FILE:
return "corrupt file";
default:
return "unknown";
}
}