#include #include #include #include #include #include #define OPTPARSE_IMPLEMENTATION #define OPTPARSE_API static #include "common.h" #include "index.h" #define RESULT_EXIT -1 #define RESULT_OK 0 typedef int result_t; typedef struct { const char* command; const char* help; result_t (*function)(const char* command, char* argline); } command_t; typedef struct { char* index; } opts_t; opts_t options; btree_t tree; void init_args(int args, char* argv[]) { optparse_t opts; optparse_init(&opts, argv); for (char opt; opt != -1; opt = optparse(&opts, "qv")) { switch (opt) { case 'q': verbosity--; break; case 'v': verbosity++; break; } } options.index = optparse_arg(&opts); } void help(const char* name) { printf( "%s - some help" , name ); } result_t help_command(const char* command, char* args); result_t exit_command(const char* command, char* args); result_t insert_command(const char* command, char* args); result_t dump_command(const char* command, char* args); result_t print_command(const char* command, char* args); result_t records_command(const char* command, char* args); result_t find_command(const char* command, char* args); result_t read_command(const char* command, char* args); result_t delete_command(const char* command, char* args); result_t update_command(const char* command, char* args); result_t verbosity_command(const char* command, char* args); result_t iostat_command(const char* command, char* args); result_t flush_command(const char* command, char* args); result_t optimize_command(const char* command, char* args); static command_t commands[] = { { "help", "Prints out help", help_command }, { "exit", "Self explanatory", exit_command }, { "insert", "Adds record to index", insert_command }, { "dump", "Dumps given page", dump_command }, { "print", "Prints tree", print_command }, { "records", "Prints records of given page", records_command }, { "find", "Finds record", find_command }, { "read", "Reads record", read_command }, { "delete", "Deletes record", delete_command }, { "update", "Updates record", update_command }, { "iostat", "Prints IO counts", iostat_command }, { "flush", "Fluhshes index into disk", flush_command }, { "verbosity", "Changes the verbosity", verbosity_command }, { "optimize", "Optimizes the index", optimize_command }, }; result_t exit_command(const char* command, char* args) { return RESULT_EXIT; } result_t iostat_command(const char* command, char* args) { printfv(VERBOSITY_NORMAL, "R/W: %u/%u (excluding cache: %u/%u)\n", reads, writes, reads_all, writes_all); return RESULT_OK; } result_t flush_command(const char* command, char* args) { btree_flush(&tree); return RESULT_OK; } result_t optimize_command(const char* command, char* args) { btree_optimize(&tree); return RESULT_OK; } result_t verbosity_command(const char* command, char* args) { verbosity_t v; char buffer[PAGE_SIZE]; if (sscanf(args, "%d", &v) == 1) { verbosity = v; } else { printf("Usage: verbosity new\n"); } return RESULT_OK; } result_t dump_command(const char* command, char* args) { page_t page; char buffer[PAGE_SIZE]; if (sscanf(args, "%zu", &page) == 1) { file_read(tree.file, page, buffer, PAGE_SIZE); hexdump(buffer, PAGE_SIZE); } else { printf("Usage: dump page\n"); } return RESULT_OK; } result_t insert_command(const char* command, char* args) { record_t record; if (sscanf(args, "%u %lf %lf", &record.key, &record.x, &record.y) == 3) { btree_insert(&tree, record); } else { printf("usage: insert key x y\n"); } return RESULT_OK; } result_t find_command(const char* command, char* args) { record_key_t key; if (sscanf(args, "%u", &key) == 1) { btree_entry_t entry; page_t page; if ((page = btree_find(&tree, key, &entry, NULL, NULL))) { printf("Record %u found on page %zu, offset: %lu\n", key, page, entry.location); } else { printf("404 Not found\n"); } } else { printf("Usage: find key\n"); } return RESULT_OK; } result_t read_command(const char* command, char* args) { record_key_t key; if (sscanf(args, "%u", &key) == 1) { btree_entry_t entry; page_t page; record_t record; if ((page = btree_find(&tree, key, &entry, NULL, NULL))) { printf("Record %u found on page %zu, offset: %lu\n", key, page, entry.location); tape_read(tree.main, entry.location, &record, sizeof(record_t)); printf("PK: %u, x: %lf, y: %lf\n", record.key, record.x, record.y); } else { printf("404 Not found\n"); } } else { printf("Usage: read key\n"); } return RESULT_OK; } result_t delete_command(const char* command, char* args) { record_key_t key; if (sscanf(args, "%u", &key) == 1) { page_t page; if ((page = btree_remove(&tree, key))) { printf("Record %u removed from page %zu\n", key, page); } else { printf("404 Not found\n"); } } else { printf("Usage: find key\n"); } return RESULT_OK; } result_t update_command(const char* command, char* args) { record_t record; if (sscanf(args, "%u %lf %lf", &record.key, &record.x, &record.y) == 3) { if (btree_update(&tree, record.key, record)) { printf("Record %u updated.\n", record.key); } else { printf("404 Not found\n"); } } else { printf("Usage: find key\n"); } return RESULT_OK; } result_t records_command(const char* command, char* args) { page_t page; char buffer[PAGE_SIZE]; if (sscanf(args, "%zu", &page) == 1) { file_read(tree.file, page, buffer, PAGE_SIZE); btree_node_t node; memcpy(&node.header, buffer, sizeof(node.header)); memcpy(&node.entries, buffer + sizeof(node.header), NODE_SIZE_MAX); printf( "Node %zu, entries: %u, flags: [%c%c]\n", page, node.header.entries, node.header.flags & NODE_IS_ROOT ? 'R' : ' ', node.header.flags & NODE_IS_LEAF ? 'L' : ' ' ); printf("Records in node (%u):\n", node.header.entries); for (int i = 0; i < node.header.entries; i++) { btree_entry_t *entry = btree_get_entry(&node, i); printf(" %zu < %u [0x%zx] > %zu\n", entry->left, entry->key, entry->location, entry->right); } } else { printf("Usage: print page\n"); } return RESULT_OK; } void print_page(page_t page, unsigned depth, unsigned current) { char buffer[PAGE_SIZE]; char prefix[1024] = {}; memset(prefix, ' ', current * 2); if (!depth || !page) return; file_read(tree.file, page, buffer, PAGE_SIZE); btree_node_t node; memcpy(&node.header, buffer, sizeof(node.header)); memcpy(&node.entries, buffer + sizeof(node.header), NODE_SIZE_MAX); if (node.header.entries == 0) { printf("%sempty\n", prefix); return; } btree_entry_t *entry; for (int i = 0; i < node.header.entries; i++) { entry = btree_get_entry(&node, i); print_page(entry->left, depth - 1, current + 1); printf("%s%zu < %u [0x%08zx] > %zu\n", prefix, entry->left, entry->key, entry->location, entry->right); } print_page(entry->right, depth - 1, current + 1); } result_t print_command(const char* command, char* args) { page_t page = 0; unsigned depth = 4; if (sscanf(args, "%zu %u", &page, &depth) >= 1) { print_page(page ? page : tree.header.root, depth, 0); } else { printf("Usage: print page\n"); } return RESULT_OK; } result_t help_command(const char* command, char* args) { size_t count = sizeof(commands) / sizeof(command_t); printf("Available commands (%zu): \n", count); for (size_t i = 0; i < count; i++) { printf("\t%s - %s\n", commands[i].command, commands[i].help); } return RESULT_OK; } command_t* get_command(char* argline) { char command[128]; argline = strtok(argline, " \t\n\r"); if (!argline) { return NULL; } strcpy(command, argline); size_t count = sizeof(commands) / sizeof(command_t); for (size_t i = 0; i < count; i++) { if (strcmp(commands[i].command, command) == 0) { return commands + i; } } return NULL; } void handle_ctrlc(int sig) { printf("\nCtrl-C - exiting gracefully...\n"); btree_close(&tree); exit(0); } int main(int argc, char* argv[]) { char *line, prompt[1024]; command_t* command; signal(SIGINT, handle_ctrlc); init_args(argc, argv); if (btree_open(&tree, options.index) == 0) { sprintf(prompt, "%s> ", options.index); while ((line = readline(prompt))) { unsigned r = reads, w = writes, ra = reads_all, wa = writes_all; command = get_command(line); if (!command) { printf("Unknown command!\n"); continue; } if (command->function(line, line + strlen(line) + 1) == RESULT_EXIT) { break; } printfv(VERBOSITY_VERBOSE, "[io] R/W: %u/%u (excluding cache: %u/%u)\n", reads - r, writes - w, reads_all - ra, writes_all - wa); } btree_close(&tree); return EXIT_SUCCESS; } return -1; }