inc/c_util/tarray_ll.h (195 lines of code) (raw):

// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. #ifndef TARRAY_LL_H #define TARRAY_LL_H #ifdef __cplusplus #include <cinttypes> #else #include <inttypes.h> #endif #include "c_pal/interlocked.h" #include "c_pal/thandle_ll.h" #include "umock_c/umock_c_prod.h" /*TARRAY is backed by a THANDLE build on the structure below*/ #define TARRAY_STRUCT_TYPE_NAME_TAG(T) MU_C2(TARRAY_TYPEDEF_NAME(T), _TAG) #define TARRAY_TYPEDEF_NAME(T) MU_C2(TARRAY_STRUCT_, T) /*TARRAY_CLEANUP_FUNCTION_TYPE_NAME(T) introduces a new name for the type of the function that is called when the TARRAY(T) is about to be deallocated.*/ #define TARRAY_LL_CLEANUP_FUNCTION_TYPE_NAME(T) MU_C2(TARRAY_TYPEDEF_NAME(T), _CLEANUP) /*TARRAY_CLEANUP_FUNCTION_TYPE(T) introduces a new function pointer type for the cleanup function - the function to be called before the TARRAY's memory is free()'d*/ #define TARRAY_LL_CLEANUP_FUNCTION_TYPEDEF(T) typedef void(*TARRAY_LL_CLEANUP_FUNCTION_TYPE_NAME(T))(TARRAY_TYPEDEF_NAME(T)* tarray, void* cleanup_context); /*TARRAY_TYPEDEF_NAME(T) introduces the base type that holds an array of T*/ #define TARRAY_DEFINE_STRUCT_TYPE(T) \ /*forward define the typedef of the TARRAY struct so that it can be used for a function pointer definition*/ \ typedef struct TARRAY_STRUCT_TYPE_NAME_TAG(T) TARRAY_TYPEDEF_NAME(T); \ TARRAY_LL_CLEANUP_FUNCTION_TYPEDEF(T) \ struct TARRAY_STRUCT_TYPE_NAME_TAG(T) \ { \ uint32_t capacity; \ TARRAY_LL_CLEANUP_FUNCTION_TYPE_NAME(T) cleanup; \ void* cleanup_context; \ T* arr; \ }; \ /*TARRAY is-a THANDLE*/ /*given a type "T" TARRAY_LL(T) expands to the name of the type. */ #define TARRAY_LL(T) THANDLE(TARRAY_TYPEDEF_NAME(T)) /*because TARRAY is a THANDLE, all THANDLE's macro APIs are useable with TARRAY.*/ /*the below are just shortcuts of THANDLE's public ones*/ #define TARRAY_LL_INITIALIZE(T) THANDLE_INITIALIZE(TARRAY_TYPEDEF_NAME(T)) #define TARRAY_LL_ASSIGN(T) THANDLE_ASSIGN(TARRAY_TYPEDEF_NAME(T)) #define TARRAY_LL_MOVE(T) THANDLE_MOVE(TARRAY_TYPEDEF_NAME(T)) #define TARRAY_LL_INITIALIZE_MOVE(T) THANDLE_INITIALIZE_MOVE(TARRAY_TYPEDEF_NAME(T)) /*introduces a new name for a function that returns a TARRAY_LL(T)*/ #define TARRAY_LL_CREATE_NAME(C) MU_C2(TARRAY_LL_CREATE_, C) #define TARRAY_LL_CREATE(C) TARRAY_LL_CREATE_NAME(C) #define TARRAY_LL_CREATE_WITH_CAPACITY_NAME(C) MU_C2(TARRAY_LL_CREATE_WITH_CAPACITY_, C) #define TARRAY_LL_CREATE_WITH_CAPACITY(C) TARRAY_LL_CREATE_WITH_CAPACITY_NAME(C) /*introduces a name for a function that takes a capacity and a cleanup function pointer*/ #define TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_NAME(C) MU_C2(TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_, C) #define TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP(C) TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_NAME(C) /*introduces a name for a function that takes a capacity and a cleanup function pointer (internal)*/ #define TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL_NAME(C) MU_C2(TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL_, C) #define TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(C) TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL_NAME(C) /*introduces a function declaration for tarray_create*/ #define TARRAY_LL_CREATE_DECLARE(C, T) MOCKABLE_FUNCTION(, TARRAY_LL(T), TARRAY_LL_CREATE(C)); /*introduces a function declaration for tarray_create_with_capacity*/ #define TARRAY_LL_CREATE_WITH_CAPACITY_DECLARE(C, T) MOCKABLE_FUNCTION(, TARRAY_LL(T), TARRAY_LL_CREATE_WITH_CAPACITY(C), uint32_t, capacity); /*introduces a function declaration for tarray_create_with_capacity_and_cleanup*/ #define TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_DECLARE(C, T) MOCKABLE_FUNCTION(, TARRAY_LL(T), TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP(C), uint32_t, capacity, TARRAY_LL_CLEANUP_FUNCTION_TYPE_NAME(T), cleanup, void*, cleanup_context); /*introduces a name for the function that free's a TARRAY when it's ref count got to 0*/ #define TARRAY_LL_FREE_NAME(C) MU_C2(TARRAY_LL_FREE_, C) /*introduces a function definition for freeing the allocated resources for a TARRAY*/ #define TARRAY_LL_FREE_DEFINE(C, T) \ static void TARRAY_LL_FREE_NAME(C)(TARRAY_TYPEDEF_NAME(T)* tarray) \ { \ if (tarray == NULL) \ { \ LogError("invalid arguments " MU_TOSTRING(TARRAY_TYPEDEF_NAME(T)) "* tarray=%p", \ tarray); \ } \ else \ { \ /*Codes_SRS_TARRAY_02_014: [ Before freeing the memory used by TARRAY(T) cleanup shall be called if not NULL and cleanup_context shall be passed as cleanup's cleanup_context ]*/ \ if(tarray->cleanup!=NULL) \ { \ tarray->cleanup(tarray, tarray->cleanup_context); \ } \ free((void*)tarray->arr); \ } \ } \ /*introduces a function definition for tarray_create_with_capacity_and_cleanup*/ /*this is the JackOfAllTrades for TARRAY_CREATE*/ #define TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL_DEFINE(C, T) \ static TARRAY_LL(T) TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(C)(uint32_t capacity, TARRAY_LL_CLEANUP_FUNCTION_TYPE_NAME(T) cleanup, void* cleanup_context) \ { \ TARRAY_TYPEDEF_NAME(T)* result; \ if (capacity == 0) \ { \ /* Codes_SRS_TARRAY_01_001: [ If capacity is 0, TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T) shall fail and return NULL. ]*/ \ LogError("Invalid arguments: uint32_t capacity=%" PRIu32 "", capacity); \ result = NULL; \ } \ else \ { \ /* Codes_SRS_TARRAY_01_002: [ TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T) shall call THANDLE_MALLOC to allocate the result. ]*/ \ result = THANDLE_MALLOC(TARRAY_TYPEDEF_NAME(C))(TARRAY_LL_FREE_NAME(C)); \ if(result == NULL) \ { \ /* Codes_SRS_TARRAY_01_005: [ If there are any failures then TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T) shall fail and return NULL. ]*/ \ LogError("failure in " MU_TOSTRING(THANDLE_MALLOC) "(" MU_TOSTRING(TARRAY_TYPEDEF_NAME(T)) "=%zu)", sizeof(TARRAY_TYPEDEF_NAME(T))); \ /*return as is*/ \ } \ else \ { \ /* Codes_SRS_TARRAY_01_003: [ TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T) shall call malloc_2 to allocate capacity entries for result->arr. ]*/ \ result->arr = malloc_2(capacity,sizeof(T)); \ if(result->arr == NULL) \ { \ /* Codes_SRS_TARRAY_01_005: [ If there are any failures then TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T) shall fail and return NULL. ]*/ \ LogError("failure in malloc_2(capacity=%" PRIu32 ", sizeof(" MU_TOSTRING(T) ")=%zu)", \ capacity, sizeof(T)); \ } \ else \ { \ result->cleanup = cleanup; \ result->cleanup_context = cleanup_context; \ result->capacity = capacity; \ /* Codes_SRS_TARRAY_01_004: [ TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T) shall succeed and return a non-NULL value. ]*/ \ goto allok; \ } \ THANDLE_FREE(TARRAY_TYPEDEF_NAME(C))(result); \ result = NULL; \ } \ } \ allok:; \ return result; \ } /*introduces a function definition for tarray_create_with_capacity_and_cleanup*/ /*Codes_SRS_TARRAY_02_013: [ TARRAY_CREATE_WITH_CAPACITY_AND_CLEANUP(T) returns what TARRAY_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T)(capacity, cleanup, cleanup_context) returns. ]*/ #define TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_DEFINE(C, T) \ TARRAY_LL(T) TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP(C)(uint32_t capacity, TARRAY_LL_CLEANUP_FUNCTION_TYPE_NAME(T) cleanup, void* cleanup_context) \ { \ return TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(C)(capacity, cleanup, cleanup_context); \ } \ /*introduces a function definition for tarray_create_with_capacity*/ /*Codes_SRS_TARRAY_02_012: [ TARRAY_CREATE_WITH_CAPACITY(T) shall return what TARRAY_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T)(capacity, NULL, NULL) returns. ]*/ #define TARRAY_LL_CREATE_WITH_CAPACITY_DEFINE(C, T) \ TARRAY_LL(T) TARRAY_LL_CREATE_WITH_CAPACITY(C)(uint32_t capacity) \ { \ return TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(C)(capacity, NULL, NULL); \ } \ /*introduces a function definition for tarray_create*/ /*Codes_SRS_TARRAY_02_011: [ TARRAY_CREATE(T) shall return what TARRAY_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(T)(1, NULL, NULL) returns. ]*/ #define TARRAY_LL_CREATE_DEFINE(C, T) \ TARRAY_LL(T) TARRAY_LL_CREATE(C)(void) \ { \ return TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL(C)(1, NULL, NULL); \ } /*introduces a name for "what function to call to ensure capacity" */ #define TARRAY_LL_ENSURE_CAPACITY(C) MU_C2(TARRAY_ENSURE_CAPACITY_, C) /*introduces the declaration of the function that grows the capacity*/ #define TARRAY_LL_ENSURE_CAPACITY_DECLARE(C, T) \ MOCKABLE_FUNCTION(, int, TARRAY_LL_ENSURE_CAPACITY(C), TARRAY_LL(T), tarray, uint32_t, capacity); #define TARRAY_LL_ENSURE_CAPACITY_DEFINE(C, T) \ int TARRAY_LL_ENSURE_CAPACITY(C)(TARRAY_LL(T) tarray, uint32_t capacity) \ { \ int result; \ if (tarray == NULL) \ { \ /*Codes_SRS_TARRAY_02_005: [ If tarray is NULL then TARRAY_ENSURE_CAPACITY(T) shall fail and return a non-zero value. ]*/ \ LogError("invalid argument TARRAY(" MU_TOSTRING(T) ") tarray=%p, uint32_t capacity=%" PRIu32 "", \ tarray, capacity); \ result = MU_FAILURE; \ } \ else \ { \ /*Codes_SRS_TARRAY_02_006: [ If tarray->capacity is greater than or equal to capacity then TARRAY_ENSURE_CAPACITY(T) shall succeed and return 0. ]*/ \ if (capacity <= tarray->capacity) \ { \ /*we already have the capacity*/ \ result = 0; \ } \ else \ { \ /*Codes_SRS_TARRAY_02_010: [ If capacity is greater than 2147483648 then TARRAY_ENSURE_CAPACITY(T) shall fail and return a non-zero value. ]*/ \ if(capacity > (((uint32_t)1)<<31)) \ { \ /*Codes_SRS_TARRAY_02_009: [ If there are any failures then TARRAY_ENSURE_CAPACITY(T) shall fail and return a non-zero value. ]*/ \ LogError("capacity (%" PRIu32 ") cannot be improved by doubling", capacity); \ result = MU_FAILURE; \ } \ else \ { \ /*Codes_SRS_TARRAY_02_007: [ TARRAY_ENSURE_CAPACITY(T) shall shall call realloc_2 to resize arr to the next multiple of 2 greater than or equal to capacity. ]*/ \ uint32_t new_capacity = tarray->capacity; \ do{ \ new_capacity *= 2; \ }while(new_capacity<capacity); \ T* temp = realloc_2((void*)tarray->arr, new_capacity, sizeof(T)); \ if (temp == NULL) \ { \ /*Codes_SRS_TARRAY_02_009: [ If there are any failures then TARRAY_ENSURE_CAPACITY(T) shall fail and return a non-zero value. ]*/ \ LogError("failure in realloc_2(tarray->arr=%p, new_capacity=%" PRIu32 ", sizeof(T)=%zu)", \ tarray->arr, new_capacity, sizeof(T)); \ result = MU_FAILURE; \ } \ else \ { \ /*Codes_SRS_TARRAY_02_008: [ TARRAY_ENSURE_CAPACITY(T) shall succeed, record the new computed capacity, and return 0. ]*/ \ ((TARRAY_TYPEDEF_NAME(T)*)tarray)->capacity = new_capacity; \ ((TARRAY_TYPEDEF_NAME(T)*)tarray)->arr = temp; \ result = 0; \ } \ } \ } \ } \ return result; \ } /*macro to be used in headers*/ \ #define TARRAY_LL_TYPE_DECLARE(C, T) \ /*hint: have TARRAY_DEFINE_STRUCT_TYPE(T) before TARRAY_LL_TYPE_DECLARE*/ \ THANDLE_LL_TYPE_DECLARE(TARRAY_TYPEDEF_NAME(C), TARRAY_TYPEDEF_NAME(T)) \ TARRAY_LL_CREATE_DECLARE(C, T) \ TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_DECLARE(C, T) \ TARRAY_LL_CREATE_WITH_CAPACITY_DECLARE(C, T) \ TARRAY_LL_ENSURE_CAPACITY_DECLARE(C, T) \ #define TARRAY_LL_TYPE_DEFINE(C, T) \ /*hint: have THANDLE_TYPE_DEFINE(TARRAY_TYPEDEF_NAME(T)) before TARRAY_LL_TYPE_DEFINE*/ \ TARRAY_LL_FREE_DEFINE(C, T) \ TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_INTERNAL_DEFINE(C, T) \ TARRAY_LL_CREATE_DEFINE(C, T) \ TARRAY_LL_CREATE_WITH_CAPACITY_AND_CLEANUP_DEFINE(C, T) \ TARRAY_LL_CREATE_WITH_CAPACITY_DEFINE(C, T) \ TARRAY_LL_ENSURE_CAPACITY_DEFINE(C, T) \ #endif /*TARRAY_LL_H*/