diff --git a/.gitignore b/.gitignore index 7ac3b40..97fedc6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ /inv.txt /inv_users.txt /polls.txt -/elem.tar.gz \ No newline at end of file +/elem.tar.gz +/combos.txt \ No newline at end of file diff --git a/README.md b/README.md index f95e7a8..d5d6b6f 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ # Elemental on Command Line This is an elemental combination game written in C, with optional multi-player support. +## Clients +- [PenguinMod client](https://studio.penguinmod.com/?fps=200&clones=Infinity&offscreen&size=850x480#7218964246) for playing the game online with a fancy interface +- [Discord client](https://discord.gg/DKZTWyWH3B) for playing the game publicly with other Discord members +- [Web client](https://elem.dervland.net/index.html) for a demo of the multiplayer version +- [Original client](https://git.dervland.net/elemental/elemental-on-terminal/releases) for developers, administrators, and command line fans + ## Single-player A single-player example pack is provided as an example in ``bin/combos.txt``. This can be customized by the user, although internal limitations require the combination string to be in alphabetical order. ## Multi-player -To set up multi-player mode, create a directory ``../elem_data``. Also, create separate directories for each user that plays. - -The game can now be played with the following syntax, inside your user directory: -```sh -cd ../your_user_dir -../elemental-command-line/bin/elem your_username -``` \ No newline at end of file +To set up multi-player mode, use the guide with the [Web proxy](https://git.dervland.net/elemental/elemental-to-web). \ No newline at end of file diff --git a/src/command.c b/src/command.c index ac5e0a1..749cbdc 100644 --- a/src/command.c +++ b/src/command.c @@ -1,9 +1,11 @@ #include "map.h" +#include #include #include #include #include - +#include +#include "loader.h" #include "main.h" // huge cleanup operation soon @@ -12,6 +14,7 @@ struct pager { int page; int i; int is_poll; + hashmap *cmd; }; struct verifier { @@ -51,24 +54,45 @@ int sort_comp(const void *a, const void *b) { int inv_handler(const void *key, size_t size, uintptr_t val, void *usr) { struct pager *i = usr; - i->i++; - if (i->i < i->page * 10 || i->i >= (i->page + 1) * 10) { - return 0; - } if (val == 0) return 0; char *key2 = (char *)key; char *val2 = (char *)val; if (i->is_poll) { - if (val2[strlen(val2) - 1] == '\n') { - printf("- %s suggested %s", key2, val2); - } else { - printf("- %s suggested %s\n", key2, val2); + char *key3 = calloc(strlen(key2) + 1, sizeof(char)); + memcpy(key3, key2, strstr(key2, "_") - key2); + + char *val3 = strstr(val2, ";") + 1; + uintptr_t result; + + hashmap_get(i->cmd, val3, strlen(val3) - 1, &result); + + if (result != 0) { + free(key3); + return 1; } + + i->i++; + if (i->i < i->page * 10 || i->i >= (i->page + 1) * 10) { + return 0; + } + + if (val2[strlen(val2) - 1] == '\n') { + printf("- user:%s suggested %s", key3, val2); + } else { + printf("- user:%s suggested %s\n", key3, val2); + } + + free(key3); return 1; } + i->i++; + if (i->i < i->page * 10 || i->i >= (i->page + 1) * 10) { + return 0; + } + if (strlen(key2) > 0 && key2[strlen(key2) - 1] == '\n') { printf("- %s", key2); } else { @@ -80,42 +104,74 @@ int inv_handler(const void *key, size_t size, uintptr_t val, void *usr) { int polls_handler(const void *key, size_t size, uintptr_t val, void *usr) { struct verifier *verified = (struct verifier *)usr; - if (((char *)val)[strlen(((char *)val)) - 1] == '\n') { - ((char *)val)[strlen(((char *)val)) - 1] = '\0'; + char *val2 = (char *)val; + char *val3 = malloc(strlen(val2) + 1); + strcpy(val3, val2); + + if (val3[strlen(val3) - 1] == '\n') { + val3[strlen(val3) - 1] = '\0'; } if (strncmp(verified->name, key, strlen(verified->name)) == 0 && - strcmp(verified->sugg, (char *)val) == 0) + strcmp(verified->sugg, val3) == 0) { + free(val3); return -1; + } + free(val3); return 0; } int success_handler(const void *key, size_t size, uintptr_t val, void *usr) { struct succ *verified = (struct succ *)usr; - if ((char *)val != verified->sugg) { - //((char *)val)[strlen(((char *)val)) - 1] = '\0'; + char *val2 = (char *)val; + char *val3 = malloc(strlen(val2) + 1); + strcpy(val3, val2); + + if (val3[strlen(val3) - 1] == '\n') { + val3[strlen(val3) - 1] = '\0'; } - if (strcmp(verified->sugg, (char *)val) == 0) { + if (strcmp(verified->sugg, val3) == 0) { verified->points[0]++; } + free(val3); + return 0; } -int suggest_command(char *command, char *command_re, hashmap *polls, - char *name) { - char *invs = "/suggest "; - - char *page; - +char *handle_pages(char *command, char *invs) { + char *data; + char *data2; if (strncmp(command, invs, strlen(invs)) == 0) { - page = &command[strlen(invs)]; + data = &command[strlen(invs)]; + data2 = malloc(strlen(data) + 1); + memcpy(data2,data,strlen(data) + 1); + return data2; } else { return 0; } +} + +int handle_pages_int(char *command, char *invs) { + if (strncmp(command, invs, strlen(invs)) == 0) { + return stoi(&command[strlen(invs)]); + } else if (strncmp(command, invs, strlen(invs) - 1) == 0) { + return 1; + } else { + return 0; + } +} + +int suggest_command(char *command, char *command_re, hashmap *polls, hashmap *combos, char *name, + int was_combination) { + + char *page = handle_pages(command, "/suggest "); + + if (!page) + return 0; page[strlen(page) - 1] = '\0'; if (strstr(page, "\n") || strstr(page, ";") || strstr(page, ",") || @@ -127,11 +183,22 @@ int suggest_command(char *command, char *command_re, hashmap *polls, char *val = calloc(MAX_BUF_LENGTH, sizeof(char)); sprintf(val, "%s;%s", page, command_re); + srand(time(NULL)); + char *key = calloc(MAX_BUF_LENGTH, sizeof(char)); sprintf(key, "%s_%i", name, (int)rand()); - struct verifier verified = {.name = name, .sugg = val}; + int point_thing = 0; + struct succ succer = {.sugg = val, .points = &point_thing}; + hashmap_iterate(polls, success_handler, (void *)&succer); + + if (was_combination == 2 && point_thing == 0) { + printf("You cannot create unique polls with elements you do not have.\n"); + return 1; + } + + struct verifier verified = {.name = name, .sugg = val}; if (hashmap_iterate(polls, polls_handler, (void *)&verified) == -1) { printf("You already suggested this!\n"); return 1; @@ -139,22 +206,15 @@ int suggest_command(char *command, char *command_re, hashmap *polls, hashmap_set(polls, key, strlen(key), (uintptr_t)val, 1); - int point_thing = 0; - - struct succ succer = {.sugg = val, .points = &point_thing}; - hashmap_iterate(polls, success_handler, (void *)&succer); + point_thing++; if (point_thing == UPVOTE_IN) { - printf("Poll was added into the game!\n"); FILE *fptr; - fptr = fopen("../elem_data/" combo_file, "a"); - if (fptr == NULL) - return 1; - fwrite(val, sizeof(char), strlen(val), fptr); - fwrite("\n", sizeof(char), 1, fptr); - fclose(fptr); + hashmap_set(combos, command_re, strlen(command_re), (uintptr_t)page, 0); + + write_elements(combos, "../elem_data/" COMBO_FILE, 0); return 1; } @@ -164,72 +224,131 @@ int suggest_command(char *command, char *command_re, hashmap *polls, FILE *fptr; - fptr = fopen("../elem_data/" poll_file, "a"); - if (fptr == NULL) - return 1; - fwrite(key, sizeof(char), strlen(key), fptr); - fwrite(";", sizeof(char), 1, fptr); - fwrite(val, sizeof(char), strlen(val), fptr); - fwrite("\n", sizeof(char), 1, fptr); - fclose(fptr); + write_elements(polls, "../elem_data/" POLL_FILE, 3); return 1; } int help_command(char *command) { - char *invs = "/help"; + char *page = handle_pages(command, "/help"); - int page; - - if (strncmp(command, invs, strlen(invs)) == 0) { - - printf("Available " - "commands:\n- elem1;elem2...\n- elem1+elem2...\n- elem1,elem2...\n- " - "/help\n- /inv [page]\n- /suggest [combo]\n- /polls [page]\n"); - } else { + if (!page) return 0; - } - + printf("Available " + "commands:\n- elem1;elem2...\n- elem1+elem2...\n- elem1,elem2...\n- " + "/help\n- /inv [page]\n- /suggest [combo]\n- /polls [page]\n"); return 1; } -int polls_command(char *command, hashmap *polls) { +int polls_command(char *command, hashmap *polls, hashmap *cmd) { - char *invs = "/polls "; - - int page; - - if (strncmp(command, invs, strlen(invs)) == 0) { - page = stoi(&command[strlen(invs)]); - } else if (strncmp(command, invs, strlen(invs) - 1) == 0) { - page = 1; - } else { + int page = handle_pages_int(command, "/polls "); + if (page == 0) return 0; - } printf("Current polls (page %i):\n", page); - struct pager i = {.page = page - 1, .i = -1, .is_poll = 1}; + struct pager i = {.page = page - 1, .i = -1, .is_poll = 1, .cmd = cmd}; hashmap_iterate(polls, inv_handler, &i); + printf("Total: %i\n", i.i + 1); + return 1; +} + +int path_command(char *command, hashmap *elements_rev, hashmap *already_done, + int top, hashmap *inv) { + char *page2; + if (top) { + page2 = handle_pages(command, "/path "); + if (page2 == 0) + return 0; + } else { + page2 = command; + } + + char *page = malloc(strlen(page2) + 1); + strcpy(page, page2); + if (page[strlen(page) - 1] == '\n') { + page[strlen(page) - 1] = '\0'; + } + if (strlen(page) == 0) { + return 1; + } + + if (top) { + printf("Path of %s:\n", page); + hashmap_free(already_done); + already_done = hashmap_create(); + } + + uintptr_t result; + hashmap_get(inv, page, strlen(page), &result); + + if (top && result != 1) { + printf("You don't have %s.\n", page); + return 1; + } + + hashmap_get(already_done, page, strlen(page), &result); + + if (result != 0) { + return 1; + } + hashmap_set(already_done, page, strlen(page), (uintptr_t)1, 0); + + hashmap_get(elements_rev, page, strlen(page) - 1, &result); + + if (result == 0 || strlen((char *)result) < 1) { + return 1; + } + + // todo: refactor; + char *tmp = (char *)result, *tmp2 = 0; + if (tmp[strlen(tmp) - 1] == '\n') { + tmp[strlen(tmp) - 1] = '\0'; + } + + int combos = 0; + for (int i = 1; i < MAX_COMBO_LENGTH; i++) { + combos = i; + tmp2 = tmp; + if (tmp2[0] == ';') { + tmp2++; + } + tmp = strstr(tmp2, ";"); + if (tmp == 0) { + tmp = &tmp2[strlen(tmp2)]; + } + + char *tmp23 = malloc(strlen(tmp2) - strlen(tmp) + 2); + memcpy(tmp23, &tmp2[0], strlen(tmp2) - strlen(tmp) + 2); + tmp23[strlen(tmp2) - strlen(tmp) + 1] = '\0'; + char *strstrd = strstr(tmp23, ";"); + if (strstrd != 0) { + strstrd[0] = '\0'; + } + + path_command(tmp23, elements_rev, already_done, 0, inv); + + if (strlen(tmp23) < 1) { + break; + } + free(tmp23); + } + + printf("<- %s\n-> %s\n", (char *)result, (char *)page); + return 1; } int slash_command(char *command, hashmap *inv) { - char *invs = "/inv "; - - int page; - - if (strncmp(command, invs, strlen(invs)) == 0) { - page = stoi(&command[strlen(invs)]); - } else if (strncmp(command, invs, strlen(invs) - 1) == 0) { - page = 1; - } else { + int page = handle_pages_int(command, "/inv "); + if (page == 0) return 0; - } printf("Your inventory (page %i):\n", page); struct pager i = {.page = page - 1, .i = -1, .is_poll = 0}; hashmap_iterate(inv, inv_handler, &i); + printf("Total: %i\n", i.i + 1); return 1; } diff --git a/src/command.h b/src/command.h index 24d7e36..cdd78bf 100644 --- a/src/command.h +++ b/src/command.h @@ -2,6 +2,8 @@ int get_command(char *command, char *command_re, char **sort_tmp); int slash_command(char *command, hashmap *inv); int suggest_command(char *command, char *command_re, hashmap *polls, - char *name); + hashmap *combos, char *name, int was_combination); int help_command(char *command); -int polls_command(char *command, hashmap *polls); \ No newline at end of file +int polls_command(char *command, hashmap *polls, hashmap *cmd); +int path_command(char *command, hashmap *elements_rev, hashmap *already_done, + int top, hashmap *inv); \ No newline at end of file diff --git a/src/loader.c b/src/loader.c index ab1a071..a41a19d 100644 --- a/src/loader.c +++ b/src/loader.c @@ -6,7 +6,58 @@ #include "main.h" -int load_elements(hashmap *m, char *table, int use_inv) { +struct write_struct { + FILE *fptr; + int mode; +}; + +int entry_handler(const void *key, size_t size, uintptr_t val, void *usr) { + struct write_struct *usr2 = (struct write_struct *)usr; + + char *val2 = (char *)val; + char *key2 = (char *)key; + + if (usr2->mode == 0) { + fwrite(val2, sizeof(char), strlen(val2), usr2->fptr); + fwrite(";", sizeof(char), 1, usr2->fptr); + fwrite(key2, sizeof(char), strlen(key2), usr2->fptr); + if (key2[strlen(key2) - 1] != '\n') { + fwrite("\n", sizeof(char), 1, usr2->fptr); + } + } else if (usr2->mode == 1) { + fwrite(key2, sizeof(char), strlen(key2), usr2->fptr); + if (key2[strlen(key2) - 1] != '\n') { + fwrite("\n", sizeof(char), 1, usr2->fptr); + } + } else { + fwrite(key2, sizeof(char), strlen(key2), usr2->fptr); + fwrite(";", sizeof(char), 1, usr2->fptr); + fwrite(val2, sizeof(char), strlen(val2), usr2->fptr); + if (val2[strlen(val2) - 1] != '\n') { + fwrite("\n", sizeof(char), 1, usr2->fptr); + } + } + + return 1; +} + +void write_elements(hashmap *m, char *table, int mode) { + FILE *fptr; + + fptr = fopen(table, "w"); + + if (fptr == NULL) + return; + + struct write_struct writer = {.fptr = fptr, .mode = mode}; + + hashmap_iterate(m, entry_handler, (void *)&writer); + //fwrite("\n", sizeof(char), 1, fptr); + + fclose(fptr); +} + +int load_elements(hashmap *m, char *table, int mode) { FILE *fptr; fptr = fopen(table, "r"); @@ -15,19 +66,28 @@ int load_elements(hashmap *m, char *table, int use_inv) { return 0; char *str; + char *str2; int did_something = 0; + int lines_get = 0; + while (1) { - str = calloc(MAX_FILE_SIZE, sizeof(char)); - if (!fgets(str, MAX_FILE_SIZE, fptr)) { - free(str); + str2 = calloc(MAX_FILE_SIZE, sizeof(char)); + if (!fgets(str2, MAX_FILE_SIZE, fptr)) { + free(str2); break; } + lines_get++; + + str = calloc(strlen(str2) + 1, sizeof(char)); + strcpy(str, str2); + free(str2); + did_something = 1; - if (use_inv == 1) { + if (mode == 1) { hashmap_set(m, str, strlen(str) - 1, (uintptr_t)1, 0); continue; } @@ -36,10 +96,20 @@ int load_elements(hashmap *m, char *table, int use_inv) { combo_o[0] = '\0'; combo_o++; - char *combo = calloc(strlen(combo_o)+1,sizeof(char)); - strcpy(combo,combo_o); + char *combo = calloc(strlen(combo_o) + 1, sizeof(char)); + strcpy(combo, combo_o); - if (use_inv == 2) { + if (mode == 3) { + uintptr_t result; + hashmap_get(m, str, strlen(str) - 1, &result); + + char *res = (char *)result; + + if (res != 0) + continue; + } + + if (mode == 2 || mode == 3) { hashmap_set(m, str, strlen(str) - 1, (uintptr_t)combo, 1); continue; } diff --git a/src/loader.h b/src/loader.h index 116d3ad..ad57b8d 100644 --- a/src/loader.h +++ b/src/loader.h @@ -1,2 +1,3 @@ #include "map.h" -int load_elements(hashmap *m, char *table, int use_inv); \ No newline at end of file +int load_elements(hashmap *m, char *table, int mode); +void write_elements(hashmap *m, char *table, int mode); \ No newline at end of file diff --git a/src/main.c b/src/main.c index e7b52b3..0028bb7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,6 @@ #include "command.h" #include "loader.h" +#include "map.h" #include #include #include @@ -8,23 +9,28 @@ #include "main.h" -// todo: polls, help commands, spacing in combos +// todo: spacing in combos, clean init_tables -void init_tables(hashmap *elements, hashmap *inv, hashmap *polls, int do_inv) { - load_elements(elements, "../elem_data/" combo_file, 0); - load_elements(elements, combo_file, 0) || - load_elements(elements, "bin/" combo_file, 0); +void init_tables(hashmap *elements, hashmap *inv, hashmap *polls, + hashmap *elements_rev, int do_inv) { + load_elements(elements, "../elem_data/" COMBO_FILE, 0); + load_elements(elements, COMBO_FILE, 0) || + load_elements(elements, "bin/" COMBO_FILE, 0); - load_elements(polls, "../elem_data/" poll_file, 2); + load_elements(elements_rev, "../elem_data/" COMBO_FILE, 3); + load_elements(elements_rev, COMBO_FILE, 3) || + load_elements(elements_rev, "bin/" COMBO_FILE, 3); + + load_elements(polls, "../elem_data/" POLL_FILE, 3); if (!do_inv) return; - load_elements(inv, "../elem_data/" inv_base_file, 1) || - load_elements(inv, inv_base_file, 1) || - load_elements(inv, "bin/" inv_base_file, 1); + load_elements(inv, "../elem_data/" INV_BASE_FILE, 1) || + load_elements(inv, INV_BASE_FILE, 1) || + load_elements(inv, "bin/" INV_BASE_FILE, 1); - load_elements(inv, inv_file, 1); + load_elements(inv, INV_FILE, 1); } int main(int argc, char *argv[]) { @@ -36,6 +42,8 @@ int main(int argc, char *argv[]) { } hashmap *elements = hashmap_create(); + hashmap *elements_rev = hashmap_create(); + hashmap *already_done = hashmap_create(); hashmap *inv = hashmap_create(); hashmap *polls = hashmap_create(); @@ -43,11 +51,13 @@ int main(int argc, char *argv[]) { char *command_re = calloc(MAX_BUF_LENGTH, sizeof(char)); char **sort_tmp = calloc(MAX_COMBO_LENGTH, sizeof(char **)); - int wasCombination = 0; + int was_combination = 0; - init_tables(elements, inv, polls, 1); + init_tables(elements, inv, polls, elements_rev, 1); - printf("%s, welcome to Elemental on Command Line!\nType /help for commands.\n", name); + printf("user:%s, welcome to Elemental on Command Line!\nType /help for " + "commands.\n", + name); int newline = 1; while (1) { // todo: separate into functions @@ -71,18 +81,24 @@ int main(int argc, char *argv[]) { command[i] = tolower(command[i]); } - if (wasCombination && suggest_command(command, command_re, polls, name)) { - wasCombination = 0; + if (was_combination && + suggest_command(command, command_re, polls, elements, name, was_combination)) { continue; } - wasCombination = 0; + was_combination = 0; if (help_command(command)) continue; - if (polls_command(command, polls)) - continue; + if (polls_command(command, polls, elements)) { + hashmap_free(polls); + polls = hashmap_create(); + init_tables(elements, inv, polls, elements_rev, 0); + } if (slash_command(command, inv)) continue; + if (path_command(command, elements_rev, already_done, 1, inv)) + continue; + int combos = get_command(command, command_re, sort_tmp); if (combos < 2) @@ -99,24 +115,30 @@ int main(int argc, char *argv[]) { } } - if (failed) - continue; - uintptr_t result; hashmap_get(elements, command_re, strlen(command_re), &result); if (result == 0) { - init_tables(elements, inv, polls, 0); + init_tables(elements, inv, polls, elements_rev, 0); hashmap_get(elements, command_re, strlen(command_re), &result); } + if (result == 0 && failed) { + was_combination = 2; + printf("Use /suggest to upvote a pre-existing combination.\n"); + continue; + } + if (result == 0) { - wasCombination = 1; + was_combination = 1; printf("You didn't make anything; use /suggest to suggest an element.\n"); continue; } + if (failed) + continue; + char *res_str = (char *)result; uintptr_t result2; @@ -130,15 +152,7 @@ int main(int argc, char *argv[]) { hashmap_set(inv, res_str, strlen(res_str), (uintptr_t)1, 0); printf("You made %s!\n", res_str); - - FILE *fptr; - - fptr = fopen(inv_file, "a"); - if (fptr == NULL) - continue; - fwrite(res_str, sizeof(char), strlen(res_str), fptr); - fwrite("\n", sizeof(char), 1, fptr); - fclose(fptr); + write_elements(inv, INV_FILE, 1); } // free(command); diff --git a/src/main.h b/src/main.h index 199f669..9e1ac77 100644 --- a/src/main.h +++ b/src/main.h @@ -1,8 +1,9 @@ -#define inv_file "inv.txt" -#define inv_base_file "inv_base.txt" -#define combo_file "combos.txt" -#define poll_file "polls.txt" -#define polls_lock_file "polls_lock.txt" +#define INV_FILE "inv.txt" +#define INV_BASE_FILE "inv_base.txt" +#define COMBO_FILE "combos.txt" +#define POLL_FILE "polls.txt" +#define POLLS_LOCK_FILE "polls_lock.txt" +#define LB_FILE "lb.txt" #define MAX_BUF_LENGTH 1024 #define MAX_COMBO_LENGTH 1024 diff --git a/src/map.c b/src/map.c index 5f362bd..6f0aad3 100644 --- a/src/map.c +++ b/src/map.c @@ -199,67 +199,6 @@ int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val, return 0; } -int hashmap_get_set(hashmap *m, const void *key, size_t ksize, - uintptr_t *out_in) { - if (m->count + 1 > HASHMAP_MAX_LOAD * m->capacity) { - if (hashmap_resize(m) == -1) - return -1; - } - - uint32_t hash = hash_data(key, ksize); - struct bucket *entry = find_entry(m, key, ksize, hash); - if (entry->key == NULL) { - m->last->next = entry; - m->last = entry; - entry->next = NULL; - - ++m->count; - - entry->value = *out_in; - entry->key = key; - entry->ksize = ksize; - entry->hash = hash; - - return 0; - } - *out_in = entry->value; - return 1; -} - -int hashmap_set_free(hashmap *m, const void *key, size_t ksize, uintptr_t val, - hashmap_callback c, void *usr) { - if (m->count + 1 > HASHMAP_MAX_LOAD * m->capacity) { - if (hashmap_resize(m) == -1) - return -1; - } - - uint32_t hash = hash_data(key, ksize); - struct bucket *entry = find_entry(m, key, ksize, hash); - if (entry->key == NULL) { - m->last->next = entry; - m->last = entry; - entry->next = NULL; - - ++m->count; - - entry->key = key; - entry->ksize = ksize; - entry->hash = hash; - entry->value = val; - // there was no overwrite, exit the function. - return 0; - } - // allow the callback to free entry data. - // use old key and value so the callback can free them. - // the old key and value will be overwritten after this call. - int error = c(entry->key, ksize, entry->value, usr); - - // overwrite the old key pointer in case the callback frees it. - entry->key = key; - entry->value = val; - return error; -} - int hashmap_get(hashmap *m, const void *key, size_t ksize, uintptr_t *out_val) { uint32_t hash = hash_data(key, ksize); struct bucket *entry = find_entry(m, key, ksize, hash); @@ -270,40 +209,6 @@ int hashmap_get(hashmap *m, const void *key, size_t ksize, uintptr_t *out_val) { return entry->key != NULL ? 1 : 0; } -// doesn't "remove" the element per se, but it will be ignored. -// the element will eventually be removed when the map is resized. -void hashmap_remove(hashmap *m, const void *key, size_t ksize) { - uint32_t hash = hash_data(key, ksize); - struct bucket *entry = find_entry(m, key, ksize, hash); - - if (entry->key != NULL) { - - // "tombstone" entry is signified by a NULL key with a nonzero value - // element removal is optional because of the overhead of tombstone checks - entry->key = NULL; - entry->value = 0xDEAD; // I mean, it's a tombstone... - - ++m->tombstone_count; - } -} - -void hashmap_remove_free(hashmap *m, const void *key, size_t ksize, - hashmap_callback c, void *usr) { - uint32_t hash = hash_data(key, ksize); - struct bucket *entry = find_entry(m, key, ksize, hash); - - if (entry->key != NULL) { - c(entry->key, entry->ksize, entry->value, usr); - - // "tombstone" entry is signified by a NULL key with a nonzero value - // element removal is optional because of the overhead of tombstone checks - entry->key = NULL; - entry->value = 0xDEAD; // I mean, it's a tombstone... - - ++m->tombstone_count; - } -} - int hashmap_size(hashmap *m) { return m->count - m->tombstone_count; } int hashmap_iterate(hashmap *m, hashmap_callback c, void *user_ptr) { diff --git a/src/map.h b/src/map.h index 775b62d..ca305c8 100644 --- a/src/map.h +++ b/src/map.h @@ -11,10 +11,6 @@ #define hashmap_str_lit(str) (str), sizeof(str) - 1 #define hashmap_static_arr(arr) (arr), sizeof(arr) -// removal of map elements is disabled by default because of its slight -// overhead. if you want to enable this feature, uncomment the line below: -// #define __HASHMAP_REMOVABLE - #include #include @@ -44,69 +40,17 @@ struct hashmap { struct bucket *last; }; -// hashmaps can associate keys with pointer values or integral types. typedef struct hashmap hashmap; -// a callback type used for iterating over a map/freeing entries: -// `int (const void* key, size_t size, uintptr_t value, void* -// usr)` `usr` is a user pointer which can be passed through `hashmap_iterate`. typedef int (*hashmap_callback)(const void *key, size_t ksize, uintptr_t value, void *usr); hashmap *hashmap_create(void); - -// only frees the hashmap object and buckets. -// does not call free on each element's `key` or `value`. -// to free data associated with an element, call `hashmap_iterate`. void hashmap_free(hashmap *map); - -// does not make a copy of `key`. -// you must copy it yourself if you want to guarantee its lifetime, -// or if you intend to call `hashmap_key_free`. -// returns -1 on error. int hashmap_set(hashmap *map, const void *key, size_t ksize, uintptr_t value, int free_old); - -// adds an entry if it doesn't exist, using the value of `*out_in`. -// if it does exist, it sets value in `*out_in`, meaning the value -// of the entry will be in `*out_in` regardless of whether or not -// it existed in the first place. -// returns -1 on error. -// returns 1 if the entry already existed, returns 0 otherwise. -int hashmap_get_set(hashmap *map, const void *key, size_t ksize, - uintptr_t *out_in); - -// similar to `hashmap_set()`, but when overwriting an entry, -// you'll be able properly free the old entry's data via a callback. -// unlike `hashmap_set()`, this function will overwrite the original key -// pointer, which means you can free the old key in the callback if applicable. -int hashmap_set_free(hashmap *map, const void *key, size_t ksize, - uintptr_t value, hashmap_callback c, void *usr); - int hashmap_get(hashmap *map, const void *key, size_t ksize, uintptr_t *out_val); - -#ifdef __HASHMAP_REMOVABLE -void hashmap_remove(hashmap *map, const void *key, size_t ksize); - -// same as `hashmap_remove()`, but it allows you to free an entry's data first -// via a callback. -void hashmap_remove_free(hashmap *m, const void *key, size_t ksize, - hashmap_callback c, void *usr); -#endif - int hashmap_size(hashmap *map); - -// iterate over the map, calling `c` on every element. -// goes through elements in the order they were added. -// the element's key, key size, value, and `usr` will be passed to `c`. -// if `c` returns -1 the iteration is aborted. -// returns the last result of `c` int hashmap_iterate(hashmap *map, hashmap_callback c, void *usr); - -// dumps bucket info for debugging. -// allows you to see how many collisions you are getting. -// `0` is an empty bucket, `1` is occupied, and `x` is removed. -// void bucket_dump(hashmap *m); - #endif // map_h diff --git a/wrap.sh b/wrap.sh index 0ef25f0..dce3017 100755 --- a/wrap.sh +++ b/wrap.sh @@ -1,5 +1,6 @@ make -CC=x86_64-w64-mingw32-gcc BIN=./bin/elem.exe make -CC=i686-w64-mingw32-gcc BIN=./bin/elem_32.exe make -rm ./elem.tar.gz -tar -cvf ./elem.tar.gz bin \ No newline at end of file +CC=x86_64-w64-mingw32-gcc BIN=bin/elem.exe make +CC=i686-w64-mingw32-gcc BIN=bin/elem_32.exe make +strip --strip-all bin/elem +rm elem.tar.gz +tar -cvf elem.tar.gz bin \ No newline at end of file