#ifndef _ARR_DESC_H_ #define _ARR_DESC_H_ /*--------------------------------------------------------------------------------*/ /* Includes */ /*--------------------------------------------------------------------------------*/ #include #include /* memset() */ #include "../util/util.h" /* CONCAT() */ /*--------------------------------------------------------------------------------*/ /* Type Definitions */ /*--------------------------------------------------------------------------------*/ /** * Array-descriptor struct. */ typedef struct ARR_DESC_struct { void * data_ptr; /* Pointer to the array contents. */ int32_t element_count; /* Number of current elements. */ int32_t element_size; /* Size of current elements in bytes. */ int32_t underlying_size; /* Size of underlying array in bytes. */ } ARR_DESC_t; /*--------------------------------------------------------------------------------*/ /* Macros and Defines */ /*--------------------------------------------------------------------------------*/ /** * Prefix of the array variable's name when creating an array and an array * descriptor at the same time. */ #define ARR_DESC_ARR_PREFIX ARR_DESC_ARR_ /** * Evaluate to the array variable's name when creating an array and an array * descriptor at the same time. */ #define ARR_DESC_ARR_NAME(name) \ CONCAT(ARR_DESC_ARR_PREFIX, name) /** * Define an #ARR_DESC_t by itself. * * @note The user must supply an array to store the data used by the * #ARR_DESC_t. */ #define ARR_DESC_INTERNAL_DEFINE(name, data_ptr, \ element_count, element_size) \ ARR_DESC_t name = { \ data_ptr, \ element_count, \ element_size, \ element_count * element_size \ } \ /** * Define both an array and an #ARR_DESC_t that describes it. * * @note Use the #CURLY() macro for the content field; it provides the curly * braces necessary for an array initialization. */ #define ARR_DESC_DEFINE(type, name, element_count, content) \ type ARR_DESC_ARR_NAME(name)[element_count] = content; \ ARR_DESC_INTERNAL_DEFINE(name, \ &ARR_DESC_ARR_NAME(name), \ element_count, \ sizeof(type)) /* Note the lacking semicolon */ /** * Create a #ARR_DESC_t which refers to a subset of the data in another. * * The new #ARR_DESC_t shares the same underlying array as the aliased * #ARR_DESC_t, but only describes a subset of the originals values. */ #define ARR_DESC_DEFINE_SUBSET(name, original, element_cnt) \ ARR_DESC_INTERNAL_DEFINE(name, \ &ARR_DESC_ARR_NAME(original), \ element_cnt, \ sizeof(ARR_DESC_ARR_NAME(original)[0]) \ ) /* Note the lacking semicolon */ /** * Creat an #ARR_DESC_t which points to the data in an existing array. * * @param start_idx Offset in array_ptr of first element. * @param element_cnt Number of elements to include in the #ARR_DESC_t. * * @example * * float my_floats[4] = {0.0f, 1.0f, 2.0f, 3.0f}; * * ARR_DESC_DEFINE_USING_ARR(my_arr_desc, my_floats, 1, 3); * * printf("Element 0: %f\n", ARR_DESC_ELT(float, 0, &my_arr_desc)); * printf("Element 1: %f\n", ARR_DESC_ELT(float, 1, &my_arr_desc)); * * Outputs: * * Element 0: 1.000000 * Element 1: 2.000000 * * @warning There are no checks in place to catch invalid start indices; This * is left to the user. */ #define ARR_DESC_DEFINE_USING_ARR(type, name, array_ptr, start_idx, element_cnt) \ ARR_DESC_INTERNAL_DEFINE( \ name, \ (type *) (array_ptr + start_idx), \ element_cnt, \ sizeof(type) \ ) /* Note the lacking semicolon*/ /** * Declare an #ARR_DESC_t object. */ #define ARR_DESC_DECLARE(name) \ extern ARR_DESC_t name /* Note the lacking semicolon */ /** * Evaluate to the number of bytes stored in the #ARR_DESC_t. */ #define ARR_DESC_BYTES(arr_desc_ptr) \ ((arr_desc_ptr)->element_count * (arr_desc_ptr)->element_size) /** * Set the contents of #ARR_DESC_t to value. */ #define ARR_DESC_MEMSET(arr_desc_ptr, value, bytes) \ do \ { \ memset((arr_desc_ptr)->data_ptr, \ value, \ BOUND(0, \ (arr_desc_ptr)->underlying_size, \ bytes) \ ); \ } while (0) /** * Perform a memcpy of 'bytes' bytes from the source #ARR_DESC_t to the * destination #ARR_DESC_t. */ #define ARR_DESC_MEMCPY(arr_desc_dest_ptr, arr_desc_src_ptr, bytes) \ do \ { \ memcpy((arr_desc_dest_ptr)->data_ptr, \ (arr_desc_src_ptr)->data_ptr, \ BOUND(0, \ (arr_desc_dest_ptr)->underlying_size, \ bytes)); \ } while (0) /** * Evaluate to true if the source #ARR_DESC_t contents will fit into the * destination #ARR_DESC_t and false otherwise. */ #define ARR_DESC_COPYABLE(arr_desc_dest_ptr, arr_desc_src_ptr) \ (ARR_DESC_BYTES(arr_desc_src_ptr) <= \ (arr_desc_dest_ptr)->underlying_size) /** * Copy all the data from the source #ARR_DESC_t to the destination * #ARR_DESC_t. * * @note If the destination #ARR_DESC_t is too small to fit the source data the * copy is aborted and nothing happens. */ #define ARR_DESC_COPY(arr_desc_dest_ptr, arr_desc_src_ptr) \ do \ { \ if (ARR_DESC_COPYABLE(arr_desc_dest_ptr, \ arr_desc_src_ptr)) \ { \ ARR_DESC_MEMCPY(arr_desc_dest_ptr, \ arr_desc_src_ptr, \ ARR_DESC_BYTES(arr_desc_src_ptr)); \ /* Update the properties*/ \ (arr_desc_dest_ptr)->element_count = \ (arr_desc_src_ptr)->element_count; \ (arr_desc_dest_ptr)->element_size = \ (arr_desc_src_ptr)->element_size; \ } \ } while (0) /** * Compare the data in two #ARR_DESC_t structs for the specified number of * bytes. */ #define ARR_DESC_MEMCMP(arr_desc_ptr_a, arr_desc_ptr_b, bytes) \ memcmp((arr_desc_ptr_a)->data_ptr, \ (arr_desc_ptr_b)->data_ptr, \ bytes) /* Note the lacking semicolon */ \ /** * Zero out the contents of the #ARR_DESC_t. */ #define ARR_DESC_ZERO(arr_desc_ptr) \ ARR_DESC_MEMSET(arr_desc_ptr, \ 0, \ (arr_desc_ptr)->underlying_size) /** * Evaluate to the data address in #ARR_DESC_t at offset. */ #define ARR_DESC_DATA_ADDR(type, arr_desc_ptr, offset) \ ((void*)(((type *) \ ((arr_desc_ptr)->data_ptr)) \ + offset)) /** * Evaluate to the element in #ARR_DESC_t with type at idx. */ #define ARR_DESC_ELT(type, idx, arr_desc_ptr) \ (*((type *) ARR_DESC_DATA_ADDR(type, \ arr_desc_ptr, \ idx))) #endif /* _ARR_DESC_H_ */