diff --git a/include/mx/list.h b/include/mx/list.h new file mode 100644 index 0000000..558f8bf --- /dev/null +++ b/include/mx/list.h @@ -0,0 +1,97 @@ +/** + * @file list.h Array List + * This is an array list implementation inspired by the stb data structures + * single header library. The list itself works like any C pointer, except + * the real base pointer is before the pointer you will see. + */ +#ifndef _MX_LIST_H_ +#define _MX_LIST_H_ + +#include "mx/base.h" +#include "mx/assert.h" +#include +#include + +struct mx_list_self_t { + int count; +}; + +/** + * @brief Internal function for retreiving the list header. + * @param list The list. + * @returns The list header. + */ +#define mx_list_self(list) (((struct mx_list_self_t*)(list))-1) + +/** + * @brief The number of items in the list. + * @param list The list. + * @returns The number of items in the list. +*/ +#define mx_list_count(list) (((list) == NULL) ? 0 : mx_list_self(list)->count) + +#define mx_list_resize(list, count) do { \ + struct mx_list_self_t *self = (list == NULL) ? NULL : mx_list_self(list); \ + self = realloc(self, sizeof(*self) + sizeof(*(list)) * (count)); \ + if (self != NULL) { \ + self->count = (count); \ + (list) = (void*)(self+1); \ + } \ +} while (0) + +/** + * @brief Insert an item into the list (with assignment) + * @param list The list. + * @param index The index to insert the item into. + * @param item The item to insert. + * @remarks This overload is for things that can be assigned, like base types. Useful for rvalues. + */ +#define mx_list_insert(list, index, item) do { \ + int idx = (index); \ + if (list == NULL) { \ + MX_ASSERT(idx == 0, "mx_list_insert() can only insert at 0 for an empty list."); \ + mx_list_resize(list, 1); \ + list[0] = item; \ + } else { \ + int count = mx_list_count(list) \ + MX_ASSERT(idx > 0 && idx <= count, "mx_list_insert() expected a range between 0 and mx_list_count()."); \ + mx_list_resize(list, count + 1); \ + memmove(&list[idx]+1, &list[idx], (count - idx) * sizeof(*list)); \ + list[idx] = item; \ + } \ +} while(0) + +/** + * @brief Insert an item into the list (with copy) + * @param list The list. + * @param index The index to insert the item into. + * @param item The item to insert. + * @remarks This overload is for things that can be copied, like structs. Item must be an addressable lvalue. + */ +#define mx_list_insert_copy(list, index, item) do { \ + int idx = (index); \ + if (list == NULL) { \ + MX_ASSERT(idx == 0, "mx_list_insert() can only insert at 0 for an empty list."); \ + mx_list_resize(list, 1); \ + memcpy(&list[0], &item, sizeof(*item)); \ + } else { \ + int count = mx_list_count(list) \ + MX_ASSERT(idx > 0 && idx <= count, "mx_list_insert() expected a range between 0 and mx_list_count()."); \ + mx_list_resize(list, count + 1); \ + memmove(&list[idx]+1, &list[idx], (count - idx) * sizeof(*list)); \ + memcpy(&list[idx], &item, sizeof(*item)); \ + } \ +} while(0) + +/** + * @brief Free the list, completely destroying any associated memory. + * @param list The list to free. + */ +#define mx_list_free(list) do { \ + if (list != NULL) { \ + free(mx_list_self(list)); \ + list = NULL; \ + } \ +} while(0) + +#endif