Compare commits

..

No commits in common. "main" and "0.0.0" have entirely different histories.
main ... 0.0.0

15 changed files with 200 additions and 698 deletions

10
.gitignore vendored
View file

@ -1,8 +1,2 @@
/bin/elem
/bin/elem.exe
/bin/elem_32.exe
/inv.txt
/inv_users.txt
/polls.txt
/elem.tar.gz
/combos.txt
/elem
/elem.exe

View file

@ -1,5 +1,5 @@
CC?=gcc
BIN?=bin/elem
BIN?=elem
make: src/main.c
$(CC) -ffunction-sections -fdata-sections -Wl,--gc-sections -Os -o $(BIN) src/map.c src/loader.c src/command.c src/main.c
$(CC) -o $(BIN) src/map.c src/loader.c src/command.c src/main.c

View file

@ -1,14 +1,2 @@
# 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/) 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, use the guide with the [Web proxy](https://git.dervland.net/elemental/elemental-to-web).
# Elemental on CLI
Elemental combination game written in C.

View file

@ -1,41 +0,0 @@
wind;air;air
dust;air;earth
heat;air;fire
cloud;air;water
land;earth;earth
lava;earth;fire
mud;earth;water
wildfire;fire;fire
smoke;fire;water
puddle;water;water
elements;air;earth;fire;water
dirt;dust;earth
clay;dirt;wind
cobble;air;lava
stone;cobble;earth
gravel;cobble;wind
blue;fire;heat
steam;heat;water
energy;steam;steam
sky;air;wind
sky;air;cloud
space;air;sky
space;sky;sky
void;space;space
zero;energy;void
numbers;elements;zero
one;numbers;numbers
color;blue;numbers
spectrum;color;numbers
pond;earth;puddle
pond;puddle;puddle
lake;pond;water
lake;pond;pond
sea;lake;lake
sea;lake;water
ocean;sea;sea
ocean;land;sea
planet;earth;ocean
planet;land;ocean
star;fire;sky
pollution;air;dust

View file

@ -1,4 +0,0 @@
air
earth
fire
water

11
combos.txt Normal file
View file

@ -0,0 +1,11 @@
wind;air;air
dust;air;earth
heat;air;fire
cloud;air;water
land;earth;earth
lava;earth;fire
mud;earth;water
wildfire;fire;fire
smoke;fire;water
puddle;water;water
elements;air;earth;fire;water

View file

@ -1,41 +1,11 @@
#include "map.h"
#include <time.h>
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "loader.h"
#include "main.h"
// huge cleanup operation soon
struct pager {
int page;
int i;
int is_poll;
hashmap *cmd;
};
struct verifier {
char *name;
char *sugg;
};
struct succ {
char *sugg;
int *points;
};
long stoi(const char *s) {
long i;
i = 0;
while (*s >= '0' && *s <= '9') {
i = i * 10 + (*s - '0');
s++;
}
return i;
}
#define MAX_BUF_LENGTH 1024
#define MAX_COMBO_LENGTH 1024
int fix_delim(char *command, char *needle) {
char *delim = strstr(command, needle);
@ -52,307 +22,8 @@ int sort_comp(const void *a, const void *b) {
return strcmp(*(char **)a, *(char **)b);
}
int inv_handler(const void *key, size_t size, uintptr_t val, void *usr) {
struct pager *i = usr;
if (val == 0)
return 0;
char *key2 = (char *)key;
char *val2 = (char *)val;
if (i->is_poll) {
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 {
printf("- %s\n", key2);
}
return 1;
}
int polls_handler(const void *key, size_t size, uintptr_t val, void *usr) {
struct verifier *verified = (struct verifier *)usr;
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, 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;
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, val3) == 0) {
verified->points[0]++;
}
free(val3);
return 0;
}
char *handle_pages(char *command, char *invs) {
char *data;
char *data2;
if (strncmp(command, invs, strlen(invs)) == 0) {
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, ",") ||
strstr(page, "+")) {
printf("This element contains illegal characters.\n");
return 1;
}
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());
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;
}
hashmap_set(polls, key, strlen(key), (uintptr_t)val, 1);
point_thing++;
if (point_thing == UPVOTE_IN) {
printf("Poll was added into the game!\n");
FILE *fptr;
hashmap_set(combos, command_re, strlen(command_re), (uintptr_t)page, 0);
write_elements(combos, "../elem_data/" COMBO_FILE, 0);
return 1;
}
printf("Suggested %s = %s.\n", command_re, page);
// todo: clean old polls
FILE *fptr;
write_elements(polls, "../elem_data/" POLL_FILE, 3);
return 1;
}
int help_command(char *command) {
char *page = handle_pages(command, "/help");
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, hashmap *cmd) {
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, .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) {
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;
}
int get_command(char *command, char *command_re, char **sort_tmp) {
char *get_command(char *command, char *command_re, char **sort_tmp) {
fgets(command, MAX_BUF_LENGTH - 1, stdin);
while (1) {
int get_out = 0;
@ -361,29 +32,21 @@ int get_command(char *command, char *command_re, char **sort_tmp) {
if (!get_out)
break;
}
if (command[0] == ';') {
command++;
}
int cl = strlen(command);
if (cl < 2)
return 0;
command[cl - 1] = '\0';
for (int i = 0; i < cl - 1; i++) {
command[i] = tolower(command[i]);
}
sort_tmp[0] = command;
int combos = 0;
for (int i = 1; i < MAX_COMBO_LENGTH; i++) {
combos = i;
sort_tmp[i] = strstr(sort_tmp[i - 1], ";");
if (sort_tmp[i] == 0 || sort_tmp[i] == sort_tmp[i - 1])
sort_tmp[i] = strstr(command, ";");
if (sort_tmp[i] == 0)
break;
if (strlen(sort_tmp[i]) < 2) {
sort_tmp[i][0] = '\0';
break;
}
sort_tmp[i][0] = '\0';
sort_tmp[i]++;
}
@ -401,5 +64,5 @@ int get_command(char *command, char *command_re, char **sort_tmp) {
command_re_tmp += strlen(last);
}
return combos;
return command_re;
}

View file

@ -1,9 +1 @@
#include "map.h"
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,
hashmap *combos, char *name, int was_combination);
int help_command(char *command);
int polls_command(char *command, hashmap *polls, hashmap *cmd);
int path_command(char *command, hashmap *elements_rev, hashmap *already_done,
int top, hashmap *inv);
char *get_command(char *command, char *command_re, char **sort_tmp);

View file

@ -4,126 +4,26 @@
#include <stdlib.h>
#include <string.h>
#include "main.h"
#define MAX_FILE_SIZE 1024 * 16
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) {
void load_elements(hashmap *m, char *table) {
FILE *fptr;
fptr = fopen(table, "r");
if (fptr == NULL)
return 0;
char *str;
char *str2;
int did_something = 0;
int lines_get = 0;
while (1) {
str2 = calloc(MAX_FILE_SIZE, sizeof(char));
if (!fgets(str2, MAX_FILE_SIZE, fptr)) {
free(str2);
str = calloc(MAX_FILE_SIZE, sizeof(char));
if (!fgets(str, MAX_FILE_SIZE, fptr))
break;
char *combo = strstr(str, ";");
combo[0] = '\0';
combo++;
hashmap_set(m, combo, strlen(combo) - 1, (uintptr_t)str);
}
lines_get++;
str = calloc(strlen(str2) + 1, sizeof(char));
strcpy(str, str2);
free(str2);
did_something = 1;
if (mode == 1) {
hashmap_set(m, str, strlen(str) - 1, (uintptr_t)1, 0);
continue;
}
char *combo_o = strstr(str, ";");
combo_o[0] = '\0';
combo_o++;
char *combo = calloc(strlen(combo_o) + 1, sizeof(char));
strcpy(combo, combo_o);
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;
}
hashmap_set(m, combo, strlen(combo) - 1, (uintptr_t)str, 1);
}
if (!did_something) {
free(str);
}
fclose(fptr);
return did_something;
// todo: properly free this
}

View file

@ -1,3 +1,2 @@
#include "map.h"
int load_elements(hashmap *m, char *table, int mode);
void write_elements(hashmap *m, char *table, int mode);
void load_elements(hashmap *m, char *table);

View file

@ -1,158 +1,36 @@
#include "command.h"
#include "loader.h"
#include "map.h"
#include <ctype.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "command.h"
#include "loader.h"
#include "main.h"
// todo: spacing in combos, clean init_tables
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(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, INV_FILE, 1);
}
#define MAX_BUF_LENGTH 1024
#define MAX_COMBO_LENGTH 1024
int main(int argc, char *argv[]) {
char *name;
if (argc < 2) {
name = "guest";
} else {
name = argv[1];
}
hashmap *elements = hashmap_create();
hashmap *elements_rev = hashmap_create();
hashmap *already_done = hashmap_create();
hashmap *inv = hashmap_create();
hashmap *polls = hashmap_create();
char *command = calloc(MAX_BUF_LENGTH, sizeof(char));
char *command_re = calloc(MAX_BUF_LENGTH, sizeof(char));
char **sort_tmp = calloc(MAX_COMBO_LENGTH, sizeof(char **));
int was_combination = 0;
load_elements(elements, "combos.txt");
init_tables(elements, inv, polls, elements_rev, 1);
printf("user:%s, welcome to Elemental on Command Line!\nType /help for "
"commands.\n",
name);
int newline = 1;
while (1) {
// todo: separate into functions
fflush(stdout);
if (newline) {
printf("\n");
}
fgets(command, MAX_BUF_LENGTH - 1, stdin);
int cl = strlen(command);
if (cl < 2) {
newline = 0;
continue;
} else {
newline = 1;
}
for (int i = 0; i < cl - 1; i++) {
command[i] = tolower(command[i]);
}
if (was_combination &&
suggest_command(command, command_re, polls, elements, name, was_combination)) {
continue;
}
was_combination = 0;
if (help_command(command))
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)
continue;
int failed = 0;
for (int i = 0; i < combos; i++) {
uintptr_t result;
hashmap_get(inv, sort_tmp[i], strlen(sort_tmp[i]), &result);
if (result != 1) {
printf("You don't have %s.\n", sort_tmp[i]);
failed = 1;
continue;
}
}
command_re = get_command(command, command_re, sort_tmp);
uintptr_t result;
hashmap_get(elements, command_re, strlen(command_re), &result);
if (result == 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");
printf("You didn't make anything.\n");
continue;
}
if (result == 0) {
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;
hashmap_get(inv, res_str, strlen(res_str), &result2);
if (result2 == 1) {
printf("You made %s, but you already have it.\n", res_str);
continue;
}
hashmap_set(inv, res_str, strlen(res_str), (uintptr_t)1, 0);
printf("You made %s!\n", res_str);
write_elements(inv, INV_FILE, 1);
printf("You made %s!\n", (char *)result);
}
// free(command);

View file

@ -1,12 +0,0 @@
#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
#define UPVOTE_IN 2
#define MAX_FILE_SIZE 1024
long stoi(const char *s);

106
src/map.c
View file

@ -2,11 +2,9 @@
// map.h
//
// Created by Mashpoe on 1/15/21.
// Modifications by BiglyDerv
//
#include "map.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -169,8 +167,7 @@ static struct bucket *find_entry(hashmap *m, const void *key, size_t ksize,
}
}
int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val,
int free_old) {
int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val) {
if (m->count + 1 > HASHMAP_MAX_LOAD * m->capacity) {
if (hashmap_resize(m) == -1)
return -1;
@ -189,16 +186,71 @@ int hashmap_set(hashmap *m, const void *key, size_t ksize, uintptr_t val,
entry->ksize = ksize;
entry->hash = hash;
}
if (free_old && entry->value != 0 && entry->value != val &&
strcmp((char *)entry->value, (char *)val) == 0) {
free((char *)val);
return 0;
}
entry->value = 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);
@ -209,6 +261,40 @@ 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) {

View file

@ -2,7 +2,6 @@
// map.h
//
// Created by Mashpoe on 1/15/21.
// Modifications by BiglyDerv
//
#ifndef map_h
@ -11,6 +10,10 @@
#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 <stddef.h>
#include <stdint.h>
@ -40,17 +43,68 @@ 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 <function name>(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);
int hashmap_set(hashmap *map, const void *key, size_t ksize, uintptr_t value,
int free_old);
// 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);
// 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

View file

@ -1,6 +0,0 @@
make
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