diff --git a/common.h b/common.h index 649a13f..a45ff30 100644 --- a/common.h +++ b/common.h @@ -3,8 +3,9 @@ #define VERBOSITY_QUIET -1 #define VERBOSITY_NORMAL 0 -#define VERBOSITY_VERBOSE 1 -#define VERBOSITY_DEBUG 2 +#define VERBOSITY_DEBUG 1 +#define VERBOSITY_VERBOSE 2 +#define VERBOSITY_VERY_VERBOSE 3 #include #include "optparse.h" diff --git a/index.c b/index.c index 1a1fa27..5dad358 100644 --- a/index.c +++ b/index.c @@ -10,11 +10,15 @@ #define BTREE_ERR_CANNOT_OPEN_FILE -1 #define BTREE_ERR_PAGE_SIZE_DIFFERENT -2 +#define BTREE_OPTIMIZE_RECORDS_MIN 10 +#define BTREE_OPTIMIZE_THRESHOLD 0.1 + #define SWAP(type, x, y) do { type __tmp__; __tmp__ = y; y = x; x = __tmp__; } while (0) /* private functions */ void _btree_insert_into_node(btree_t *tree, btree_node_t *node, btree_entry_t *entry); void _btree_remove_from_node(btree_t *tree, btree_node_t *node, unsigned index); +void _btree_optimize_if_needed(btree_t *tree) ; int _btree_save_header(btree_t *tree) { @@ -42,14 +46,14 @@ page_t _btree_alloc(btree_t *tree) page += bit + 1; - printfv(VERBOSITY_DEBUG, "[alloc] allocated page %zu.\n", page); + printfv(VERBOSITY_VERBOSE, "[alloc] allocated page %zu.\n", page); return page; } void _btree_page_free(btree_t *tree, page_t page) { - printfv(VERBOSITY_DEBUG, "[alloc] freeing page %zu.\n", page); + printfv(VERBOSITY_VERBOSE, "[alloc] freeing page %zu.\n", page); uint8_t bitmap[PAGE_SIZE]; file_read(tree->file, 1 + (page / (PAGE_SIZE * 8)) * PAGE_SIZE, bitmap, PAGE_SIZE); @@ -88,7 +92,7 @@ bool _btree_stack_empty(btree_t *tree) void _btree_stack_push(btree_t *tree, btree_node_t *node, unsigned n) { - printfv(VERBOSITY_DEBUG, "[btree] Pushing %zu page on parent stack, entry index %u\n", node->page, n); + printfv(VERBOSITY_VERBOSE, "[btree] Pushing %zu page on parent stack, entry index %u\n", node->page, n); tree->trace.current++; tree->trace.current->node = *node; tree->trace.current->entry = btree_get_entry(&tree->trace.current->node, n); @@ -149,7 +153,7 @@ unsigned _btree_concat_entries(void* current, size_t na, void* added, size_t nb) void _btree_node_insert_entry(btree_node_t* node, btree_entry_t entry) { - printfv(VERBOSITY_DEBUG, "[btree] inserting { %zu < %zu > %zu } on %zu\n", entry.left, entry.key, entry.right, node->page); + printfv(VERBOSITY_VERBOSE, "[btree] inserting { %zu < %zu > %zu } on %zu\n", entry.left, entry.key, entry.right, node->page); _btree_concat_entries(node->entries, node->header.entries, &entry, 1); node->header.entries++; @@ -371,10 +375,10 @@ void _btree_split_node(btree_t *tree, btree_node_t *old, btree_entry_t *entry) tree->header.root = parent->page; _btree_save_header(tree); - printfv(VERBOSITY_DEBUG, "[btree] designated new root %zu.\n", parent->page); + printfv(VERBOSITY_DEBUG, "[btree] Designated new root %zu.\n", parent->page); }; - printfv(VERBOSITY_DEBUG, "[btree] spliting node %zu.\n", old->page); + printfv(VERBOSITY_DEBUG, "[btree] Spliting node %zu.\n", old->page); btree_entry_t parent_entry = *NODE_ENTRY(buffer, half); @@ -457,7 +461,7 @@ page_t btree_insert(btree_t *tree, record_t record) btree_node_t node; if (btree_find(tree, key, NULL, &node, NULL) != PAGE_NONE) { - printfv(VERBOSITY_DEBUG, "[btree] record with key %zu already exists.\n", key); + printfv(VERBOSITY_DEBUG, "[btree] Record with key %zu already exists.\n", key); return PAGE_NONE; } @@ -465,12 +469,18 @@ page_t btree_insert(btree_t *tree, record_t record) btree_entry_t entry = { PAGE_NONE, key, offset, PAGE_NONE }; _btree_insert_into_node(tree, &node, &entry); + + tree->header.changes++; + tree->header.records++; + + _btree_optimize_if_needed(tree); + return node.page; } void _btree_merge(btree_t *tree, btree_node_t* left, btree_node_t* right) { - printfv(VERBOSITY_DEBUG, "[btree] merging %zu with %zu.\n", left->page, right->page); + printfv(VERBOSITY_DEBUG, "[btree] Merging %zu with %zu.\n", left->page, right->page); size_t nl = left->header.entries, nr = right->header.entries, @@ -501,7 +511,7 @@ void _btree_merge(btree_t *tree, btree_node_t* left, btree_node_t* right) left->header.flags |= NODE_IS_ROOT; tree->header.root = left->page; - printfv(VERBOSITY_DEBUG, "[btree] designated new root %zu.\n", left->page); + printfv(VERBOSITY_DEBUG, "[btree] Designated new root %zu.\n", left->page); } else { _btree_write_node(&parent->node, tree, parent->node.page); } @@ -518,7 +528,7 @@ void _btree_fix_underflow(btree_t *tree, btree_node_t *node) { _btree_load_node(&sibling, tree, siblings.left); entry = siblings.left_entry; if (sibling.header.entries + node->header.entries >= 2*tree->header.d) { - printfv(VERBOSITY_DEBUG, "[btree] rebalancing with left sibling %zu.\n", siblings.left); + printfv(VERBOSITY_VERBOSE, "[btree] rebalancing with left sibling %zu.\n", siblings.left); _btree_rebalance(tree, &sibling, node, siblings.left_entry); return; } @@ -528,13 +538,13 @@ void _btree_fix_underflow(btree_t *tree, btree_node_t *node) { _btree_load_node(&sibling, tree, siblings.right); entry = siblings.right_entry; if (sibling.header.entries + node->header.entries >= 2*tree->header.d) { - printfv(VERBOSITY_DEBUG, "[btree] rebalancing with right sibling %zu.\n", siblings.right); + printfv(VERBOSITY_VERBOSE, "[btree] rebalancing with right sibling %zu.\n", siblings.right); _btree_rebalance(tree, node, &sibling, siblings.right_entry); return; } } - printfv(VERBOSITY_DEBUG, "[btree] unable to rebalance.\n"); + printfv(VERBOSITY_DEBUG, "[btree] Enable to rebalance.\n"); if (siblings.right != PAGE_NONE) { _btree_merge(tree, node, &sibling); @@ -563,7 +573,7 @@ void _btree_remove_from_node(btree_t *tree, btree_node_t *node, unsigned index) node->header.entries = m; if ((~node->header.flags & NODE_IS_ROOT) && node->header.entries < tree->header.d) { - printfv(VERBOSITY_DEBUG, "[btree] underflow in %zu.\n", node->page); + printfv(VERBOSITY_DEBUG, "[btree] Underflow in %zu.\n", node->page); _btree_fix_underflow(tree, node); } else { _btree_write_node(node, tree, node->page); @@ -578,7 +588,7 @@ page_t btree_remove(btree_t *tree, record_key_t key) unsigned index; if (btree_find(tree, key, NULL, &node, &index) == PAGE_NONE) { - printfv(VERBOSITY_DEBUG, "[btree] record with key %zu does not exist.\n", key); + printfv(VERBOSITY_DEBUG, "[btree] Record with key %zu does not exist.\n", key); // 404 return PAGE_NONE; } @@ -586,11 +596,11 @@ page_t btree_remove(btree_t *tree, record_key_t key) entry = btree_get_entry(&node, index); if (node.header.flags & NODE_IS_LEAF) { - printfv(VERBOSITY_DEBUG, "[btree] removing record with key %zu from leaf %zu.\n", entry->key, node.page); + printfv(VERBOSITY_DEBUG, "[btree] Removing record with key %zu from leaf %zu.\n", entry->key, node.page); _btree_remove_from_node(tree, &node, index); _btree_write_node(&node, tree, node.page); } else { - printfv(VERBOSITY_DEBUG, "[btree] removing record with key %zu from node %zu.\n", entry->key, node.page); + printfv(VERBOSITY_DEBUG, "[btree] Removing record with key %zu from node %zu.\n", entry->key, node.page); btree_node_t *replacement; btree_entry_t *replaced; @@ -608,7 +618,7 @@ page_t btree_remove(btree_t *tree, record_key_t key) replaced = NODE_ENTRY(node.entries, 0); entry = btree_get_entry(replacement, index); - printfv(VERBOSITY_DEBUG, "[btree] exchanging %zu with %zu.\n", entry->key, replaced->key); + printfv(VERBOSITY_DEBUG, "[btree] Exchanging %zu with %zu.\n", entry->key, replaced->key); SWAP(record_key_t, replaced->key, entry->key); SWAP(offset_t, replaced->location, entry->location); _btree_write_node(replacement, tree, replacement->page); @@ -616,13 +626,18 @@ page_t btree_remove(btree_t *tree, record_key_t key) _btree_remove_from_node(tree, &node, 0); } + tree->header.changes++; + tree->header.records--; + + _btree_optimize_if_needed(tree); + return node.page; } bool btree_update(btree_t *tree, record_key_t key, record_t record) { if (record.key != key) { - printfv(VERBOSITY_DEBUG, "[btree] update key mismatch, removing %zu and adding %zu.\n", key, record.key); + printfv(VERBOSITY_DEBUG, "[btree] Update key mismatch, removing %zu and adding %zu.\n", key, record.key); btree_remove(tree, key); return btree_insert(tree, record) != PAGE_NONE; } @@ -663,6 +678,8 @@ page_t btree_read_record(btree_t *tree, record_key_t key, record_t *record) void btree_close(btree_t* tree) { printfv(VERBOSITY_DEBUG, "[btree] Closing index %s.\n", tree->file->filename); + _btree_save_header(tree); + file_close(tree->file); tape_close(tree->main); } @@ -673,6 +690,15 @@ void btree_flush(btree_t* tree) file_flush(tree->main->file); } +void _btree_optimize_if_needed(btree_t *tree) +{ + double ratio = (double)tree->header.changes / (double)tree->header.records; + if (tree->header.records > BTREE_OPTIMIZE_RECORDS_MIN && ratio > BTREE_OPTIMIZE_THRESHOLD) { + printfv(VERBOSITY_DEBUG, "[btree] Auto optimization triggered.\n"); + btree_optimize(tree); + } +} + void _btree_optimize_page(btree_t* tree, tape_t* tmp, page_t page) { if (!page) return; @@ -696,10 +722,13 @@ void _btree_optimize_page(btree_t* tree, tape_t* tmp, page_t page) void btree_optimize(btree_t *tree) { + char tmp[PATH_MAX + 4], old[PATH_MAX]; strcpy(old, tree->main->file->filename); sprintf(tmp, "%s.tmp", old); + printfv(VERBOSITY_DEBUG, "[btree] Optimizing btree using temp file %s.\n", tmp); + tape_t *temp = tape_open(tmp, "wb+"); _btree_optimize_page(tree, temp, tree->header.root); tape_close(temp); diff --git a/index.h b/index.h index 5e61868..2a8dfbc 100644 --- a/index.h +++ b/index.h @@ -23,7 +23,9 @@ typedef struct { size_t page_size; /* 8 bytes long */ page_t root; /* 8 bytes long */ char main[256]; /* 256 bytes long */ -} btree_header_t; /* 280 bytes long */ + size_t records; /* 8 bytes long */ + size_t changes; /* 8 bytes long */ +} btree_header_t; /* 296 bytes long */ typedef struct { uint16_t flags; diff --git a/io.c b/io.c index 167fdcc..08e6c23 100644 --- a/io.c +++ b/io.c @@ -19,7 +19,7 @@ file_t* file_open(const char* filename, const char* mode) return NULL; } - printfv(VERBOSITY_DEBUG, "[io] File %s opened in %s with page size %zu.\n", filename, mode, PAGE_SIZE); + printfv(VERBOSITY_VERY_VERBOSE, "[io] File %s opened in %s with page size %zu.\n", filename, mode, PAGE_SIZE); file_t* result = malloc(sizeof(file_t)); result->file = handle; @@ -34,7 +34,7 @@ void file_close(file_t* file) { file_flush(file); - printfv(VERBOSITY_DEBUG, "[io] Closing file %s.\n", file->filename); + printfv(VERBOSITY_VERY_VERBOSE, "[io] Closing file %s.\n", file->filename); fclose(file->file); free(file->filename); @@ -48,14 +48,14 @@ size_t file_read(file_t* file, page_t page, void* buffer, size_t length) page_cache_entry_t *entry = _file_load_page(file, page); memcpy(buffer, entry->data, length); - printfv(VERBOSITY_DEBUG, "[io] Reading page %zu in %s (%zu bytes).\n", page, file->filename, entry->size); + printfv(VERBOSITY_VERY_VERBOSE, "[io] Reading page %zu in %s (%zu bytes).\n", page, file->filename, entry->size); return entry->size; } size_t file_write(file_t* file, page_t page, const void* buffer, size_t length) { - printfv(VERBOSITY_DEBUG, "[io] Writing page %zu in %s (%zu bytes).\n", page, file->filename, length); + printfv(VERBOSITY_VERY_VERBOSE, "[io] Writing page %zu in %s (%zu bytes).\n", page, file->filename, length); writes_all++; page_cache_entry_t *entry = _file_load_page(file, page); @@ -92,7 +92,7 @@ page_cache_entry_t *_file_load_page(file_t* file, page_t page) memset(entry->data, 0, PAGE_SIZE); entry->size = fread(entry->data, 1, PAGE_SIZE, file->file); if (entry->size) { - printfv(VERBOSITY_DEBUG, "[io] Loading page %u of %s (%zu bytes).\n", page, file->filename, entry->size); + printfv(VERBOSITY_VERY_VERBOSE, "[io] Loading page %u of %s (%zu bytes).\n", page, file->filename, entry->size); reads++; } @@ -105,7 +105,7 @@ page_cache_entry_t *_file_load_page(file_t* file, page_t page) void _file_flush_page(file_t *file, page_cache_entry_t *entry) { if (entry->flags & PAGE_PRESENT && entry->flags & PAGE_DIRTY) { - printfv(VERBOSITY_DEBUG, "[io] Flushing page %u to %s (%zu bytes).\n", entry->page, file->filename, entry->size); + printfv(VERBOSITY_VERY_VERBOSE, "[io] Flushing page %u to %s (%zu bytes).\n", entry->page, file->filename, entry->size); fseek(file->file, entry->page * PAGE_SIZE, SEEK_SET); fwrite(entry->data, 1, entry->size, file->file);