From 66d4591ebbcd2edde3c38e40429adcdb38adadbf Mon Sep 17 00:00:00 2001
From: Kacper Donat <kadet1090@gmail.com>
Date: Fri, 14 Dec 2018 22:51:55 +0100
Subject: [PATCH] better verbose messages

---
 common.h |  5 +++--
 index.c  | 65 ++++++++++++++++++++++++++++++++++++++++----------------
 index.h  |  4 +++-
 io.c     | 12 +++++------
 4 files changed, 59 insertions(+), 27 deletions(-)

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 <stdio.h>
 #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);