#include "hashmap.h" #include #include #include #include #include #include #define INPUT_BUF_SIZE 1024 const char HASHMAPP_MAGIC[] = "HSMP"; typedef struct AppState { char running; HashMap map; } AppState; void open_file(HashMap *map, const char *filename) { FILE *file = fopen(filename, "r"); if (file == NULL) { fprintf(stderr, "failed to open file: %s\n", strerror(errno)); return; } fseek(file, 0, SEEK_END); long filesize = ftell(file); fseek(file, 0, SEEK_SET); if (filesize < 4) { fprintf(stderr, "invalid file: %s\n", filename); return; } int magic_len = strlen(HASHMAPP_MAGIC); char magic[magic_len + 1]; unsigned long cur = ftell(file); fread(magic, magic_len, 1, file); magic[magic_len] = 0; if (strcmp(HASHMAPP_MAGIC, magic) != 0) { fprintf(stderr, "invalid file: %s\n", filename); return; } while (cur < filesize) { uint16_t key_len = 0; fread(&key_len, sizeof(uint16_t), 1, file); char key[key_len + 1]; fread(key, key_len, 1, file); key[key_len] = 0; uint16_t data_size = 0; fread(&data_size, sizeof(uint16_t), 1, file); char *data = malloc(data_size); fread(data, data_size, 1, file); data[data_size] = 0; map_set(map, key, data, data_size); cur = ftell(file); } if (fclose(file) != 0) { fprintf(stderr, "failed to close file: %s\n", strerror(errno)); return; } printf("opened db: %s\n", filename); } void write_file(HashMap *map, const char *filename) { FILE *file = fopen(filename, "w+"); if (file == NULL) { fprintf(stderr, "failed to open file: %s\n", strerror(errno)); return; } int fd = fileno(file); if (fd == -1) { fprintf(stderr, "failed to get descriptor: %s\n", strerror(errno)); fclose(file); return; } unsigned long offset = 0; offset += fwrite(HASHMAPP_MAGIC, strlen(HASHMAPP_MAGIC), 1, file); for (uint16_t bucket_i = 0; bucket_i < HASHMAP_BUCKETS; bucket_i++) { Bucket *bucket = &map->buckets[bucket_i]; for (uint16_t i = 0; i < bucket->n_entries; i++) { Entry *entry = &bucket->entries[i]; uint16_t key_len = strlen(entry->key); uint16_t data_len = strlen(entry->data); offset += fwrite(&key_len, sizeof(uint16_t), 1, file); offset += fwrite(entry->key, strlen(entry->key), 1, file); offset += fwrite(&data_len, sizeof(uint16_t), 1, file); offset += fwrite(entry->data, strlen(entry->data), 1, file); } } if (ftruncate(fd, offset) != 0) { fprintf(stderr, "failed to truncate file: %s\n", strerror(errno)); fclose(file); return; } if (fclose(file) != 0) { fprintf(stderr, "failed to close file: %s\n", strerror(errno)); return; } printf("written db to \"%s\" successfully.\n", filename); } void handle_command(AppState *app, char *buf, size_t buf_len) { if (!strcmp(buf, "q")) app->running = 0; else if (strcmp(buf, "w ") > 0) write_file(&app->map, buf + 2); else if (strcmp(buf, "o ") > 0) open_file(&app->map, buf + 2); else printf("?\n"); } int main(int argc, char *argv[]) { printf("hashmapp. the map to app all hashes. wait-\n" "\":q\" to exit\n" "\":w \" to save\n" "\":o \" to load\n\n"); AppState app = { 0 }; init_map(&app.map); char buf[INPUT_BUF_SIZE]; app.running = 1; while (app.running) { memset(buf, 0, INPUT_BUF_SIZE); printf("> "); if (fgets(buf, INPUT_BUF_SIZE, stdin) == NULL) { fprintf(stderr, "failed to read stdin: %u\n", errno); return EXIT_FAILURE; } size_t buf_len = strnlen(buf, INPUT_BUF_SIZE); buf[buf_len - 1] = 0; if (buf_len == 0) continue; if (buf[0] == ':') { if (buf_len < 2) { printf("?\n"); continue; } handle_command(&app, buf + 1, buf_len - 1); continue; } char *eq = strchr(buf, '='); if (eq == NULL) { void *data = map_get(&app.map, buf); if (data == NULL) printf("\n"); else printf("%s\n", (char*)data); continue; } uint16_t key_len = eq - buf + 1; char key[key_len]; strncpy(key, buf, key_len); key[key_len - 1] = 0; uint16_t value_len = strlen(buf + key_len); char *value = malloc(sizeof(char) * value_len); strncpy(value, buf + key_len, value_len); value[value_len] = 0; map_set(&app.map, key, value, value_len); } for (size_t bucket_i = 0; bucket_i < HASHMAP_BUCKETS; bucket_i++) { Bucket *bucket = &app.map.buckets[bucket_i]; for (size_t i = 0; i < bucket->n_entries; i++) { free(bucket->entries[i].key); free(bucket->entries[i].data); } } return EXIT_SUCCESS; }