inc/c_util/two_d_array_ll.h (234 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 TWO_D_ARRAY_LL_H #define TWO_D_ARRAY_LL_H #ifdef __cplusplus #include <cinttypes> #else // __cplusplus #include <inttypes.h> #endif // __cplusplus #include "c_pal/thandle_ll.h" #include "umock_c/umock_c_prod.h" /*TWO_D_ARRAY is backed by a THANDLE build on the structure below*/ #define TWO_D_ARRAY_STRUCT_TYPE_NAME_TAG(T) MU_C2(TWO_D_ARRAY_TYPEDEF_NAME(T), _TAG) #define TWO_D_ARRAY_TYPEDEF_NAME(T) MU_C2(TWO_D_ARRAY_STRUCT_, T) /*TWO_D_ARRAY_TYPEDEF_NAME(T) introduces the base type that holds an array of array of T*/ #define TWO_D_ARRAY_DEFINE_STRUCT_TYPE(T) \ /*forward define the typedef of the TWO_D_ARRAY struct so that it can be used for a function pointer definition*/ \ typedef struct TWO_D_ARRAY_STRUCT_TYPE_NAME_TAG(T) TWO_D_ARRAY_TYPEDEF_NAME(T); \ struct TWO_D_ARRAY_STRUCT_TYPE_NAME_TAG(T) \ { \ uint32_t rows; \ uint32_t cols; \ T* row_arrays[]; \ }; \ /*TWO_D_ARRAY is-a THANDLE*/ /*given a type "T" TWO_D_ARRAY_LL(T) expands to the name of the type. */ #define TWO_D_ARRAY_LL(T) THANDLE(TWO_D_ARRAY_TYPEDEF_NAME(T)) /*because TWO_D_ARRAY is a THANDLE, all THANDLE's macro APIs are useable with TWO_D_ARRAY.*/ /*the below are just shortcuts of THANDLE's public ones*/ #define TWO_D_ARRAY_LL_INITIALIZE(T) THANDLE_INITIALIZE(TWO_D_ARRAY_TYPEDEF_NAME(T)) #define TWO_D_ARRAY_LL_ASSIGN(T) THANDLE_ASSIGN(TWO_D_ARRAY_TYPEDEF_NAME(T)) #define TWO_D_ARRAY_LL_MOVE(T) THANDLE_MOVE(TWO_D_ARRAY_TYPEDEF_NAME(T)) #define TWO_D_ARRAY_LL_INITIALIZE_MOVE(T) THANDLE_INITIALIZE_MOVE(TWO_D_ARRAY_TYPEDEF_NAME(T)) /*introduces a new name for a function that returns a TWO_D_ARRAY_LL(T)*/ #define TWO_D_ARRAY_LL_CREATE_NAME(C) MU_C2(TWO_D_ARRAY_LL_CREATE_, C) #define TWO_D_ARRAY_LL_CREATE(C) TWO_D_ARRAY_LL_CREATE_NAME(C) /*introduces a name for a function that takes a two_d_array and a row index*/ #define TWO_D_ARRAY_LL_FREE_ROW_NAME(C) MU_C2(TWO_D_ARRAY_LL_FREE_ROW_, C) #define TWO_D_ARRAY_LL_FREE_ROW(C) TWO_D_ARRAY_LL_FREE_ROW_NAME(C) /*introduces a name for a function that takes a two_d_array and a row index*/ #define TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW_NAME(C) MU_C2(TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW_, C) #define TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW(C) TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW_NAME(C) /*introduces a name for a function that takes a two_d_array and a row index*/ #define TWO_D_ARRAY_LL_GET_ROW_NAME(C) MU_C2(TWO_D_ARRAY_LL_GET_ROW_, C) #define TWO_D_ARRAY_LL_GET_ROW(C) TWO_D_ARRAY_LL_GET_ROW_NAME(C) /*introduces a name for the function that free's a TWO_D_ARRAY when it's ref count got to 0*/ #define TWO_D_ARRAY_LL_FREE_NAME(C) MU_C2(TWO_D_ARRAY_LL_FREE_, C) /*introduces a function declaration for two_d_array_create*/ #define TWO_D_ARRAY_LL_CREATE_DECLARE(C, T) MOCKABLE_FUNCTION(, TWO_D_ARRAY_LL(T), TWO_D_ARRAY_LL_CREATE(C), uint32_t, row_size, uint32_t, col_size); /*introduces a function declaration for two_d_array_free_row*/ #define TWO_D_ARRAY_LL_FREE_ROW_DECLARE(C, T) MOCKABLE_FUNCTION(, int, TWO_D_ARRAY_LL_FREE_ROW(C), TWO_D_ARRAY_LL(T), two_d_array, uint32_t, row_index); /*introduces a function declaration for two_d_array_allocate_new_row*/ #define TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW_DECLARE(C, T) MOCKABLE_FUNCTION(, int, TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW(C), TWO_D_ARRAY_LL(T), two_d_array, uint32_t, row_index); /*introduces a function declaration for two_d_array_get_row*/ #define TWO_D_ARRAY_LL_GET_ROW_DECLARE(C, T) MOCKABLE_FUNCTION(, T*, TWO_D_ARRAY_LL_GET_ROW(C), TWO_D_ARRAY_LL(T), two_d_array, uint32_t, row_index); /*introduces a function definition for freeing the allocated resources for a TWO_D_ARRAY*/ #define TWO_D_ARRAY_LL_FREE_DEFINE(C, T) \ void TWO_D_ARRAY_LL_FREE_NAME(C)(TWO_D_ARRAY_TYPEDEF_NAME(T)* two_d_array) \ { \ /* Codes_SRS_TWO_D_ARRAY_07_007: [ If two_d_array is NULL, TWO_D_ARRAY_FREE(T) shall do nothing. ]*/ \ if (two_d_array == NULL) \ { \ LogError("invalid arguments " MU_TOSTRING(TWO_D_ARRAY_TYPEDEF_NAME(T)) "* two_d_array=%p", \ two_d_array); \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_009: [ TWO_D_ARRAY_FREE(T) shall free the memory associated with TWO_D_ARRAY(T). ]*/ \ for (uint32_t i = 0; i < two_d_array->rows; i++) \ { \ if (two_d_array->row_arrays[i] != NULL) \ { \ /* Codes_SRS_TWO_D_ARRAY_07_008: [ TWO_D_ARRAY_FREE(T) shall free all rows that are non-NULL. ]*/ \ free((void*)two_d_array->row_arrays[i]); \ } \ } \ } \ } #define TWO_D_ARRAY_LL_CREATE_DEFINE(C, T) \ TWO_D_ARRAY_LL(T) TWO_D_ARRAY_LL_CREATE(C)(uint32_t row_size, uint32_t col_size) \ { \ TWO_D_ARRAY_TYPEDEF_NAME(T)* result; \ /* Codes_SRS_TWO_D_ARRAY_07_001: [ If row_size equals to zero, TWO_D_ARRAY_CREATE(T) shall fail and return NULL. ]*/ \ if (row_size == 0 || \ /* Codes_SRS_TWO_D_ARRAY_07_002: [ If col_size equals to zero, TWO_D_ARRAY_CREATE(T) shall fail and return NULL. ]*/ \ col_size == 0) \ { \ LogError("Invalid arguments: uint32_t row_size=%" PRIu32 ", col_size=%" PRIu32, row_size, col_size); \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_003: [ TWO_D_ARRAY_CREATE(T) shall call THANDLE_MALLOC_FLEX with TWO_D_ARRAY_FREE(T) as dispose function, nmemb set to row_size and size set to sizeof(T*). ]*/\ result = THANDLE_MALLOC_FLEX(TWO_D_ARRAY_TYPEDEF_NAME(C))(TWO_D_ARRAY_LL_FREE_NAME(C), row_size, sizeof(T*)); \ /* Codes_SRS_TWO_D_ARRAY_07_005: [ If there are any errors then TWO_D_ARRAY_CREATE(T) shall fail and return NULL. ]*/ \ if (result == NULL) \ { \ LogError("failure in malloc_flex row_size=%" PRIu32 ", sizeof(" MU_TOSTRING(T) "*)=%zu);", row_size, sizeof(T*)); \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_006: [ TWO_D_ARRAY_CREATE(T) shall succeed and return a non-NULL value. ]*/ \ result->rows = row_size; \ result->cols = col_size; \ /* Codes_SRS_TWO_D_ARRAY_07_004: [ TWO_D_ARRAY_CREATE(T) shall set all rows pointers to NULL. ]*/ \ for (uint32_t i = 0; i < row_size; i++) \ { \ result->row_arrays[i] = NULL; \ } \ goto all_ok; \ } \ } \ result = NULL; \ all_ok: \ return result; \ } \ #define TWO_D_ARRAY_LL_FREE_ROW_DEFINE(C, T) \ int TWO_D_ARRAY_LL_FREE_ROW(C)(TWO_D_ARRAY_LL(T) two_d_array, uint32_t row_index) \ { \ int result; \ /* Codes_SRS_TWO_D_ARRAY_07_010: [ If two_d_array is NULL, TWO_D_ARRAY_FREE_ROW(T) shall fail return a non-zero value. ]*/ \ if (two_d_array == NULL) { \ LogError("Invalid arguments: TWO_D_ARRAY (" MU_TOSTRING(T) ") two_d_array=%p", two_d_array); \ result = MU_FAILURE; \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_011: [ If row_index is equal or greater than row_size, TWO_D_ARRAY_FREE_ROW(T) shall fail and return a non-zero value. ]*/ \ if (row_index >= two_d_array->rows) \ { \ LogError("Invalid arguments: uint32_t row_index=%" PRIu32 " out of bound, total_rows=%" PRIu32, row_index, two_d_array->rows); \ result = MU_FAILURE; \ } \ else \ { \ TWO_D_ARRAY_TYPEDEF_NAME(T)* array = THANDLE_GET_T(TWO_D_ARRAY_TYPEDEF_NAME(C))(two_d_array); \ if(array->row_arrays[row_index] == NULL) \ { \ LogError("Row not allocated yet: uint64_t row_index=%" PRIu32 ", total_rows=%" PRIu32, row_index, two_d_array->rows); \ result = MU_FAILURE; \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_012: [ TWO_D_ARRAY_FREE_ROW(T) shall free the memory associated with the row specified by row_index and set it to NULL. ]*/ \ free((void*)two_d_array->row_arrays[row_index]); \ array->row_arrays[row_index] = NULL; \ /* Codes_SRS_TWO_D_ARRAY_07_013: [ TWO_D_ARRAY_FREE_ROW(T) shall return zero on success. ]*/ \ result = 0; \ } \ } \ } \ return result; \ } \ #define TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW_DEFINE(C, T) \ int TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW(C)(TWO_D_ARRAY_LL(T) two_d_array, uint32_t row_index) \ { \ int result; \ /* Codes_SRS_TWO_D_ARRAY_07_014: [ If two_d_array is NULL, TWO_D_ARRAY_ALLOCATE_NEW_ROW(T) shall fail and return a non-zero value. ]*/ \ if (two_d_array == NULL) \ { \ LogError("Invalid arguments: TWO_D_ARRAY (" MU_TOSTRING(T) ") two_d_array=%p", two_d_array); \ result = MU_FAILURE; \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_015: [ If row_index is equal or greater than row_size, TWO_D_ARRAY_ALLOCATE_NEW_ROW(T) shall fail and return a non-zero value. ]*/ \ if (row_index >= two_d_array->rows) \ { \ LogError("Invalid arguments: uint32_t row_index=%" PRIu32 " out of bound, total_rows=%" PRIu32, row_index, two_d_array->rows); \ result = MU_FAILURE; \ } \ else \ { \ if (two_d_array->row_arrays[row_index] != NULL) \ { \ /* Codes_SRS_TWO_D_ARRAY_07_016: [ If the row specified by row_index has already been allocated, TWO_D_ARRAY_ALLOCATE_NEW_ROW(T) shall fail and return a non-zero value. ]*/ \ result = MU_FAILURE; \ LogError("Invalid arguments: uint32_t row_index=%" PRIu32 " has already been allocated", row_index); \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_017: [ Otherwise, TWO_D_ARRAY_ALLOCATE_NEW_ROW(T) shall allocate memory for the new row and return zero on success. ]*/ \ T* new_row = malloc_2(two_d_array->cols, sizeof(T)); \ if (new_row == NULL) \ { \ /* Codes_SRS_TWO_D_ARRAY_07_018: [ If there are any errors then TWO_D_ARRAY_ALLOCATE_NEW_ROW(T) shall fail and return a non-zero value. ]*/ \ LogError("failure in malloc_2(number of cols=%" PRIu32 ", sizeof(" MU_TOSTRING(T) ")=%zu)", \ two_d_array->cols, sizeof(T)); \ result = MU_FAILURE; \ } \ else \ { \ TWO_D_ARRAY_TYPEDEF_NAME(T)* array = THANDLE_GET_T(TWO_D_ARRAY_TYPEDEF_NAME(C))(two_d_array); \ array->row_arrays[row_index] = new_row; \ result = 0; \ } \ } \ } \ } \ return result; \ } #define TWO_D_ARRAY_LL_GET_ROW_DEFINE(C, T) \ T* TWO_D_ARRAY_LL_GET_ROW(C)(TWO_D_ARRAY_LL(T) two_d_array, uint32_t row_index) \ { \ T* result; \ /* Codes_SRS_TWO_D_ARRAY_07_019: [ If two_d_array is NULL, TWO_D_ARRAY_GET_ROW(T) shall fail return NULL. ]*/ \ if (two_d_array == NULL) \ { \ LogError("Invalid arguments: TWO_D_ARRAY (" MU_TOSTRING(T) ") two_d_array=%p", two_d_array); \ } \ else \ { \ /* Codes_SRS_TWO_D_ARRAY_07_020: [ If row_index is equal or greater than row_size, TWO_D_ARRAY_GET_ROW(T) shall fail return NULL. ]*/ \ if (row_index >= two_d_array->rows) \ { \ LogError("Invalid arguments: uint32_t row_index=%" PRIu32 " out of bound, total_rows=%" PRIu32, row_index, two_d_array->rows); \ } \ else \ { \ result = two_d_array->row_arrays[row_index]; \ if(result == NULL) \ { \ /* Codes_SRS_TWO_D_ARRAY_07_021: [ If the array stored in row_index is NULL, TWO_D_ARRAY_GET_ROW(T) shall fail and return NULL. ]*/ \ LogError("The row trying to acquire is not allocated: TWO_D_ARRAY (" MU_TOSTRING(T) ") two_d_array=%p, row_index=%" PRIu32, \ two_d_array, row_index); \ } \ /* Codes_SRS_TWO_D_ARRAY_07_022: [ Otherwise, TWO_D_ARRAY_GET_ROW(T) shall return the entire column stored in the corresponding row_index. ]*/ \ else \ { \ goto all_ok; \ } \ } \ } \ result = NULL; \ all_ok: \ return result; \ } \ /*"macros to use in 'reals_' headers"*/ \ #define TWO_D_ARRAY_LL_TYPE_DECLARE(C, T) \ /*hint: have TWO_D_ARRAY_DEFINE_STRUCT_TYPE(T) before TWO_D_ARRAY_LL_TYPE_DECLARE*/ \ THANDLE_LL_TYPE_DECLARE(TWO_D_ARRAY_TYPEDEF_NAME(C), TWO_D_ARRAY_TYPEDEF_NAME(T)) \ TWO_D_ARRAY_LL_CREATE_DECLARE(C, T) \ TWO_D_ARRAY_LL_FREE_ROW_DECLARE(C, T) \ TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW_DECLARE(C, T) \ TWO_D_ARRAY_LL_GET_ROW_DECLARE(C, T) \ #define TWO_D_ARRAY_LL_TYPE_DEFINE(C, T) \ /*hint: have THANDLE_TYPE_DEFINE(TWO_D_ARRAY_TYPEDEF_NAME(T)) before TWO_D_ARRAY_LL_TYPE_DEFINE*/ \ TWO_D_ARRAY_LL_FREE_DEFINE(C, T) \ TWO_D_ARRAY_LL_CREATE_DEFINE(C, T) \ TWO_D_ARRAY_LL_FREE_ROW_DEFINE(C, T) \ TWO_D_ARRAY_LL_ALLOCATE_NEW_ROW_DEFINE(C, T) \ TWO_D_ARRAY_LL_GET_ROW_DEFINE(C, T) \ #endif /*TWO_D_ARRAY_LL_H*/