From b1dd9baf98ba5496a606cced00ca7ccde0df36db Mon Sep 17 00:00:00 2001 From: Ulysse Cura Date: Thu, 30 Jul 2026 17:59:14 +0200 Subject: [PATCH] Added a reserve function to linked lists. This came along an allocator function and better memory management. Elements and error handling has exploded everywere, that's why there is so much changes. --- src/asset_manager.c | 104 +++++++---- src/ecs.c | 133 ++++++++------ src/headers/asset_manager.h | 6 +- src/headers/ecs.h | 24 +-- src/headers/linked_list.h | 249 ++++++++++++-------------- src/linked_list.c | 342 +++++++++++++++--------------------- 6 files changed, 413 insertions(+), 445 deletions(-) diff --git a/src/asset_manager.c b/src/asset_manager.c index 96dcaea..48385e3 100644 --- a/src/asset_manager.c +++ b/src/asset_manager.c @@ -8,22 +8,21 @@ #include "display.h" #include "errors.h" -asset_t *create_asset(asset_type_t asset_type) +int create_asset(asset_t *asset, va_list args) { - asset_t *asset = malloc(sizeof(asset_t)); if(!asset) { error_printf("Failed to allocate memory : %d", errno); - return NULL; + return EXIT_FAILURE; } - asset->type = asset_type; + asset->type = va_arg(args, asset_type_t); asset->nb_refs = 0; - return asset; + return EXIT_SUCCESS; } -void destroy_asset(asset_t *asset) +int destroy_asset(asset_t *asset) { free(asset->name); @@ -38,21 +37,22 @@ void destroy_asset(asset_t *asset) break; } - free(asset); + return EXIT_SUCCESS; } -void destroy_asset_header_def(asset_header_def_t *asset_header_def) +int destroy_asset_header_def(asset_header_def_t *asset_header_def) { free(asset_header_def->name); - free(asset_header_def); + + return EXIT_SUCCESS; } int asset_manager_init(asset_manager_t *asset_manager) { assert("Asset manager cannot be NULL" && asset_manager); - linked_list_init(&asset_manager->assets, sizeof(asset_t), (deleter_t)destroy_asset); - linked_list_init(&asset_manager->assets_header_defs, sizeof(asset_header_def_t), (deleter_t)destroy_asset_header_def); + linked_list_init(&asset_manager->assets, sizeof(asset_t), (allocator_t)create_asset, (deleter_t)destroy_asset); + linked_list_init(&asset_manager->assets_header_defs, sizeof(asset_header_def_t), NULL, (deleter_t)destroy_asset_header_def); asset_manager->asset_file = fopen(ASSET_FILE_NAME, "rb"); if(!asset_manager->asset_file) @@ -69,28 +69,34 @@ int asset_manager_init(asset_manager_t *asset_manager) return EXIT_FAILURE; } + if(linked_list_reserve(&asset_manager->assets_header_defs, nb_assets + 1)) return EXIT_FAILURE; + for(size_t i = 0; i < nb_assets; i++) { - elem_t *elem = elem_create(&asset_manager->assets_header_defs); - if(!elem) return EXIT_FAILURE; + elem_t *asset_header_def_elem = create_elem(&asset_manager->assets_header_defs); + if(!asset_header_def_elem) return EXIT_FAILURE; + asset_header_def_t *asset_header_def = asset_header_def_elem->data; size_t cap = 0; - ret = getdelim(&((asset_header_def_t *)elem->data)->name, &cap, '\0', asset_manager->asset_file); + char *asset_name; + ret = getdelim(&asset_name, &cap, '\0', asset_manager->asset_file); if(ret == SIZE_MAX) { error_printf("Failed to read asset file."); return EXIT_FAILURE; } + asset_header_def->name = calloc(strlen(asset_name) + 1, sizeof(char)); + strcpy(asset_header_def->name, asset_name); - ret = fread(&((asset_header_def_t *)elem->data)->start, sizeof(((asset_header_def_t *)elem->data)->start), 1, asset_manager->asset_file); + ret = fread(&asset_header_def->start, sizeof(asset_header_def->start), 1, asset_manager->asset_file); if(ret != 1) { error_printf("Failed to read asset file."); return EXIT_FAILURE; } - if(linked_list_push_back_elem(&asset_manager->assets_header_defs, elem)) return EXIT_FAILURE; + if(linked_list_push_back(&asset_manager->assets_header_defs, asset_header_def_elem)) return EXIT_FAILURE; } debug_printf("Initialized asset manager."); @@ -101,8 +107,8 @@ int asset_manager_exit(asset_manager_t *asset_manager) { assert("Asset manager cannot be NULL" && asset_manager); - linked_list_clear(&asset_manager->assets_header_defs); - linked_list_clear(&asset_manager->assets); + if(linked_list_clear(&asset_manager->assets_header_defs)) return EXIT_FAILURE; + if(linked_list_clear(&asset_manager->assets)) return EXIT_FAILURE; if(fclose(asset_manager->asset_file)) { @@ -125,7 +131,9 @@ inline int asset_manager_collect_garbage(asset_manager_t *asset_manager) { assert("Asset manager cannot be NULL" && asset_manager); - return linked_list_remove_if(&asset_manager->assets, condition_is_asset_unused); + if(linked_list_remove_if(&asset_manager->assets, condition_is_asset_unused)) return EXIT_FAILURE; + + return EXIT_SUCCESS; } // Only for this file @@ -210,21 +218,31 @@ int asset_manager_load_asset(asset_manager_t *asset_manager, const char *asset_n assert("Asset manager cannot be NULL" && asset_manager); // Check if asset has alread been loaded. - asset_t *asset = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); - if(!asset) + elem_t *asset_elem = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); + if(asset_elem) + { + ((asset_t *)asset_elem->data)->nb_refs++; + + debug_printf("Asset \"%s\" has already been loaded, nb refs : %lu", ((asset_t *)asset_elem->data)->name, ((asset_t *)asset_elem->data)->nb_refs); + + return EXIT_SUCCESS; + } + else { // If it didn't, load it - asset_header_def_t *asset_header_def = linked_list_get_if(&asset_manager->assets_header_defs, condition_is_asset_header_def_name, asset_name); - if(!asset_header_def) + elem_t *asset_header_def_elem = linked_list_get_if(&asset_manager->assets_header_defs, condition_is_asset_header_def_name, asset_name); + if(!asset_header_def_elem) { error_printf("Asset doesn't exist in asset file : %s", asset_name); return EXIT_FAILURE; } + asset_header_def_t *asset_header_def = asset_header_def_elem->data; - asset = create_asset(asset_type); - if(!asset) return EXIT_FAILURE; + asset_elem = create_elem(&asset_manager->assets, asset_type); + if(!asset_elem) return EXIT_FAILURE; + asset_t *asset = asset_elem->data; - asset->name = malloc(strlen(asset_name) + 1); + asset->name = calloc(strlen(asset_name) + 1, sizeof(char)); if(!asset->name) { error_printf("Failed to allocate memory for asset name : %d", errno); @@ -243,46 +261,56 @@ int asset_manager_load_asset(asset_manager_t *asset_manager, const char *asset_n default: error_printf("Asset type doesn't exists : %d", asset_type); destroy_asset(asset); + return EXIT_FAILURE; } - linked_list_push_back(&asset_manager->assets, asset); - } + asset->nb_refs++; - asset->nb_refs++; - debug_printf("Asset \"%s\" has been loaded, nb refs : %lu", asset->name, asset->nb_refs); - return EXIT_SUCCESS; + linked_list_push_back(&asset_manager->assets, asset_elem); + + debug_printf("Asset \"%s\" has been loaded, nb refs : %lu", asset->name, asset->nb_refs); + + return EXIT_SUCCESS; + } } int asset_manager_let_go_asset(asset_manager_t *asset_manager, const char *asset_name) { assert("Asset manager cannot be NULL" && asset_manager); - asset_t *asset = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); - if(!asset) + elem_t *asset_elem = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); + if(!asset_elem) { error_printf("Asset doesn't exists : %s", asset_name); + return EXIT_FAILURE; } + asset_t *asset = asset_elem->data; + asset->nb_refs--; + debug_printf("Asset \"%s\" has been let go, nb refs : %lu", asset->name, asset->nb_refs); + return EXIT_SUCCESS; } -texture_t *asset_manager_get_texture(asset_manager_t *asset_manager, const char *asset_name) +texture_t *asset_manager_get_texture(asset_manager_t *asset_manager, const char *texture_name) { assert("Asset manager cannot be NULL" && asset_manager); - asset_t *asset = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, asset_name); - if(!asset) + elem_t *asset_elem = linked_list_get_if(&asset_manager->assets, condition_is_asset_name, texture_name); + if(!asset_elem) { - error_printf("Asset \"%s\" hasn't been loaded yet.", asset_name); + error_printf("Asset \"%s\" hasn't been loaded yet.", texture_name); return NULL; } + asset_t *asset = asset_elem->data; + if(asset->type != ASSET_TEXTURE) { - error_printf("Asset \"%s\" isn't of type ASSET_TEXTURE.", asset_name); + error_printf("Asset \"%s\" isn't of type ASSET_TEXTURE.", texture_name); return NULL; } diff --git a/src/ecs.c b/src/ecs.c index 4607dc0..dc867a1 100644 --- a/src/ecs.c +++ b/src/ecs.c @@ -4,18 +4,17 @@ #include "components.h" #include "errors.h" -component_t *create_component(component_type_t component_type) +int create_component(component_t *component, va_list args) { - component_t *component = malloc(sizeof(component_t)); if(!component) { - error_printf("Failed to allocate memory : %d", errno); - return NULL; + error_printf("Component cannot be NULL"); + return EXIT_FAILURE; } - component->component_type = component_type; + component->component_type = va_arg(args, component_type_t); - switch(component_type) + switch(component->component_type) { #define COMPONENT(NAME, PREFIX, DRAW) \ case NAME: \ @@ -25,61 +24,76 @@ component_t *create_component(component_type_t component_type) component->component_data = malloc(sizeof(PREFIX##_data_t)); \ if(!component->component_data) \ { \ - free(component); \ error_printf("Failed to allocate memory : %d", errno); \ - return NULL; \ + return EXIT_FAILURE; \ } \ component->component_deleter = PREFIX##_destroy; \ break; #include "components.def" default: - free(component); - error_printf("Component type doesn't exists : %d", component_type); - return NULL; + error_printf("Component type doesn't exists : %d", component->component_type); + return EXIT_FAILURE; } - return component; + return EXIT_SUCCESS; } -inline void destroy_component(component_t *component) +inline int destroy_component(component_t *component) { - component->component_deleter(component); - free(component); + if(!component) + { + error_printf("Component cannot be NULL"); + return EXIT_FAILURE; + } + + if(component->component_deleter(component)) return EXIT_FAILURE; + + return EXIT_SUCCESS; } -entity_t *create_entity(const unsigned int id) +int create_entity(entity_t *entity, va_list args) { - entity_t *entity = malloc(sizeof(entity_t)); if(!entity) { - error_printf("Failed to allocate memory : %d", errno); - return NULL; + error_printf("Entity cannot be NULL"); + return EXIT_FAILURE; } - entity->id = id; + entity->id = va_arg(args, size_t); entity->draw_priority = 0; - linked_list_init(&entity->components, sizeof(component_t), (deleter_t)destroy_component); + linked_list_init(&entity->components, sizeof(component_t), (allocator_t)create_component, (deleter_t)destroy_component); - return entity; + return EXIT_SUCCESS; } -void add_component(entity_t *entity, component_t *component, ...) +inline int destroy_entity(entity_t *entity) { + if(!entity) + { + error_printf("Entity cannot be NULL"); + return EXIT_FAILURE; + } + + if(linked_list_clear(&entity->components)) return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +component_t *entity_new_component(entity_t *entity, component_type_t component_type) +{ + elem_t *component_elem = create_elem(&entity->components, component_type); + if(!component_elem) return NULL; + component_t *component = component_elem->data; + component->entity = entity; - va_list args, args_copy; + if(component->component_init(component)) return NULL; - va_start(args, component); - va_copy(args_copy, args); + if(linked_list_push_front(&entity->components, component_elem)) return NULL; - component->component_init(component, args_copy); - - va_end(args_copy); - va_end(args); - - linked_list_push_back(&entity->components, component); + return component; } // Only for this file @@ -89,12 +103,13 @@ static inline bool condition_is_component_type(elem_t *elem, va_list args) return ((component_t *)elem->data)->component_type == va_arg(args, component_type_t); } -inline component_t *get_component(entity_t *entity, component_type_t component_type) +inline component_t *entity_get_component(entity_t *entity, component_type_t component_type) { - component_t *component = linked_list_get_if(&entity->components, condition_is_component_type, component_type); + elem_t *elem = linked_list_get_if(&entity->components, condition_is_component_type, component_type); + component_t *component = elem->data; if(!component) { - error_printf("Entity with id:%d doesn't have componant of type : %d", entity->id, component_type); + error_printf("Entity with id:%lu doesn't have componant of type : %d", entity->id, component_type); return NULL; } @@ -106,14 +121,16 @@ inline component_t *get_component(entity_t *entity, component_type_t component_t static inline int action_update_component(elem_t *elem, va_list args __attribute__((unused))) { component_t *component = elem->data; - component->component_update(component); + if(component->component_update(component)) return EXIT_FAILURE; return EXIT_SUCCESS; } inline int update_entity(entity_t *entity) { - return linked_list_for_each(&entity->components, action_update_component); + if(linked_list_for_each(&entity->components, action_update_component)) return EXIT_FAILURE; + + return EXIT_SUCCESS; } // Only for this file @@ -121,39 +138,39 @@ inline int update_entity(entity_t *entity) static inline int action_draw_component(elem_t *elem, va_list args __attribute__((unused))) { component_t *component = elem->data; - if(component->component_draw) component->component_draw(component); + if(component->component_draw) if(component->component_draw(component)) return EXIT_FAILURE; + return EXIT_SUCCESS; } inline int draw_entity(entity_t *entity) { - return linked_list_for_each(&entity->components, action_draw_component); -} + if(linked_list_for_each(&entity->components, action_draw_component)) return EXIT_FAILURE; -inline void destroy_entity(entity_t *entity) -{ - linked_list_clear(&entity->components); - free(entity); + return EXIT_SUCCESS; } inline void entity_manager_init(entity_manager_t *entity_manager) { assert("Entity manager cannot be NULL" && entity_manager); - linked_list_init(&entity_manager->entities, sizeof(entity_t), (deleter_t)destroy_entity); + linked_list_init(&entity_manager->entities, sizeof(entity_t), (allocator_t)create_entity, (deleter_t)destroy_entity); debug_printf("Initialized entity manager."); } -inline int entity_manager_add_entity(entity_manager_t *entity_manager, entity_t *entity) +inline entity_t *entity_manager_new_entity(entity_manager_t *entity_manager, size_t id) { assert("Entity manager cannot be NULL" && entity_manager); - if(linked_list_push_back(&entity_manager->entities, entity)) return EXIT_FAILURE; + elem_t *entity_elem = create_elem(&entity_manager->entities, id); + if(!entity_elem) return NULL; - debug_printf("Added entity %d to entity manager.", entity->id); + if(linked_list_push_back(&entity_manager->entities, entity_elem)) return NULL; - return EXIT_SUCCESS; + debug_printf("Added entity %lu to entity manager.", id); + + return entity_elem->data; } // Only for this file @@ -167,7 +184,9 @@ inline int entity_manager_update(entity_manager_t *entity_manager) { assert("Entity manager cannot be NULL" && entity_manager); - return linked_list_for_each(&entity_manager->entities, action_update_entity); + if(linked_list_for_each(&entity_manager->entities, action_update_entity)) return EXIT_FAILURE; + + return EXIT_SUCCESS; } // Only for this file @@ -181,7 +200,9 @@ inline int entity_manager_draw(entity_manager_t *entity_manager) { assert("Entity manager cannot be NULL" && entity_manager); - return linked_list_for_each(&entity_manager->entities, action_draw_entity); + if(linked_list_for_each(&entity_manager->entities, action_draw_entity)) return EXIT_FAILURE; + + return EXIT_SUCCESS; } // Only for this file @@ -195,14 +216,18 @@ inline int entity_manager_remove_entity(entity_manager_t *entity_manager, unsign { assert("Entity manager cannot be NULL" && entity_manager); - return linked_list_remove_if(&entity_manager->entities, condition_is_entity_name, id); + if(linked_list_remove_if(&entity_manager->entities, condition_is_entity_name, id)) return EXIT_FAILURE; + + return EXIT_SUCCESS; } -inline void entity_manager_exit(entity_manager_t *entity_manager) +inline int entity_manager_exit(entity_manager_t *entity_manager) { assert("Entity manager cannot be NULL" && entity_manager); - linked_list_clear(&entity_manager->entities); + if(linked_list_clear(&entity_manager->entities)) return EXIT_FAILURE; debug_printf("Exited entity manager."); + + return EXIT_SUCCESS; } diff --git a/src/headers/asset_manager.h b/src/headers/asset_manager.h index e28384c..6577dae 100644 --- a/src/headers/asset_manager.h +++ b/src/headers/asset_manager.h @@ -25,16 +25,16 @@ typedef struct asset_t { asset_type_t type; } asset_t; -asset_t *create_asset(asset_type_t asset_type); +int create_asset(asset_t *asset, va_list args); -void destroy_asset(asset_t *asset); +int destroy_asset(asset_t *asset); typedef struct asset_header_def_t { char *name; size_t start; } asset_header_def_t; -void destroy_asset_header_def(asset_header_def_t *asset_header_def); +int destroy_asset_header_def(asset_header_def_t *asset_header_def); typedef struct asset_manager_t { linked_list_t assets_header_defs; diff --git a/src/headers/ecs.h b/src/headers/ecs.h index 202d0d4..0c8319e 100644 --- a/src/headers/ecs.h +++ b/src/headers/ecs.h @@ -14,13 +14,13 @@ typedef enum component_type_t { typedef struct component_t component_t; // Component init function. It is called when component added to entity. This function is necessary. -typedef int (*component_init_t)(component_t *component, va_list args); +typedef int (*component_init_t)(component_t *component); // Component update function. It is called in the main loop to update the component. This function is necessary. typedef int (*component_update_t)(component_t *component); // Component draw function. It is called in the main loop to draw the component on screen. This function is not necessary. typedef int (*component_draw_t)(component_t *component); // Component deleter function. It is called when the component is removed. It should free the data in the component data but not the component itself. -typedef void (*component_deleter_t)(component_t *component); +typedef int (*component_deleter_t)(component_t *component); typedef struct component_t { component_init_t component_init; @@ -37,35 +37,35 @@ typedef struct component_t { } component_t; -component_t *create_component(component_type_t component_type); +int create_component(component_t *component, va_list args); -void destroy_component(component_t *component); +int destroy_component(component_t *component); typedef struct entity_t { - unsigned int id; + size_t id; size_t draw_priority; linked_list_t components; } entity_t; -entity_t *create_entity(const unsigned int id); +int create_entity(entity_t *entity, va_list args); -void add_component(entity_t *entity, component_t *component, ...); +int destroy_entity(entity_t *entity); -component_t *get_component(entity_t *entity, component_type_t component_type); +component_t *entity_new_component(entity_t *entity, component_type_t component_type); + +component_t *entity_get_component(entity_t *entity, component_type_t component_type); int update_entity(entity_t *entity); int draw_entity(entity_t *entity); -void destroy_entity(entity_t *entity); - typedef struct entity_manager_t { linked_list_t entities; } entity_manager_t; void entity_manager_init(entity_manager_t *entity_manager); -int entity_manager_add_entity(entity_manager_t *entity_manager, entity_t *entity); +entity_t *entity_manager_new_entity(entity_manager_t *entity_manager, size_t id); int entity_manager_update(entity_manager_t *entity_manager); @@ -73,6 +73,6 @@ int entity_manager_draw(entity_manager_t *entity_manager); int entity_manager_remove_entity(entity_manager_t *entity_manager, const unsigned int id); -void entity_manager_exit(entity_manager_t *entity_manager); +int entity_manager_exit(entity_manager_t *entity_manager); #endif // ECS_H \ No newline at end of file diff --git a/src/headers/linked_list.h b/src/headers/linked_list.h index c3b6127..82f8524 100644 --- a/src/headers/linked_list.h +++ b/src/headers/linked_list.h @@ -21,194 +21,177 @@ typedef struct elem_t { } elem_t; /** - * @brief A type for deleters used during the destruction of data stored in elements. + * @brief A type for allocators used during the creation of data stored in elements' data. + * + * During the initialisation of linked lists you can pass in argument an allocator. + * You can pass NULL to the initialisation to not use an allocator. + * Your allocator_t function declaration must look like this : + * + * int name_of_your_allocator(void* data, va_list args); + * + * Then do what you need to fill your data. + * + * @param data Data to fill. + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. +*/ +typedef int (*allocator_t)(void *data, va_list args); + +/** + * @brief A type for deleters used during the destruction of data stored in elements' data. * * During the initialisation of linked lists you can pass in argument a deleter. - * It is used during the destruction of an element. - * You can pass NULL to the initialisation to use the default deleter (free(elem->data)). + * You can pass NULL to the initialisation to not use a deleter. * Your deleter_t function declaration must look like this : * * void name_of_your_deleter(void *data); * - * Then do what you need to destroy your data (don't forget to free the data itself...) but not the elements. - * You should always define your function with "inline" if it's no too long. + * Then do what you need to destroy your data (don't free the data itself). + * + * @param data Data to free. */ -typedef void (*deleter_t)(void *data); +typedef int (*deleter_t)(void *data); /** - * @brief A type for condition used for function like linked_list_remove_if + * @brief A type for condition used for function like linked_list_remove_if. * - * For function like linked_list_remove_if you need a condition. - * It have two argument which are "elem_t *" and "va_list", that is because it passes the element on the actual index - * and the args passed to the function. * Your condition_t function declaration must look like this : * * bool name_of_your_condition(elem_t *elem, va_list); * - * Then return true if you want the element to be removed from the list or false if not. - * You should always define your function with "static", and "inline". + * @param elem Processed element + * @param args Arguments to pass to the function + * + * @return False, true for the element to be removed. */ typedef bool (*condition_t)(elem_t *elem, va_list args); /** * @brief A type for action used for function like linked_list_for_each * - * For function like linked_list_for_each you need an action to do. - * It have two argument which are "elem_t *" and "va_list", that is because it passes the element on the actual index - * and the args passed to the function. - * You can use the return value to indicate an error with EXIT_SUCCESS or EXIT_FAILURE. * Your condition_t function declaration must look like this : * * int name_of_your_condition(elem_t *elem, va_list); * - * Then do whatever you want with the element data, just don't free it. - * You should always define your function with "static", and "inline". + * @param elem Processed element + * @param args Arguments to pass to the function + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ typedef int (*action_t)(elem_t *elem, va_list args); /** * @brief Base of linked lists. - * This struct is the base for creating linked lists. * - * @param data_size Size of the data stored in elements of the linked list (used while creating new elements) - * @param first Pointer to the first element in the linked list - * @param last Pointer to the last element in the linked list - * @param size Size of the linked list - * @param deleter Deleter of the data when removing an element + * @param data_size Size of the data stored in elements + * @param allocator Allocator of data when creating new elements + * @param deleter Deleter of data when removing elements + * @param first Pointer to the first element in the list + * @param last Pointer to the last element in the list + * @param size Size of the linked list + * @param reserved Pointer to the first reserved element + * @param target_max_size Total target number of elements in the list */ typedef struct linked_list_t { size_t data_size; + allocator_t allocator; + deleter_t deleter; elem_t *first; elem_t *last; size_t size; - deleter_t elem_deleter; + elem_t *reserved; + size_t target_max_size; } linked_list_t; /** - * @brief Init a linked list - * This function is necessary if you create a linked list. + * @brief Init a linked list. * - * WARNING : It doesn't allocate the memory for you ! - * - * @param linked_list Pointer to an linked list - * @param data_size Data size for elements in the linked list - * @param deleter The deleter used during destroyement of an elem + * @param linked_list Pointer to a linked list + * @param data_size Data size for elements data in the linked list + * @param deleter The deleter for elem */ -void linked_list_init(linked_list_t *linked_list, const size_t data_size, deleter_t deleter); +void linked_list_init(linked_list_t *linked_list, size_t data_size, const allocator_t allocator, const deleter_t deleter); /** - * @brief Create an elem to fill with data to insert in a linked list. - * This function is usefull when you want to initialise an elem with the proper data_size strored in the linked list. - * - * WARNING : Don't change the "data" ptr after calling this function or it will lead to memory leaks ! - * Instead do something like this : *(int *)elem->data = 42; + * @brief Pre-allocate elements for a target size. + * If you want n more elements in your list, then you want linked_list.size + n target_size. + * + * @param linked_list Pointer to a linked list + * @param target_size Target size of linked list + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. + */ +int linked_list_reserve(linked_list_t *linked_list, size_t target_size); + +/** + * @brief Create an elem to insert in a linked list. The data pointer is allocated. + * If there is an element available in reserved list then it will be used. * * @param linked_list Pointer to an linked list + * @param ... Arguments to pass to the allocator * - * @return Address of initialised elem, NULL on error. + * @return pointer to initialised elem, NULL on error. */ -elem_t *elem_create(const linked_list_t *linked_list); +elem_t *create_elem(linked_list_t *linked_list, ...); /** * @brief Destroy the given element and its data. - * This function is usefull when you want to free an element. - * The deleter is used for the data only and you can pass NULL to use the default deleter. + * May move the element into the reserved list for future use depending on target size of the list. * + * @param linked_list Pointer to a linked list * @param elem Pointer to an element - * @param elem_deleter Function to use to free the data + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ -void destroy_elem(elem_t *elem, const deleter_t elem_deleter); +int destroy_elem(linked_list_t *linked_list, elem_t *elem); /** * @brief Insert an element from the back in a linked list, do nothing on error. * * @param linked_list Pointer to a linked list * @param elem Pointer to an element + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ -int linked_list_push_back_elem(linked_list_t *linked_list, elem_t *elem); +int linked_list_push_back(linked_list_t *linked_list, elem_t *elem); /** * @brief Insert an element from the front in a linked list, do nothing on error. * * @param linked_list Pointer to a linked list * @param elem Pointer to an element + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ -int linked_list_push_front_elem(linked_list_t *linked_list, elem_t *elem); +int linked_list_push_front(linked_list_t *linked_list, elem_t *elem); /** - * @brief Insert an element initialised and filled with data from the back in a linked list, do nothing on error. + * @brief Remove and return the last element in the given linked list, do nothing on error. * * @param linked_list Pointer to a linked list - * @param data Pointer to any data + * + * @return Pointer to element, NULL on error. */ -int linked_list_push_back(linked_list_t *linked_list, void *data); +elem_t *linked_list_pop_back(linked_list_t *linked_list); /** - * @brief Insert an element filled with data from the front in a linked list. + * @brief Remove and return the first element in the given linked list, do nothing on error. * * @param linked_list Pointer to a linked list - * @param data Pointer to any data -*/ -int linked_list_push_front(linked_list_t *linked_list, void *data); - -/** - * @brief Delete the last element in the given linked list, do nothing on error. * - * @param linked_list Pointer to a linked list + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ -int linked_list_pop_back(linked_list_t *linked_list); - -/** - * @brief Delete the first element in the given linked list, do nothing on error. - * - * @param linked_list Pointer to a linked list -*/ -int linked_list_pop_front(linked_list_t *linked_list); +elem_t *linked_list_pop_front(linked_list_t *linked_list); /** * @brief Clear the given linked list, do nothing on error. + * May move the elements into the reserved list for future use depending on target size of the list. * * @brief linked_list Pointer to a linked list + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ -void linked_list_clear(linked_list_t *linked_list); - -/** - * @brief Check if the given linked list is empty. - * - * @param linked_list Pointer to a linked list - * - * @return True if the given linked list is empty else false. -*/ -bool linked_list_is_empty(const linked_list_t *linked_list); - -/** - * @brief Check if the given index is whithin the range of the linked list. - * - * @param linked_list Pointer to an linked_list_t - * - * @return True if index in within the range of the linked list else false. -*/ -bool linked_list_is_in_bound(const linked_list_t *linked_list, const size_t index); - -/** - * @brief Get element at the given index in the linked list. - * - * @param linked_list Pointer to a linked list - * @param index Index of the element - * - * @return Element, NULL on error. -*/ -elem_t *linked_list_get_elem(const linked_list_t *linked_list, const size_t index); - -/** - * @brief Get data in the element at the given index in the linked list. - * - * @param linked_list Pointer to an linked list - * @param index Index of the wanted element - * - * @return Data, NULL on error. -*/ -void *linked_list_get(const linked_list_t *linked_list, const size_t index); +int linked_list_clear(linked_list_t *linked_list); /** * @brief Insert element at the given index in the linked list, do nothing on error. @@ -216,55 +199,42 @@ void *linked_list_get(const linked_list_t *linked_list, const size_t index); * @param linked_list Pointer to a linked list * @param elem Pointer to an element * @param index Index to insert element -*/ -int linked_list_insert_elem(linked_list_t *linked_list, elem_t *elem, const size_t index); - -/** - * @brief Insert element with data at the given index in the linked list, do nothing on error. * - * @param linked_list Pointer to a linked list - * @param data Data to put in the new element - * @param index Index to insert the element + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ -int linked_list_insert(linked_list_t *linked_list, void *data, const size_t index); +int linked_list_insert(linked_list_t *linked_list, elem_t *elem, size_t index); /** - * @brief Remove the given elem in the linked list, do nothing on error. - * - * @param linked_list Pointer to a linked list - * @param elem Element to remove -*/ -int linked_list_remove_elem(linked_list_t *linked_list, elem_t *elem); - -/** - * @brief Remove element at the given index in the linked list, do nothing on error. + * @brief Get element at the given index in the linked list. * * @param linked_list Pointer to a linked list * @param index Index of the element + * + * @return Pointer to element, NULL on error. */ -int linked_list_remove(linked_list_t *linked_list, const size_t index); +elem_t *linked_list_get(const linked_list_t *linked_list, size_t index); /** - * @brief Return first element that the condition verify + * @brief Remove the given elem in the linked list, do nothing on error. + * May move the elements into the reserved list for future use depending on target size of the list. + * + * @param linked_list Pointer to a linked list + * @param elem Element to remove + * + * @return EXIT_SUCCESS, EXIT_FAILURE on error. +*/ +int linked_list_remove(linked_list_t *linked_list, elem_t *elem); + +/** + * @brief Return first element that the condition verify. * * @param linked_list Pointer to a linked list * @param condition Condition to verify * @param ... Argument to pass to the condition * - * @return Element, NULL if no element verify the condition or if an error ocurred. + * @return Pointer to element, NULL if no element verify the condition or if an error occurred. */ -elem_t *linked_list_get_elem_if(const linked_list_t *linked_list, const condition_t condition, ...); - -/** - * @brief Return data in the first element that the condition verify - * - * @param linked_list Pointer to a linked list - * @param condition Condition to verify - * @param ... Argument to pass to the condition - * - * @return Element data, NULL if no element verify the condition or if an error ocurred. -*/ -void *linked_list_get_if(const linked_list_t *linked_list, const condition_t condition, ...); +elem_t *linked_list_get_if(const linked_list_t *linked_list, const condition_t condition, ...); /** * @brief Remove every elements that the condition verify, do nothing on error. @@ -272,19 +242,20 @@ void *linked_list_get_if(const linked_list_t *linked_list, const condition_t con * @param linked_list Pointer to an linked list * @param condition Condition to verify * @param ... Argument to pass to the condition + * + * @return Pointer to element, NULL if no element verify the condition or if an error occurred. */ int linked_list_remove_if(linked_list_t *linked_list, const condition_t condition, ...); /** * @brief Apply an action to every elements in the list. - * Useful for changing a value in every single element data. * The action can be anything that doesn't destroy the element. * * @param linked_list Pointer to an linked list * @param action Action to apply * @param ... Argument to pass to the action * - * @return The success of the action if supported by the action. + * @return EXIT_SUCCESS, EXIT_FAILURE on error. */ int linked_list_for_each(const linked_list_t *linked_list, const action_t action, ...); diff --git a/src/linked_list.c b/src/linked_list.c index b5a539a..db8e170 100644 --- a/src/linked_list.c +++ b/src/linked_list.c @@ -3,54 +3,124 @@ #include "linked_list.h" #include "errors.h" -elem_t *elem_create(const linked_list_t *linked_list) +elem_t *create_elem(linked_list_t *linked_list, ...) { assert("Linked list cannot be NULL" && linked_list); elem_t *elem; - elem = malloc(sizeof(elem_t)); - if(!elem) + + if(linked_list->reserved) { - error_printf("Failed to allocate memory : %d", errno); - return NULL; + elem = linked_list->reserved; + linked_list->reserved = elem->next; + } + else + { + elem = malloc(sizeof(elem_t)); + if(!elem) + { + error_printf("Failed to allocate memory : %d", errno); + return NULL; + } + + elem->data = malloc(linked_list->data_size); + if(!elem->data) + { + error_printf("Failed to allocate memory : %d", errno); + free(elem); + return NULL; + } } - elem->data = malloc(linked_list->data_size); - if(!elem->data) + if(linked_list->allocator) { - error_printf("Failed to allocate memory : %d", errno); - free(elem); - return NULL; + va_list args, args_copy; + va_start(args, linked_list); + va_copy(args_copy, args); + + if(linked_list->allocator(elem->data, args_copy)) return NULL; + + va_end(args_copy); + va_end(args); } return elem; } -inline void destroy_elem(elem_t *elem, const deleter_t elem_deleter) -{ - if(!elem) return; // Not fatal - - if(elem_deleter) - elem_deleter(elem->data); - else - free(elem->data); - - free(elem); -} - -void linked_list_init(linked_list_t *linked_list, const size_t data_size, const deleter_t elem_deleter) +inline int destroy_elem(linked_list_t *linked_list, elem_t *elem) { assert("Linked list cannot be NULL" && linked_list); + if(!elem) + { + error_printf("Elem cannot be NULL."); + return EXIT_FAILURE; + } + + if(linked_list->deleter) + if(linked_list->deleter(elem->data)) return EXIT_FAILURE; + + if(linked_list->size < linked_list->target_max_size) + { + elem->next = linked_list->reserved; + linked_list->reserved = elem; + } + else + { + free(elem->data); + free(elem); + } + + return EXIT_SUCCESS; +} + +void linked_list_init(linked_list_t *linked_list, const size_t data_size, const allocator_t allocator, const deleter_t elem_deleter) +{ + assert("Linked list cannot be NULL" && linked_list); + + linked_list->data_size = data_size; + linked_list->allocator = allocator; + linked_list->deleter = elem_deleter; + linked_list->first = NULL; linked_list->last = NULL; linked_list->size = 0; - linked_list->data_size = data_size; - linked_list->elem_deleter = elem_deleter; + linked_list->reserved = NULL; + linked_list->target_max_size = 0; } -int linked_list_push_back_elem(linked_list_t *linked_list, elem_t *elem) +int linked_list_reserve(linked_list_t *linked_list, size_t target_max_size) +{ + assert("Linked list cannot be NULL" && linked_list); + + linked_list->target_max_size = target_max_size; + + for(size_t i = linked_list->size; i < target_max_size; i++) + { + elem_t *elem = malloc(sizeof(elem_t)); + if(!elem) + { + error_printf("Failed to allocate memory : %d", errno); + return EXIT_FAILURE; + } + + elem->data = malloc(linked_list->data_size); + if(!elem->data) + { + error_printf("Failed to allocate memory : %d", errno); + free(elem); + return EXIT_FAILURE; + } + + elem->next = linked_list->reserved; + linked_list->reserved = elem; + } + + return EXIT_SUCCESS; +} + +int linked_list_push_back(linked_list_t *linked_list, elem_t *elem) { assert("Linked list cannot be NULL" && linked_list); @@ -75,7 +145,7 @@ int linked_list_push_back_elem(linked_list_t *linked_list, elem_t *elem) return EXIT_SUCCESS; } -int linked_list_push_front_elem(linked_list_t *linked_list, elem_t *elem) +int linked_list_push_front(linked_list_t *linked_list, elem_t *elem) { assert("Linked list cannot be NULL" && linked_list); @@ -100,110 +170,74 @@ int linked_list_push_front_elem(linked_list_t *linked_list, elem_t *elem) return EXIT_SUCCESS; } -int linked_list_push_back(linked_list_t *linked_list, void *data) +elem_t *linked_list_pop_back(linked_list_t *linked_list) { assert("Linked list cannot be NULL" && linked_list); - elem_t *tmp = malloc(sizeof(elem_t)); - if(!tmp) - { - error_printf("Failed to allocate memory : %d", errno); - return EXIT_FAILURE; - } - tmp->data = data; - - if(linked_list_push_back_elem(linked_list, tmp)) return EXIT_FAILURE; - - return EXIT_SUCCESS; -} - -int linked_list_push_front(linked_list_t *linked_list, void *data) -{ - assert("Linked list cannot be NULL" && linked_list); - - elem_t *tmp = malloc(sizeof(elem_t)); - if(!tmp) - { - error_printf("Failed to allocate memory : %d", errno); - return EXIT_FAILURE; - } - tmp->data = data; - - if(linked_list_push_front_elem(linked_list, tmp)) return EXIT_FAILURE; - - return EXIT_SUCCESS; -} - -int linked_list_pop_back(linked_list_t *linked_list) -{ - assert("Linked list cannot be NULL" && linked_list); - - elem_t *tmp = linked_list->last; - if(!tmp) + elem_t *elem = linked_list->last; + if(!elem) { error_printf("No elem to pop back."); - return EXIT_FAILURE; + return NULL; } - linked_list->last = tmp->prev; + linked_list->last = elem->prev; if(linked_list->last) linked_list->last->next = NULL; else linked_list->first = NULL; - destroy_elem(tmp, linked_list->elem_deleter); - linked_list->size--; - return EXIT_SUCCESS; + return elem; } -int linked_list_pop_front(linked_list_t *linked_list) +elem_t *linked_list_pop_front(linked_list_t *linked_list) { assert("Linked list cannot be NULL" && linked_list); - elem_t *tmp = linked_list->first; - if(!tmp) + elem_t *elem = linked_list->first; + if(!elem) { error_printf("No elem to pop front."); - return EXIT_FAILURE; + return NULL; } - linked_list->first = tmp->next; + linked_list->first = elem->next; if(linked_list->first) linked_list->first->prev = NULL; else linked_list->last = NULL; - destroy_elem(tmp, linked_list->elem_deleter); - linked_list->size--; - return EXIT_SUCCESS; + return elem; } -void linked_list_clear(linked_list_t *linked_list) +int linked_list_clear(linked_list_t *linked_list) { assert("Linked list cannot be NULL" && linked_list); - elem_t *actual_elem = linked_list->first; + elem_t *current_elem = linked_list->first; elem_t *tmp; - while(actual_elem) + while(current_elem) { - tmp = actual_elem; + tmp = current_elem; - actual_elem = actual_elem->next; + current_elem = current_elem->next; - destroy_elem(tmp, linked_list->elem_deleter); + if(destroy_elem(linked_list, tmp)) return EXIT_FAILURE; } linked_list->first = NULL; linked_list->last = NULL; linked_list->size = 0; + + return EXIT_SUCCESS; } inline bool linked_list_is_empty(const linked_list_t *linked_list) @@ -213,18 +247,11 @@ inline bool linked_list_is_empty(const linked_list_t *linked_list) return !linked_list->first; } -inline bool linked_list_is_in_bound(const linked_list_t *linked_list, size_t index) +elem_t *linked_list_get(const linked_list_t *linked_list, size_t index) { assert("Linked list cannot be NULL" && linked_list); - return index < linked_list->size; -} - -elem_t *linked_list_get_elem(const linked_list_t *linked_list, size_t index) -{ - assert("Linked list cannot be NULL" && linked_list); - - if(!linked_list_is_in_bound(linked_list, index)) + if(index >= linked_list->size) { error_printf("List index out of range."); return NULL; @@ -235,31 +262,18 @@ elem_t *linked_list_get_elem(const linked_list_t *linked_list, size_t index) if(index == linked_list->size - 1) return linked_list->last; - elem_t *actual_elem = linked_list->first; - if(!actual_elem) return NULL; + elem_t *current_elem = linked_list->first; + if(!current_elem) return NULL; for (size_t i = 0; i < index; i++) { - actual_elem = actual_elem->next; + current_elem = current_elem->next; } - return actual_elem; + return current_elem; } -void *linked_list_get(const linked_list_t *linked_list, size_t index) -{ - assert("Linked list cannot be NULL" && linked_list); - - elem_t *tmp = linked_list_get_elem(linked_list, index); - if(!tmp) - { - error_printf("Failed to get elem."); - return NULL; - } - return tmp->data; -} - -int linked_list_insert_elem(linked_list_t *linked_list, elem_t *elem, size_t index) +int linked_list_insert(linked_list_t *linked_list, elem_t *elem, size_t index) // XXX TODO SIMPLIFY INSERT FUNC XXX // { assert("Linked list cannot be NULL" && linked_list); @@ -277,14 +291,14 @@ int linked_list_insert_elem(linked_list_t *linked_list, elem_t *elem, size_t ind if(index == 0) { - return linked_list_push_front_elem(linked_list, elem); + return linked_list_push_front(linked_list, elem); } else if(index == linked_list->size) { - return linked_list_push_back_elem(linked_list, elem); + return linked_list_push_back(linked_list, elem); } - elem_t *next_insert_elem = linked_list_get_elem(linked_list, index); + elem_t *next_insert_elem = linked_list_get(linked_list, index); if(!next_insert_elem) return EXIT_FAILURE; elem_t *prev_insert_elem = next_insert_elem->prev; @@ -299,22 +313,7 @@ int linked_list_insert_elem(linked_list_t *linked_list, elem_t *elem, size_t ind return EXIT_SUCCESS; } -int linked_list_insert(linked_list_t *linked_list, void *data, size_t index) -{ - assert("Linked list cannot be NULL" && linked_list); - - elem_t *tmp = malloc(sizeof(elem_t)); - if(!tmp) - { - error_printf("Failed to allocate memory : %d", errno); - return EXIT_FAILURE; - } - tmp->data = data; - - return linked_list_insert_elem(linked_list, tmp, index); -} - -int linked_list_remove_elem(linked_list_t *linked_list, elem_t *elem) +int linked_list_remove(linked_list_t *linked_list, elem_t *elem) { assert("Linked list cannot be NULL" && linked_list); @@ -324,39 +323,20 @@ int linked_list_remove_elem(linked_list_t *linked_list, elem_t *elem) return EXIT_FAILURE; } - if(elem == linked_list->first) - { - return linked_list_pop_front(linked_list); - } - else if(elem == linked_list->last) - { - return linked_list_pop_back(linked_list); - } - elem_t *prev_elem = elem->prev; elem_t *next_elem = elem->next; - prev_elem->next = next_elem; - next_elem->prev = prev_elem; + if(prev_elem) prev_elem->next = next_elem; + if(next_elem) next_elem->prev = prev_elem; - destroy_elem(elem, linked_list->elem_deleter); + if(destroy_elem(linked_list, elem)) return EXIT_FAILURE; linked_list->size--; return EXIT_SUCCESS; } -int linked_list_remove(linked_list_t *linked_list, size_t index) -{ - assert("Linked list cannot be NULL" && linked_list); - - elem_t *tmp = linked_list_get_elem(linked_list, index); - if(!tmp) return EXIT_FAILURE; - - return linked_list_remove_elem(linked_list, tmp); -} - -elem_t *linked_list_get_elem_if(const linked_list_t *linked_list, const condition_t condition, ...) +elem_t *linked_list_get_if(const linked_list_t *linked_list, const condition_t condition, ...) { assert("Linked list cannot be NULL" && linked_list); assert("Condition cannot be NULL" && condition); @@ -364,63 +344,27 @@ elem_t *linked_list_get_elem_if(const linked_list_t *linked_list, const conditio va_list args; va_start(args, condition); - elem_t *actual_elem = linked_list->first; + elem_t *current_elem = linked_list->first; elem_t *next_elem; - while(actual_elem) + while(current_elem) { - next_elem = actual_elem->next; + next_elem = current_elem->next; va_list args_copy; va_copy(args_copy, args); - if(condition(actual_elem, args_copy)) + if(condition(current_elem, args_copy)) { va_end(args_copy); va_end(args); - return actual_elem; + return current_elem; } va_end(args_copy); - actual_elem = next_elem; - } - - va_end(args); - - return NULL; -} - -void *linked_list_get_if(const linked_list_t *linked_list, const condition_t condition, ...) -{ - assert("Linked list cannot be NULL" && linked_list); - assert("Condition cannot be NULL" && condition); - - va_list args; - va_start(args, condition); - - elem_t *actual_elem = linked_list->first; - elem_t *next_elem; - - while(actual_elem) - { - next_elem = actual_elem->next; - - va_list args_copy; - va_copy(args_copy, args); - - if(condition(actual_elem, args_copy)) - { - va_end(args_copy); - va_end(args); - - return actual_elem->data; - } - - va_end(args_copy); - - actual_elem = next_elem; + current_elem = next_elem; } va_end(args); @@ -448,7 +392,7 @@ int linked_list_remove_if(linked_list_t *linked_list, const condition_t conditio if(condition(actual_elem, args_copy)) { - if(linked_list_remove_elem(linked_list, actual_elem)) + if(linked_list_remove(linked_list, actual_elem)) { va_end(args_copy); va_end(args); @@ -475,14 +419,14 @@ int linked_list_for_each(const linked_list_t *linked_list, const action_t action va_list args; va_start(args, action); - elem_t *actual_elem = linked_list->first; + elem_t *current_elem = linked_list->first; - while(actual_elem) + while(current_elem) { va_list args_copy; va_copy(args_copy, args); - if(action(actual_elem, args_copy)) + if(action(current_elem, args_copy)) { va_end(args_copy); va_end(args); @@ -492,7 +436,7 @@ int linked_list_for_each(const linked_list_t *linked_list, const action_t action va_end(args_copy); - actual_elem = actual_elem->next; + current_elem = current_elem->next; } va_end(args);