commit c73be4c21cd4b567425d4cceac65c4153261bd9d Author: Squibid Date: Sat Aug 16 18:25:33 2025 -0400 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9daeafb --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3ec9cf4 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +test: + $(CC) test.c ds.c ds.h -o test -g2 -Og && ./test + +.PHONY: test +.DEFAULT: test diff --git a/README.md b/README.md new file mode 100644 index 0000000..b638bdd --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Data Structures +Probably not the most efficient, but I don't really care (it's still faster +than the average site using js). + +## Singly/Doubly Linked List +Example of working with a singly linked list (sll): +```c +char *a = "one"; +char *b = "two"; +ds_sll_t *ll = ds_sll_new_node(a); // [ "one" ] +ds_sll_insert(ll, b); // [ "one", "two" ] +ds_ll_foreach(ds_sll_t, ll) { + puts(cur->data); + // one + // two +} +char *pa = ds_sll_remove(&ll, 0); // [ "two" ] +char *pb = ds_sll_remove(&ll, 0); // [ ] +``` +If you need a doubly linked list just use the equivalent `ds_dll` functions and +datatypes. + +## Hash Map +```c +struct complex { + int num; + char *str; +}; + +struct complex *a = calloc(1, sizeof(struct complex)); +a->num = 1; +a->str = "abc"; +struct complex *b = calloc(1, sizeof(struct complex)); +b->num = 2; +b->str = "def"; + +ds_hmp_t *hmp = ds_hmp_init(101); +ds_hmp_insert(hmp, a->str, a); // [ (23)[ [ a->str, a ] ] ] +ds_hmp_insert(hmp, b->str, b); // [ (23)[ [ a->str, a ] ], (58)[ [ b->str, b ] ] +struct complex *pa = ds_hmp_remove(hmp, a->str); // a +free(pa->str); +free(pa); +ds_hmp_free(&hmp); +free(b); +``` + +# TODO +- [ ] more data structures +- [ ] tests diff --git a/ds.c b/ds.c new file mode 100644 index 0000000..d9ea54b --- /dev/null +++ b/ds.c @@ -0,0 +1,257 @@ +#include +#include + +#include "ds.h" + +ds_sll_t +*ds_sll_new_node(void *data) +{ + ds_sll_t *node; + + node = calloc(1, sizeof(ds_sll_t)); + node->data = data; + node->next = NULL; + + return node; +} + +ds_dll_t +*ds_dll_new_node(void *data) +{ + ds_dll_t *node; + + node = calloc(1, sizeof(ds_dll_t)); + node->data = data; + node->next = node->prev = NULL; + + return node; +} + +void +ds_sll_insert(ds_sll_t *ll, void *data) +{ + ds_ll_foreach(ds_sll_t, ll) { + if (!cur->data) { + cur->data = data; + return; + } else if (!cur->next) { + cur->next = ds_sll_new_node(data); + return; + } + } +} + +void +ds_dll_insert(ds_dll_t *ll, void *data) +{ + ds_ll_foreach(ds_dll_t, ll) { + if (!cur->data) { + cur->data = data; + return; + } else if (!cur->next) { + cur->next = ds_dll_new_node(data); + cur->next->prev = cur; + return; + } + } +} + +void +*ds_sll_remove(ds_sll_t **ll, unsigned idx) +{ + int i; + void *data; + ds_sll_t *cur, *rm; + + if (!ll || !*ll) { + return NULL; + } + + for (i = -1, cur = *ll;; i++, cur = cur->next) { + if (i + 1 == idx) { + if (idx == 0) { + rm = cur; + if (!rm) { + return NULL; + } + *ll = cur->next; + } else { + rm = cur->next; + if (!rm) { + return NULL; + } + cur->next = cur->next->next; + } + break; + } + } + + data = rm->data; + free(rm); + return data; +} + +void +*ds_dll_remove(ds_dll_t **ll, unsigned idx) +{ + int i; + void *data; + ds_dll_t *cur, *rm; + + if (!ll || !*ll) { + return NULL; + } + + for (i = -1, cur = *ll;; i++, cur = cur->next) { + if (i + 1 == idx) { + if (idx == 0) { + rm = cur; + if (!rm) { + return NULL; + } + if (cur->next) { + cur->next->prev = NULL; + *ll = cur->next; + } + } else { + rm = cur->next; + if (!rm) { + return NULL; + } + cur->next = cur->next->next; + if (cur->next) { + cur->next->prev = cur; + } + } + break; + } + } + + data = rm->data; + free(rm); + return data; +} + +ds_hmp_t +*ds_hmp_init(int data_len) +{ + ds_hmp_t *hmp; + + hmp = calloc(1, sizeof(ds_hmp_t)); + hmp->data_len = data_len; + hmp->data = calloc(data_len, sizeof(ds_sll_t **)); + + return hmp; +} + +void +ds_hmp_free(ds_hmp_t **hmp) +{ + int i; + _ds_hmp_kv_t *kv; + ds_sll_t *ll; + + if (!hmp || !*hmp) { + return; + } + + for (i = 0; i < (*hmp)->data_len; i++) { + ll = (*hmp)->data[i]; + while (ll && ll->data) { + if ((kv = ds_sll_remove(&ll, 0))) { + free(kv); + } + } + if (ll) { + free(ll); + } + } + free((*hmp)->data); + free(*hmp); +} + +int +_ds_hmp_gen_hash(char *str) +{ + unsigned long hash = 5381; + int c; + + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + + return hash; +} + +void +ds_hmp_insert(ds_hmp_t *hmp, char *key, void *data) +{ + _ds_hmp_kv_t *kv; + ds_sll_t *ll; + unsigned hash_pos; + + kv = malloc(sizeof(_ds_hmp_kv_t)); + kv->key = key; + kv->val = data; + + hash_pos = _ds_hmp_gen_hash(key) % hmp->data_len; + if (!hmp->data[hash_pos]) { + hmp->data[hash_pos] = calloc(1, sizeof(ds_sll_t)); + } + + /* get the ll and put the data into it */ + ll = hmp->data[hash_pos]; + ds_sll_insert(ll, kv); +} + +void +*ds_hmp_get(ds_hmp_t *hmp, char *key) +{ + ds_sll_t *cur, *ll; + unsigned hash_pos; + + hash_pos = _ds_hmp_gen_hash(key) % hmp->data_len; + ll = hmp->data[hash_pos]; + if (!ll) { + return NULL; + } + + ds_ll_foreach(ds_sll_t, ll) { + if (strcmp(((_ds_hmp_kv_t *)cur->data)->key, key) == 0) { + return ((_ds_hmp_kv_t *)cur->data)->val; + } + } + + return NULL; +} + +void +*ds_hmp_remove(ds_hmp_t *hmp, char *key) +{ + int i; + void *data; + unsigned hash_pos; + ds_sll_t *ll, *cur; + _ds_hmp_kv_t *kv; + + hash_pos = _ds_hmp_gen_hash(key) % hmp->data_len; + ll = hmp->data[hash_pos]; + if (!ll) { + return NULL; + } + + for (i = 0, cur = ll; cur; i++, cur = cur->next) { + if (strcmp(((_ds_hmp_kv_t *)cur->data)->key, key) == 0) { + kv = ds_sll_remove(&ll, i); + hmp->data[hash_pos] = ll; + if (!kv) { + return NULL; + } + data = kv->val; + free(kv); + return data; + } + } + + return NULL; +} diff --git a/ds.h b/ds.h new file mode 100644 index 0000000..7f5e777 --- /dev/null +++ b/ds.h @@ -0,0 +1,131 @@ +#ifndef _DS_LOADED +#define _DS_LOADED + +typedef struct _sll { + struct _sll *next; + void *data; +} ds_sll_t; + +typedef struct _dll { + struct _dll *next; + struct _dll *prev; + void *data; +} ds_dll_t; + +typedef struct { + ds_sll_t **data; + unsigned data_len; +} ds_hmp_t; + +typedef struct { + char *key; + void *val; +} _ds_hmp_kv_t; + +#define ds_ll_foreach(t, ll) for (t *cur = ll; cur; cur = cur->next) + +/** + * @brief create a new allocated node for a singly linked list + * + * @param data the data you want to store + * @return the node + */ +ds_sll_t *ds_sll_new_node(void *data); + +/** + * @brief create a new allocated node for a doubly linked list + * + * @param data the data you want to store + * @return the node + */ +ds_dll_t *ds_dll_new_node(void *data); + +/** + * @brief data to insert into a singly linked list. This will use + * ds_sll_new_node to create the node for you. + * + * @param ll singly linked list + * @param data data you want to add + */ +void ds_sll_insert(ds_sll_t *ll, void *data); + +/** + * @brief data to insert into a doubly linked list. This will use + * ds_sll_new_node to create the node for you. + * + * @param ll doubly linked list + * @param data data you want to add + */ +void ds_dll_insert(ds_dll_t *ll, void *data); + +/** + * @brief remove an index from a singly linked list + * + * @param ll singly linked list + * @param idx the node + * @return the data in the node + */ +void *ds_sll_remove(ds_sll_t **ll, unsigned idx); + +/** + * @brief remove an index from a doubly linked list + * + * @param ll doubly linked list + * @param idx the node + * @return the data in the node + */ +void *ds_dll_remove(ds_dll_t **ll, unsigned idx); + +/** + * @brief initialize a new hashmap + * + * @param data_len the amount of slots in the array + * @return the hashmap + */ +ds_hmp_t *ds_hmp_init(int data_len); + +/** + * @brief free all data allocated by ds_hmp_init and ds_hmp_insert + * + * @param hmp pointer to the hashmap + */ +void ds_hmp_free(ds_hmp_t **hmp); + +/** + * @brief generate a numerical hash from a given string. You shouldn't need to + * use this, but it's available just incase. + * + * @param str + * @return the hash + */ +int _ds_hmp_gen_hash(char *str); + +/** + * @brief insert data at key (which will be hashed using _ds_hmp_gen_hash). You + * are responsible for freeing the key later. + * + * @param hmp the hashmap to insert into + * @param key the key + * @param data the data + */ +void ds_hmp_insert(ds_hmp_t *hmp, char *key, void *data); + +/** + * @brief get something from a hashmap using it's key + * + * @param hmp the hashmap + * @param key the key where it's stored + * @return the data stored at the key + */ +void *ds_hmp_get(ds_hmp_t *hmp, char *key); + +/** + * @brief remove something from a hashmap using it's key + * + * @param hmp the hashmap + * @param key the key where it's stored + * @return the data stored at the key + */ +void *ds_hmp_remove(ds_hmp_t *hmp, char *key); + +#endif diff --git a/test.c b/test.c new file mode 100644 index 0000000..4ee951c --- /dev/null +++ b/test.c @@ -0,0 +1,41 @@ +#include "ds.h" +#include +#include + +struct complex { + int num; + char *str; +}; + +int +main(int argc, char *argv[]) +{ + char *a = "one"; + char *b = "two"; + ds_sll_t *ll = ds_sll_new_node(a); // [ "one" ] + ds_sll_insert(ll, b); // [ "one", "two" ] + ds_ll_foreach(ds_sll_t, ll) { + puts(cur->data); + } + char *pa = ds_sll_remove(&ll, 0); // [ "two" ] + char *pb = ds_sll_remove(&ll, 0); // [ ] + + struct complex *a2 = calloc(1, sizeof(struct complex)); + a2->num = 1; + a2->str = "abc"; + struct complex *b2 = calloc(1, sizeof(struct complex)); + b2->num = 2; + b2->str = "def"; + + ds_hmp_t *hmp = ds_hmp_init(101); + ds_hmp_insert(hmp, a2->str, a2); // [ (23)[ [ a->str, a ] ] ] + ds_hmp_insert(hmp, b2->str, b2); // [ (23)[ [ a->str, a ] ], (58)[ [ b->str, b ] ] + struct complex *pa2 = ds_hmp_remove(hmp, a2->str); // [ (23)[ [ a->str, a ] ] + struct complex *pb2 = ds_hmp_remove(hmp, b2->str); // [ (58)[ [ b->str, b ] ] + ds_hmp_free(&hmp); + puts(pa2->str); + puts(pb2->str); + + free(pa2); + free(pb2); +}