diff --git a/include/zh_vector.h b/include/zh_vector.h index e48b193..d2e3be4 100644 --- a/include/zh_vector.h +++ b/include/zh_vector.h @@ -22,6 +22,8 @@ #pragma once +#include "stdint.h" +#include "stddef.h" #include "esp_err.h" #include "esp_log.h" #include "esp_heap_caps.h" @@ -62,36 +64,38 @@ extern "C" * * Frees all allocated elements, the items array, the mutex, and the vector * structure itself. The vector pointer is set to NULL after deletion. + * The vector is marked as deleting and the function waits until all + * operations in progress finish (reference counter drops to zero). * - * @param[in,out] vector Pointer to the vector handle to be freed (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle to be freed (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if vector or *vector is NULL - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted, mutex acquisition fails or deletion is already in progress */ esp_err_t zh_vector_free(zh_vector_t **vector); /** * @brief Get the current number of elements in the vector. * - * @param[in] vector Pointer to the vector handle (must not be NULL) + * @param[in] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[out] size Pointer to receive the current size (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if any parameter is NULL - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails */ esp_err_t zh_vector_get_size(zh_vector_t **vector, uint16_t *size); /** * @brief Get the current capacity of the vector. * - * @param[in] vector Pointer to the vector handle (must not be NULL) + * @param[in] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[out] capacity Pointer to receive the current capacity (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if any parameter is NULL - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails */ esp_err_t zh_vector_get_capacity(zh_vector_t **vector, uint16_t *capacity); @@ -102,12 +106,12 @@ extern "C" * a copy of the provided item at index 0. The vector capacity * will be increased if necessary. * - * @param[in,out] vector Pointer to the vector handle (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[in] item Pointer to the item to insert (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if any parameter is NULL - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails * @return ESP_ERR_NO_MEM if vector is full or element allocation fails */ esp_err_t zh_vector_push_front(zh_vector_t **vector, const void *item); @@ -118,12 +122,12 @@ extern "C" * Creates a copy of the provided item and adds it to the end of the vector. * The vector capacity will be increased if necessary. * - * @param[in,out] vector Pointer to the vector handle (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[in] item Pointer to the item to append (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if any parameter is NULL - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails * @return ESP_ERR_NO_MEM if vector is full or element allocation fails */ esp_err_t zh_vector_push_back(zh_vector_t **vector, const void *item); @@ -134,26 +138,26 @@ extern "C" * Copies the provided item into the existing element at the given index. * The element must already exist and be non-NULL. * - * @param[in,out] vector Pointer to the vector handle (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[in] index Index of the element to replace (must be < size) * @param[in] item Pointer to the new item data (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if any parameter is NULL or index is out of bounds - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails or element at index is NULL + * @return ESP_ERR_INVALID_STATE if the vector is being deleted, mutex acquisition fails or the element at index is NULL */ esp_err_t zh_vector_change_item(zh_vector_t **vector, uint16_t index, const void *item); /** * @brief Retrieve a copy of the element at the specified index. * - * @param[in] vector Pointer to the vector handle (must not be NULL) + * @param[in] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[in] index Index of the element to retrieve (must be < size) * @param[out] item Pointer to buffer where the element will be copied (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if any parameter is NULL or index is out of bounds - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails or item is NULL + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails * * @note The element at the specified index must exist and be non-NULL. * This is guaranteed if the vector was properly initialized and no @@ -167,12 +171,12 @@ extern "C" * Frees the element's memory, shifts remaining elements, and updates size. * Capacity may be reduced if it exceeds twice the current size. * - * @param[in,out] vector Pointer to the vector handle (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[in] index Index of the element to delete (must be < size) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if vector or *vector is NULL or index is out of bounds - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails */ esp_err_t zh_vector_delete_item(zh_vector_t **vector, uint16_t index); @@ -182,11 +186,11 @@ extern "C" * Frees the last element's memory and updates size. * Capacity may be reduced if it exceeds twice the current size. * - * @param[in,out] vector Pointer to the vector handle (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if vector or *vector is NULL or vector is empty - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails */ esp_err_t zh_vector_delete_back(zh_vector_t **vector); @@ -196,11 +200,11 @@ extern "C" * Frees the first element's memory, shifts remaining elements, and updates size. * Capacity may be reduced if it exceeds twice the current size. * - * @param[in,out] vector Pointer to the vector handle (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if vector or *vector is NULL or vector is empty - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails */ esp_err_t zh_vector_delete_front(zh_vector_t **vector); @@ -210,11 +214,11 @@ extern "C" * Compares elements using memcmp and removes subsequent duplicates. * Elements are compared based on their raw binary content. * - * @param[in,out] vector Pointer to the vector handle (must not be NULL) + * @param[in,out] vector Pointer to the pointer to the vector handle (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if vector or *vector is NULL - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails */ esp_err_t zh_vector_remove_duplicates(zh_vector_t **vector); @@ -224,13 +228,13 @@ extern "C" * Searches for an element whose binary content matches the provided item * using memcmp. Returns the index of the first match or -1 if not found. * - * @param[in] vector Pointer to the vector handle (must not be NULL) + * @param[in] vector Pointer to the pointer to the vector handle (must not be NULL) * @param[in] item Pointer to the item to search for (must not be NULL) * @param[out] index Pointer to receive the found index (-1 if not found) (must not be NULL) * * @return ESP_OK on success * @return ESP_ERR_INVALID_ARG if any parameter is NULL - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails * @return ESP_ERR_NOT_FOUND if item is not found in the vector */ esp_err_t zh_vector_find_item(zh_vector_t **vector, const void *item, int32_t *index); @@ -239,23 +243,26 @@ extern "C" * @brief Find an element by comparing a specific field within structures. * * Searches for an element where a specified field (identified by offset - * and size) matches the provided value. Comparison starts from the - * specified index. + * and size) matches the provided value. The offset is derived from the + * address difference between `item` and `sample_struct` and must lie + * within the element together with the field size. Comparison starts + * from the specified index. * - * @param[in] vector Pointer to the vector handle containing structures (must not be NULL) + * @param[in] vector Pointer to the pointer to the vector handle containing structures (must not be NULL) * @param[in] sample_struct Pointer to a sample structure (used for field offset calculation) (must not be NULL) * @param[in] item Pointer to the field within the structure (e.g., `&sample.id`). Used to calculate the field offset (must not be NULL) - * @param[in] size Size of the field in bytes (must be > 0) + * @param[in] size Size of the field in bytes (must be > 0 and the field must fit within the element) * @param[in] value Pointer to the value to match against the field (must not be NULL) * @param[in] start Index to begin searching from (must be < size) * @param[out] index Pointer to receive the found index (-1 if not found) (must not be NULL) * * @return ESP_OK on success - * @return ESP_ERR_INVALID_ARG if any parameter is invalid - * @return ESP_ERR_INVALID_STATE if mutex acquisition fails + * @return ESP_ERR_INVALID_ARG if any parameter is invalid, the field offset with size exceeds the element size, + * or *index is NULL when size is zero + * @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails * @return ESP_ERR_NOT_FOUND if no matching element is found */ - esp_err_t zh_vector_find_item_in_field(zh_vector_t **vector, const void *sample_struct, const void *item, size_t size, const void *value, uint16_t start, int32_t *index); + esp_err_t zh_vector_find_item_in_field(zh_vector_t **vector, const void *sample_struct, const void *item, uint16_t size, const void *value, uint16_t start, int32_t *index); #ifdef __cplusplus } diff --git a/zh_vector.c b/zh_vector.c index 3cf6f41..b96bc7d 100644 --- a/zh_vector.c +++ b/zh_vector.c @@ -32,8 +32,35 @@ struct _zh_vector_t uint16_t size; /*!< Current number of elements in the vector */ uint16_t unit; /*!< Size of each element in bytes */ SemaphoreHandle_t mutex; /*!< FreeRTOS mutex for thread-safe operations */ + portMUX_TYPE lock; /*!< Critical section guard for deleting flag and reference counter */ + bool deleting; /*!< Flag indicating the vector is being deleted and new operations must be rejected */ + uint16_t refcount; /*!< Number of public operations currently in progress */ }; +/** + * @brief Register an in-progress operation on the vector. + * + * Increments the reference counter unless the vector is already being + * deleted. The check and the increment are performed atomically within + * a critical section. + * + * @param vector Pointer to the vector + * + * @return true if the operation is allowed and the reference counter was incremented + * @return false if the vector is being deleted + */ +static bool _zh_vector_take(zh_vector_t *vector); + +/** + * @brief Unregister a finished operation on the vector. + * + * Decrements the reference counter within a critical section so that + * a pending deletion can proceed once no operations remain in progress. + * + * @param vector Pointer to the vector + */ +static void _zh_vector_give(zh_vector_t *vector); + /** * @brief Resize the vector's internal items array. * @@ -54,8 +81,9 @@ static esp_err_t _resize(zh_vector_t *vector, uint16_t capacity); * @brief Delete an element at the specified index and shift remaining elements. * * Frees the element's memory, shifts elements after the index one position left, - * and updates size. Automatically shrinks capacity if it exceeds twice the size - * or if the vector becomes empty. + * and updates size. Releases all internal storage when the vector becomes empty. + * Otherwise shrinks capacity to twice the size when capacity exceeds four and + * half the capacity is at least four times the size. * * @param vector Pointer to the vector * @param index Index of the element to delete @@ -80,16 +108,19 @@ static inline uint16_t _calc_new_capacity(uint16_t current); esp_err_t zh_vector_init(zh_vector_t **vector, uint16_t unit) { ZH_LOGI("Vector initialization begin."); - ZH_ERROR_CHECK(vector != NULL && unit > 0, ESP_ERR_INVALID_ARG, NULL, "Vector initialization failed. Invalid argument."); + ZH_ERROR_CHECK(vector != NULL && unit != 0, ESP_ERR_INVALID_ARG, NULL, "Vector initialization failed. Invalid argument."); ZH_ERROR_CHECK(*vector == NULL, ESP_ERR_INVALID_STATE, NULL, "Vector initialization failed. Vector is already initialized."); *vector = heap_caps_calloc(1, sizeof(zh_vector_t), MALLOC_CAP_8BIT); ZH_ERROR_CHECK(*vector != NULL, ESP_ERR_NO_MEM, NULL, "Vector initialization failed. Failed to allocate vector structure."); (*vector)->mutex = xSemaphoreCreateMutex(); ZH_ERROR_CHECK((*vector)->mutex != NULL, ESP_ERR_NO_MEM, heap_caps_free(*vector); *vector = NULL, "Vector initialization failed. Failed to create mutex."); + portMUX_INITIALIZE(&(*vector)->lock); (*vector)->items = NULL; (*vector)->capacity = 0; (*vector)->size = 0; (*vector)->unit = unit; + (*vector)->deleting = false; + (*vector)->refcount = 0; ZH_LOGI("Vector initialization success."); return ESP_OK; } @@ -99,6 +130,23 @@ esp_err_t zh_vector_free(zh_vector_t **vector) ZH_LOGI("Vector deletion begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Vector deletion failed. Invalid argument."); ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Vector deletion failed. Failed to acquire mutex."); + taskENTER_CRITICAL(&(*vector)->lock); + ZH_ERROR_CHECK((*vector)->deleting == false, ESP_ERR_INVALID_STATE, taskEXIT_CRITICAL(&(*vector)->lock); xSemaphoreGive((*vector)->mutex), "Vector deletion failed. The vector is being deleted."); + (*vector)->deleting = true; + taskEXIT_CRITICAL(&(*vector)->lock); + xSemaphoreGive((*vector)->mutex); + for (;;) + { + uint16_t refcount; + taskENTER_CRITICAL(&(*vector)->lock); + refcount = (*vector)->refcount; + taskEXIT_CRITICAL(&(*vector)->lock); + if (refcount == 0) + { + break; + } + vTaskDelay(1); + } for (uint16_t i = 0; i < (*vector)->size; ++i) { if ((*vector)->items[i] != NULL) @@ -123,9 +171,11 @@ esp_err_t zh_vector_get_size(zh_vector_t **vector, uint16_t *size) { ZH_LOGI("Getting vector size begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL && size != NULL, ESP_ERR_INVALID_ARG, NULL, "Getting vector size failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Getting vector size failed. Failed to acquire mutex."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Getting vector size failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Getting vector size failed. Failed to acquire mutex."); *size = (*vector)->size; xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Getting vector size success."); return ESP_OK; } @@ -134,9 +184,11 @@ esp_err_t zh_vector_get_capacity(zh_vector_t **vector, uint16_t *capacity) { ZH_LOGI("Getting vector capacity begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL && capacity != NULL, ESP_ERR_INVALID_ARG, NULL, "Getting vector capacity failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Getting vector capacity failed. Failed to acquire mutex."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Getting vector capacity failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Getting vector capacity failed. Failed to acquire mutex."); *capacity = (*vector)->capacity; xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Getting vector capacity success."); return ESP_OK; } @@ -145,15 +197,17 @@ esp_err_t zh_vector_push_front(zh_vector_t **vector, const void *item) { ZH_LOGI("Adding item to beginning of vector begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to beginning of vector failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Adding item to beginning of vector failed. Failed to acquire mutex."); - ZH_ERROR_CHECK((*vector)->size < UINT16_MAX, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex), "Adding item to beginning of vector failed. Vector is full."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to beginning of vector failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Adding item to beginning of vector failed. Failed to acquire mutex."); + ZH_ERROR_CHECK((*vector)->size < UINT16_MAX, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to beginning of vector failed. Vector is full."); if ((*vector)->capacity == (*vector)->size) { uint16_t new_capacity = _calc_new_capacity((*vector)->capacity); - ZH_ERROR_CHECK(_resize(*vector, new_capacity) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex), "Adding item to beginning of vector failed. Memory reallocation failed."); + ZH_ERROR_CHECK(new_capacity > (*vector)->capacity, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to beginning of vector failed. Cannot grow beyond current capacity."); + ZH_ERROR_CHECK(_resize(*vector, new_capacity) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to beginning of vector failed. Memory reallocation failed."); } void *new_item = heap_caps_calloc(1, (*vector)->unit, MALLOC_CAP_8BIT); - ZH_ERROR_CHECK(new_item != NULL, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex), "Adding item to beginning of vector failed. Element allocation failed."); + ZH_ERROR_CHECK(new_item != NULL, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to beginning of vector failed. Element allocation failed."); for (uint16_t i = (*vector)->size; i > 0; --i) { (*vector)->items[i] = (*vector)->items[i - 1]; @@ -162,6 +216,7 @@ esp_err_t zh_vector_push_front(zh_vector_t **vector, const void *item) memcpy((*vector)->items[0], item, (*vector)->unit); (*vector)->size++; xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Adding item to beginning of vector success."); return ESP_OK; } @@ -170,19 +225,22 @@ esp_err_t zh_vector_push_back(zh_vector_t **vector, const void *item) { ZH_LOGI("Adding item to vector begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to vector failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Adding item to vector failed. Failed to acquire mutex."); - ZH_ERROR_CHECK((*vector)->size < UINT16_MAX, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex), "Adding item to vector failed. Vector is full."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to vector failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Adding item to vector failed. Failed to acquire mutex."); + ZH_ERROR_CHECK((*vector)->size < UINT16_MAX, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to vector failed. Vector is full."); if ((*vector)->capacity == (*vector)->size) { uint16_t new_capacity = _calc_new_capacity((*vector)->capacity); - ZH_ERROR_CHECK(_resize(*vector, new_capacity) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex), "Adding item to vector failed. Memory reallocation failed."); + ZH_ERROR_CHECK(new_capacity > (*vector)->capacity, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to vector failed. Cannot grow beyond current capacity."); + ZH_ERROR_CHECK(_resize(*vector, new_capacity) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to vector failed. Memory reallocation failed."); } uint16_t idx = (*vector)->size; (*vector)->items[idx] = heap_caps_calloc(1, (*vector)->unit, MALLOC_CAP_8BIT); - ZH_ERROR_CHECK((*vector)->items[idx] != NULL, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex), "Adding item to vector failed. Element allocation failed."); + ZH_ERROR_CHECK((*vector)->items[idx] != NULL, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Adding item to vector failed. Element allocation failed."); memcpy((*vector)->items[idx], item, (*vector)->unit); (*vector)->size++; xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Adding item to vector success."); return ESP_OK; } @@ -191,11 +249,13 @@ esp_err_t zh_vector_change_item(zh_vector_t **vector, uint16_t index, const void { ZH_LOGI("Changing item in vector begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Changing item in vector failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Changing item in vector failed. Failed to acquire mutex."); - ZH_ERROR_CHECK(index < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex), "Changing item in vector failed. Index out of bounds."); - ZH_ERROR_CHECK((*vector)->items[index] != NULL, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex), "Changing item in vector failed. Item is NULL."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Changing item in vector failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Changing item in vector failed. Failed to acquire mutex."); + ZH_ERROR_CHECK(index < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Changing item in vector failed. Index out of bounds."); + ZH_ERROR_CHECK((*vector)->items[index] != NULL, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Changing item in vector failed. Item is NULL."); memcpy((*vector)->items[index], item, (*vector)->unit); xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Changing item in vector success."); return ESP_OK; } @@ -204,11 +264,13 @@ esp_err_t zh_vector_get_item(zh_vector_t **vector, uint16_t index, void *item) { ZH_LOGI("Getting item from vector begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Getting item from vector failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Getting item from vector failed. Failed to acquire mutex."); - ZH_ERROR_CHECK(index < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex), "Getting item from vector failed. Index out of bounds."); - ZH_ERROR_CHECK((*vector)->items[index] != NULL, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex), "Getting item from vector failed. Item is NULL."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Getting item from vector failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Getting item from vector failed. Failed to acquire mutex."); + ZH_ERROR_CHECK(index < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Getting item from vector failed. Index out of bounds."); + ZH_ERROR_CHECK((*vector)->items[index] != NULL, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Getting item from vector failed. Item is NULL."); memcpy(item, (*vector)->items[index], (*vector)->unit); xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Getting item from vector success."); return ESP_OK; } @@ -217,10 +279,12 @@ esp_err_t zh_vector_delete_item(zh_vector_t **vector, uint16_t index) { ZH_LOGI("Deleting item in vector begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Deleting item in vector failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Deleting item in vector failed. Failed to acquire mutex."); - ZH_ERROR_CHECK(index < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex), "Deleting item in vector failed. Index out of bounds."); - ZH_ERROR_CHECK(_delete(*vector, index) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex), "Deleting item in vector failed. Internal error."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Deleting item in vector failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Deleting item in vector failed. Failed to acquire mutex."); + ZH_ERROR_CHECK(index < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Deleting item in vector failed. Index out of bounds."); + ZH_ERROR_CHECK(_delete(*vector, index) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Deleting item in vector failed. Internal error."); xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Deleting item in vector success."); return ESP_OK; } @@ -229,10 +293,12 @@ esp_err_t zh_vector_delete_back(zh_vector_t **vector) { ZH_LOGI("Deleting item in back begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Deleting item in back failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Deleting item in back failed. Failed to acquire mutex."); - ZH_ERROR_CHECK((*vector)->size > 0, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex), "Deleting item in back failed. Vector is empty."); - ZH_ERROR_CHECK(_delete(*vector, (*vector)->size - 1) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex), "Deleting item in back failed. Internal error."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Deleting item in back failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Deleting item in back failed. Failed to acquire mutex."); + ZH_ERROR_CHECK((*vector)->size > 0, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Deleting item in back failed. Vector is empty."); + ZH_ERROR_CHECK(_delete(*vector, (*vector)->size - 1) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Deleting item in back failed. Internal error."); xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Deleting item in back success."); return ESP_OK; } @@ -241,10 +307,12 @@ esp_err_t zh_vector_delete_front(zh_vector_t **vector) { ZH_LOGI("Deleting item in front begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Deleting item in front failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Deleting item in front failed. Failed to acquire mutex."); - ZH_ERROR_CHECK((*vector)->size > 0, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex), "Deleting item in front failed. Vector is empty."); - ZH_ERROR_CHECK(_delete(*vector, 0) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex), "Deleting item in front failed. Internal error."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Deleting item in front failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Deleting item in front failed. Failed to acquire mutex."); + ZH_ERROR_CHECK((*vector)->size > 0, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Deleting item in front failed. Vector is empty."); + ZH_ERROR_CHECK(_delete(*vector, 0) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Deleting item in front failed. Internal error."); xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Deleting item in front success."); return ESP_OK; } @@ -253,26 +321,35 @@ esp_err_t zh_vector_remove_duplicates(zh_vector_t **vector) { ZH_LOGI("Removing duplicates from vector begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Removing duplicates from vector failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Removing duplicates from vector failed. Failed to acquire mutex."); - if ((*vector)->size >= 2) + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Removing duplicates from vector failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Removing duplicates from vector failed. Failed to acquire mutex."); + ZH_ERROR_CHECK((*vector)->size > 0, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Removing duplicates from vector failed. Vector is empty."); + for (uint16_t i = 0; i < (*vector)->size; ++i) { - for (uint16_t i = 0; i < (*vector)->size - 1; ++i) + if ((*vector)->items[i] == NULL) { - uint16_t j = i + 1; - while (j < (*vector)->size) + continue; + } + uint16_t j = i + 1; + while (j < (*vector)->size) + { + if ((*vector)->items[j] == NULL) { - if (memcmp((*vector)->items[i], (*vector)->items[j], (*vector)->unit) == 0) - { - ZH_ERROR_CHECK(_delete(*vector, j) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex), "Removing duplicates from vector failed. Internal error."); - } - else - { - ++j; - } + ++j; + continue; + } + if (memcmp((*vector)->items[i], (*vector)->items[j], (*vector)->unit) == 0) + { + ZH_ERROR_CHECK(_delete(*vector, j) == ESP_OK, ESP_ERR_INVALID_STATE, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Removing duplicates from vector failed. Internal error."); + } + else + { + ++j; } } } xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Removing duplicates from vector success."); return ESP_OK; } @@ -281,61 +358,89 @@ esp_err_t zh_vector_find_item(zh_vector_t **vector, const void *item, int32_t *i { ZH_LOGI("Finding item in vector begin."); ZH_ERROR_CHECK(vector != NULL && *vector != NULL && item != NULL && index != NULL, ESP_ERR_INVALID_ARG, NULL, "Finding item in vector failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Finding item in vector failed. Failed to acquire mutex."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Finding item in vector failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Finding item in vector failed. Failed to acquire mutex."); *index = -1; for (uint16_t i = 0; i < (*vector)->size; ++i) { + if ((*vector)->items[i] == NULL) + { + continue; + } if (memcmp((*vector)->items[i], item, (*vector)->unit) == 0) { *index = (int32_t)i; xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Finding item in vector success (found)."); return ESP_OK; } } xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Finding item in vector success (not found)."); return ESP_ERR_NOT_FOUND; } -esp_err_t zh_vector_find_item_in_field(zh_vector_t **vector, const void *sample_struct, const void *item, size_t size, const void *value, uint16_t start, int32_t *index) +esp_err_t zh_vector_find_item_in_field(zh_vector_t **vector, const void *sample_struct, const void *item, uint16_t size, const void *value, uint16_t start, int32_t *index) { ZH_LOGI("Finding field in structure begin."); - ZH_ERROR_CHECK(vector != NULL && *vector != NULL && sample_struct != NULL && item != NULL && value != NULL && index != NULL && size > 0, ESP_ERR_INVALID_ARG, NULL, "Finding field in structure failed. Invalid argument."); - ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, NULL, "Finding field in structure failed. Mutex acquire failed."); - ZH_ERROR_CHECK(start < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex), "Finding field in structure failed. Start index out of bounds."); - size_t offset = (const uint8_t *)item - (const uint8_t *)sample_struct; - ZH_ERROR_CHECK((offset + size) <= (*vector)->unit, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex), "Finding field in structure failed. Field exceeds element size."); + ZH_ERROR_CHECK(vector != NULL && *vector != NULL && sample_struct != NULL && item != NULL && value != NULL && index != NULL, ESP_ERR_INVALID_ARG, NULL, "Finding field in structure failed. Invalid argument."); + ZH_ERROR_CHECK(size > 0, ESP_ERR_INVALID_ARG, *index = -1, "Finding field in structure failed. Invalid argument."); + ZH_ERROR_CHECK(_zh_vector_take(*vector) == true, ESP_ERR_INVALID_STATE, NULL, "Finding field in structure failed. The vector is being deleted."); + ZH_ERROR_CHECK(xSemaphoreTake((*vector)->mutex, portMAX_DELAY) == pdTRUE, ESP_ERR_INVALID_STATE, _zh_vector_give(*vector), "Finding field in structure failed. Mutex acquire failed."); + ZH_ERROR_CHECK(start < (*vector)->size, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Finding field in structure failed. Start index out of bounds."); + ZH_ERROR_CHECK((uint32_t)item >= (uint32_t)sample_struct, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Finding field in structure failed. Field pointer is before sample struct."); + uint32_t offset = (uint32_t)item - (uint32_t)sample_struct; + ZH_ERROR_CHECK(offset < (*vector)->unit, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Finding field in structure failed. Field offset exceeds element size."); + ZH_ERROR_CHECK((offset + size) <= (*vector)->unit, ESP_ERR_INVALID_ARG, xSemaphoreGive((*vector)->mutex); _zh_vector_give(*vector), "Finding field in structure failed. Field exceeds element size."); *index = -1; for (uint16_t i = start; i < (*vector)->size; ++i) { - const uint8_t *elem = (const uint8_t *)(*vector)->items[i]; - if (memcmp(elem + offset, value, size) == 0) + if ((*vector)->items[i] == NULL) + { + continue; + } + if (memcmp((const uint8_t *)(*vector)->items[i] + offset, value, size) == 0) { *index = (int32_t)i; xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Finding field in structure success (found)."); return ESP_OK; } } xSemaphoreGive((*vector)->mutex); + _zh_vector_give(*vector); ZH_LOGI("Finding field in structure success (not found)."); return ESP_ERR_NOT_FOUND; } +static bool _zh_vector_take(zh_vector_t *vector) +{ + taskENTER_CRITICAL(&vector->lock); + if (vector->deleting == true) + { + taskEXIT_CRITICAL(&vector->lock); + return false; + } + ++vector->refcount; + taskEXIT_CRITICAL(&vector->lock); + return true; +} + +static void _zh_vector_give(zh_vector_t *vector) +{ + taskENTER_CRITICAL(&vector->lock); + --vector->refcount; + taskEXIT_CRITICAL(&vector->lock); +} + static esp_err_t _resize(zh_vector_t *vector, uint16_t capacity) { ZH_ERROR_CHECK(capacity >= vector->size, ESP_ERR_INVALID_ARG, NULL, "Invalid argument."); if (capacity == 0) { - if (vector->items != NULL) - { - for (uint16_t i = 0; i < vector->size; ++i) - { - heap_caps_free(vector->items[i]); - vector->items[i] = NULL; - } - } heap_caps_free(vector->items); vector->items = NULL; vector->size = 0; @@ -370,13 +475,24 @@ static esp_err_t _delete(zh_vector_t *vector, uint16_t index) heap_caps_free(freed_item); freed_item = NULL; --vector->size; - if (vector->size > 0 && vector->capacity / 2 > vector->size) + if (vector->size == 0) { - return _resize(vector, vector->size); + if (vector->capacity > 0) + { + _resize(vector, 0); + } } - else if (vector->size == 0 && vector->capacity > 0) + else if (vector->capacity > 4 && vector->capacity / 2 >= vector->size * 2) { - _resize(vector, 0); + uint16_t target = (vector->size > UINT16_MAX / 2) ? vector->capacity : vector->size * 2; + if (target < 4) + { + target = 4; + } + if (target < vector->capacity) + { + _resize(vector, target); + } } return ESP_OK; }