diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/container.h | 363 |
1 files changed, 354 insertions, 9 deletions
diff --git a/include/container.h b/include/container.h index 47eb0a7..7399bed 100644 --- a/include/container.h +++ b/include/container.h @@ -17,6 +17,7 @@ #define struct_member_size(type, member) sizeof(( (type*)0 )->member) typedef unsigned char byte; +/// Find closest powers of 2 #ifdef __GNUC__ #define upper_2_power_32(number) (32 - __builtin_clz(number)) #define upper_2_power_64(number) (64 - __builtin_clzl(number)) @@ -29,6 +30,7 @@ typedef unsigned char byte; unsigned long __bit_scan_64(int64_t number); #endif +/// define FALLTHROUGH macro #if defined(__clang__) || defined(__GNUC__) #define FALLTHROUGH __attribute__((fallthrough)) #elif defined(_MSC_VER) @@ -37,12 +39,17 @@ typedef unsigned char byte; #define FALLTHROUGH ((void)0) #endif +/// define TODO macro #define TODO (void)(*(volatile char*)0) +/// typedefs for array qsort typedef int(*qsort_cmp_t)(const void*, const void*); #define get_qsort_cmp(type) __qsort_cmps[sizeof(type)] extern const qsort_cmp_t __qsort_cmps[64]; +typedef void*(*memcpy_t)(void* restrict, const void*, size_t); + +/// default qsort_cmp_t functions to be used with integral types. #ifdef CONTAINER_EXPOSE_HELPERS #define _CONTAINER_STATIC int __default_char_cmp(const void* a, const void* b); @@ -124,12 +131,127 @@ extern const qsort_cmp_t __qsort_cmps[64]; int array_compare(const void *const a1, const void *const a2, qsort_cmp_t comp); /// Exact copy of the array. you have to free the pointer as well. void* array_copy(void* old); +/// Search for element `element` in array `array` using comparator `cmp`. +/// Returns bool. Assumes that the array is sorted. char array_binary_search(void* array, void* element, qsort_cmp_t cmp); /* ----------------------------------------------------------------- */ /* ----------------------LINKED LIST HEADER------------------------- */ /* ----------------------------------------------------------------- */ -// TODO! + +struct __linked_list_meta { + size_t element_size; + size_t assumed_size; +}; +struct linked_list_node { + void* data; + struct linked_list_node *next; +}; +/// holds meta about list and first element. you should not use it's members. +struct linked_list { + struct __linked_list_meta meta; + struct linked_list_node *first; + struct linked_list_node *last; +}; +/// creates an empty linked list. `memb_size` should usually be sizeof(element type), +/// O(1) +struct linked_list ll_create(size_t memb_size); +/// Created linked list with elements as in buf. copies the memory with `memcpy`. +/// Assumes buffer size is >= buff_len * memb_size +/// O(buff_len) +struct linked_list ll_create_from_buffer(size_t memb_size, const void* buf, size_t buff_len); +/// iteratively frees a linked list +/// O(n) +void ll_free(struct linked_list* list); +/// searches an `element` in the list using `cmp`. returst whether it found it or not. +/// O(n) +char ll_search(const struct linked_list* list, const void* element, qsort_cmp_t cmp); +/// get a pointer to node at index i. DO NOT FREE THIS POINTER. returns NULL if +/// list is not long enough. +/// O(i) +struct linked_list_node* ll_at(const struct linked_list* list, const size_t i); +/// truncate a list starting from index i. if i==0, frees the list and you have to initialize a new one. +/// O(n) +void ll_truncate(struct linked_list* list, const size_t i); +/// returns a size of a list as stored in metadata. +/// Should be same as `ll_definite_size` unless user modifies nodes directly. Which you should not do. +/// O(1) +size_t ll_probably_size(const struct linked_list* list); +/// Counts elements in linked list, updating size in metadata if necessary. +/// if `ll_probably_size` is greater than `ll_definite_size`, there was a memory leak. +// if it is equal, everything is fine +/// else, the creator of the library is dumb. +/// O(n) +size_t ll_definite_size(struct linked_list *list); +/// creates a new node at the end of list and copies memory from element to new node. +/// The amount of memory copied is `memb_size` that was passed in `ll_create*` function. +/// If list->last->next == NULL, O(1) (which is guaranteed if you don't touch the nodes). +void ll_append(struct linked_list *list, const void* restrict element); +/// same as `ll_append`, except it does not perform memcpy and directly assigns pointer to node. +/// the only reqirement to this function is that `pointer` must be freeable with `free` function. +/// ONLY USE THIS IF YOU ARE 100% SURE THAT POINTER CAME UNMODIFIED FROM MALLOC, +/// OTHERWISE THE `ll_free` FUNCTION MAY DOUBLE-FREE, LEAK MEMORY AND OTHER TERRIBLE THINGS. +/// Complexity same as `ll_append`, usually O(1) +void ll_append_mallocated_pointer(struct linked_list *list, void* pointer); +/// inserts an element to the front of the list. +/// O(1) +void ll_insert_front(struct linked_list *list, const void* restrict element); +/// This to `ll_insert_front` is same as `ll_append_mallocated_pointer` to `ll_append`. +/// Copies `pointer` into newly created node. Same security considerations +/// as in `ll_append_mallocated_pointer`. +/// O(1) +void ll_insert_front_mallocated_pointer(struct linked_list *list, void* pointer); +/// inserts an element at index I if it exists. Returns 1 if insertion was successfull, +/// 0 if list is not long enough. +/// O(i) +char ll_insert_at(struct linked_list *list, const void* restrict element, size_t i); +/// copies a pointer directly into newly created node. see `ll_insert_at` +/// and `ll_append_mallocated_pointer` for more details. +/// O(i) +char ll_insert_at_mallocated_pointer(struct linked_list *list, void* pointer, size_t i); +/// removes the element from the front, or, if the list is empty, does nothing +/// O(1) +void ll_remove_front(struct linked_list* list); +/// removes the element from the back, or, if the list is empty, does nothing +/// O(n) +void ll_remove_back(struct linked_list* list); +/// Removes element at index i. if there is no such index, does nothing. +/// O(n) +char ll_remove_at(struct linked_list *list, size_t i); +/// splits a linked list into two, the second one starting from element at index `i`. +/// List that you passed stays valid, only it's size changes. return value is the second list. +/// O(n) +struct linked_list ll_split_at(struct linked_list *list, size_t i); +/// merges 2 lists into one, with elements of second list appearing at the end of first list +/// invalidates second list, leaving only first one valid. +/// O(1) +void ll_merge(struct linked_list* fst, struct linked_list* snd); +/// splits linked list into N linked lists at indexes given in varargs. +/// INDEXES MUST BE IN ASCENDING ORGER, OR ELSE ARE IGNORED. +/// Returns array (as defined in array implementation above). +/// O(n) +struct linked_list* ll_split_n(size_t splits, size_t i, ...); +/// merges N linked lists into one. +/// Leaves first list valid (being), all other ones are invalidated. +/// Lists are appended in the order they are given in varargs. +/// O(`merges`) +void ll_merge_n(size_t merges, struct linked_list *l, ...); +/// creates array (not an array of pointers, array of elements! it uses memcpy to copy elements) +/// Leaves linked list unchanged +void* ll_to_array(const struct linked_list* list); +/// Same as `ll_to_array`, except it acceps function used for copying. Use if you think +/// that your data should not be `memcpy`'d +void* ll_to_array_custom_cpy(const struct linked_list* list, memcpy_t cpy); +/// literally does: +/// 1. linked list to array +/// 2. sort the array using qsort +/// 3. array to linked list +/// since linked list in not random-access, i think this is literally more efficient. +/// Also, I am not in a mood to write sorting algorithms today +void ll_sort(struct linked_list *list, qsort_cmp_t cmp); +/// Deallocates a node and returns next one; +struct linked_list_node* ll_free_node(struct linked_list_node* node); + /* ------------------------------------------------------------------------- */ /* ------From now on, the rest of the header is implementation details------ */ @@ -160,7 +282,7 @@ void *_insert_to_index_dynarray(void *const dynarray, const void *const element, // uncomment in dev mode so that LSP highlights the code -/*#define CONTAINER_IMPLEMENTATION*/ +#define CONTAINER_IMPLEMENTATION #ifdef CONTAINER_IMPLEMENTATION /* ----------------------------------------------------------------- */ @@ -330,7 +452,6 @@ void *_insert_to_index_dynarray(void *const dynarray, const void *const element, int array_compare(const void *const a1, const void *const a2, qsort_cmp_t comp) { struct _dynarray_header *hdr = array_header(a1); struct _dynarray_header *hdr2 = array_header(a2); - assert(hdr->memb_size == hdr2->memb_size && "The arrays being compared do not have same element size"); const size_t s1 = hdr->size, s2 = hdr->size; const size_t elsize = hdr->memb_size; size_t i; @@ -341,14 +462,13 @@ int array_compare(const void *const a1, const void *const a2, qsort_cmp_t comp) if (s1 == s2) return 0; if (i == s1) return -1; if (i == s2) return 1; -#define NDEBUG - assert(0); -#undef NDEBUG + TODO; + __builtin_unreachable(); } -/*void* array_copy(void* old) {*/ -/* //TODO!*/ -/*}*/ +void* array_copy(void* old) { + return _alloc_dynarray_buffer(array_element_size(old), array_size(old), old, array_size(old) * array_element_size(old)); +} char array_binary_search(void* array, void* element, qsort_cmp_t cmp) { ssize_t index1 = -1, @@ -364,6 +484,231 @@ char array_binary_search(void* array, void* element, qsort_cmp_t cmp) { } } +/* ----------------------------------------------------------------- */ +/* ------------------LINKED LIST IMPLEMENTATION--------------------- */ +/* ----------------------------------------------------------------- */ +struct linked_list ll_create(size_t memb_size) { + struct __linked_list_meta meta = { + .assumed_size = 0, + .element_size = memb_size, + }; + struct linked_list ret = { + .meta = meta, + .first = NULL, + .last = NULL, + }; + return ret; +} +struct linked_list ll_create_from_buffer(size_t memb_size, const void* buf, size_t buff_len) { + struct __linked_list_meta meta = { + .assumed_size = buff_len, + .element_size = memb_size, + }; + struct linked_list_node *current_node = calloc(1, sizeof(struct linked_list_node)); + struct linked_list ret = { .meta = meta, .first = current_node }; + for (size_t i = 0; i < buff_len; ++i) { + current_node->data = malloc(memb_size); + memcpy(current_node->data, (const byte *)buf + memb_size * i, memb_size); + if (i != buff_len - 1) { + current_node->next = calloc(1, sizeof(struct linked_list_node)); + current_node = current_node->next; + } + } + ret.last = current_node; + return ret; +} +/// An older, more ugly implementation +void ll_free_(struct linked_list* list) { + struct linked_list_node *to_dealloc = NULL; + for (struct linked_list_node *current = list->first; current != NULL; current = current->next) { + free(to_dealloc); + free(current->data); + to_dealloc = current; + } + free(to_dealloc); + memset(list, 0, sizeof(*list)); +} +void ll_free(struct linked_list* list) { + struct linked_list_node* node = list->first; + while ((node = ll_free_node(node)) != NULL) {} + memset(list, 0, sizeof(*list)); +} +char ll_search(const struct linked_list* list, const void* element, qsort_cmp_t cmp) { + for (struct linked_list_node *current = list->first; current != NULL; current = current->next) { + if (cmp(element, current->data) == 0) return 1; + } + return 0; +} +struct linked_list_node* ll_at(const struct linked_list* list, const size_t i) { + size_t j = 0; + if (i == list->meta.assumed_size - 1) return list->last; + if (i >= list->meta.assumed_size) return NULL; + for (struct linked_list_node *current = list->first; current != NULL; current = current->next) { + if (i == j) return current; + ++j; + } + return NULL; +} +void ll_truncate(struct linked_list* list, const size_t i) { + if (i == 0) { + ll_free(list); + return; + } + struct linked_list_node *start = ll_at(list, i-1); + if (start == NULL) return; + struct linked_list freeable = { + .meta = {list->meta.element_size, list->meta.assumed_size - i}, + .first = start->next, + }; + ll_free(&freeable); + list->meta.assumed_size = i; + start->next = NULL; + list->last = start; +} +size_t ll_probably_size(const struct linked_list* list) { + return list->meta.assumed_size; +} +size_t ll_definite_size(struct linked_list *list) { + int i = 0; + for (struct linked_list_node *current = list->first; current != NULL; current = current->next) + ++i; + list->meta.assumed_size = i; + return i; +} +void ll_append(struct linked_list *list, const void* restrict element) { + struct linked_list_node *new = calloc(1, sizeof(struct linked_list_node)); + new->data = malloc(list->meta.element_size); + memcpy(new->data, element, list->meta.element_size); + if (list->last == NULL) list->first = list->last = new; + else list->last->next = new; + ++list->meta.assumed_size; + list->last = new; +} +void ll_append_mallocated_pointer(struct linked_list *list, void* pointer) { + struct linked_list_node *new = calloc(1, sizeof(struct linked_list_node)); + new->data = pointer; + if (list->last == NULL) list->first = list->last = new; + else list->last->next = new; + ++list->meta.assumed_size; + list->last = new; +} +void ll_insert_front(struct linked_list *list, const void* restrict element) { + struct linked_list_node *new = calloc(1, sizeof(struct linked_list_node)); + new->data = malloc(list->meta.element_size); + memcpy(new->data, element, list->meta.element_size); + if (list->first == NULL) list->first = list->last = new; + else { + new->next = list->first; + list->first = new; + } + ++list->meta.assumed_size; +} +void ll_insert_front_mallocated_pointer(struct linked_list *list, void* pointer) { + struct linked_list_node *new = calloc(1, sizeof(struct linked_list_node)); + new->data = pointer; + if (list->first == NULL) list->first = list->last = new; + else { + new->next = list->first; + list->first = new; + } + ++list->meta.assumed_size; +} +char ll_insert_at(struct linked_list *list, const void* restrict element, size_t i) { + if (i == 0) { + ll_insert_front(list, element); + return 1; + } + if (i == list->meta.assumed_size) { + ll_append(list, element); + return 1; + } + if (list->last == NULL) return 0; + + struct linked_list_node* delim; + if ( (delim = ll_at(list, i-1)) == NULL ) return 0; + struct linked_list_node* new = malloc(sizeof(struct linked_list_node)); + new->next = delim->next; + delim->next = new; + new->data = malloc(list->meta.element_size); + memcpy(new->data, element, list->meta.element_size); + ++list->meta.assumed_size; + return 1; +} +char ll_insert_at_mallocated_pointer(struct linked_list *list, void* pointer, size_t i) { + if (i == 0) { + ll_insert_front_mallocated_pointer(list, pointer); + return 1; + } + if (i == list->meta.assumed_size) { + ll_append_mallocated_pointer(list, pointer); + return 1; + } + if (list->last == NULL) return 0; + + struct linked_list_node* delim; + if ((delim = ll_at(list, i-1)) == NULL) return 0; + struct linked_list_node* new = malloc(sizeof(struct linked_list_node)); + new->next = delim->next; + delim->next = new; + new->data = pointer; + ++list->meta.assumed_size; + return 1; +} +void ll_remove_front(struct linked_list* list) { + if (list->first == NULL) return; + list->first = ll_free_node(list->first); + --list->meta.assumed_size; + if (list->meta.assumed_size <= 1) list->last = list->first; +} +void ll_remove_back(struct linked_list* list) { + if (list->meta.assumed_size == 0) return; + if (list->meta.assumed_size == 1) { + list->first = ll_free_node(list->first); + goto common; + } + if (list->meta.assumed_size == 2) { + list->first->next = ll_free_node(list->last); + list->last = list->first; + goto common; + } + struct linked_list_node* prelast; + if ((prelast = ll_at(list, list->meta.assumed_size-1-1)) == NULL) return; + prelast->next = ll_free_node(list->last); + list->last = prelast; +common: + --list->meta.assumed_size; +} +char ll_remove_at(struct linked_list *list, size_t i) { + if (list->first == NULL) return 0; + if (i == 0) { + ll_remove_front(list); + return 1; + } + if (i == list->meta.assumed_size - 1) { + ll_remove_back(list); + return 1; + } + struct linked_list_node *before_removed; + if ((before_removed = ll_at(list, i-1)) == NULL) return 0; + before_removed->next = ll_free_node(before_removed->next); + --list->meta.assumed_size; + return 1; +} +struct linked_list ll_split_at(struct linked_list *list, size_t i); +void ll_merge(struct linked_list* fst, struct linked_list* snd); +struct linked_list* ll_split_n(size_t splits, size_t i, ...); +void ll_merge_n(size_t merges, struct linked_list *l, ...); +void* ll_to_array(const struct linked_list* list); +void* ll_to_array_custom_cpy(const struct linked_list* list, memcpy_t cpy); +void ll_sort(struct linked_list *list, qsort_cmp_t cmp); +struct linked_list_node* ll_free_node(struct linked_list_node* node) { + if (node == NULL) return NULL; + struct linked_list_node* ret = node->next; + free(node->data); + free(node); + return ret; +} + #endif // CONTAINER_IMPLEMENTATION |