From f17658bcff628939f16e5eb5d9ca3bd3da77e100 Mon Sep 17 00:00:00 2001 From: Alexey Zholtikov Date: Sat, 1 Aug 2026 10:41:33 +0300 Subject: [PATCH] style: updated comments --- README.md | 11 +- WIKI_EN.md | 13 +- WIKI_RU.md | 15 +-- include/zh_vector.h | 318 +++++++++++++++++++------------------------- zh_vector.c | 86 +++++++----- 5 files changed, 209 insertions(+), 234 deletions(-) diff --git a/README.md b/README.md index 0951774..51a6d02 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,13 @@ ## Features -1. Support of any data types. -2. The maximum size of the vector is 65535 elements. -3. Thread-safe implementation using FreeRTOS mutex. -4. Automatic memory management with heap_caps functions. +1. Support for any data types through void pointers. +2. Dynamic capacity with automatic growth and shrinkage. +3. Maximum capacity of 65,535 elements (16-bit index). +4. Thread-safe implementation using FreeRTOS mutex. +5. Automatic memory management with ESP-IDF heap_caps functions. +6. Flexible insertion at front or back. +7. Duplicate removal and item search capabilities. ## Using diff --git a/WIKI_EN.md b/WIKI_EN.md index b41eb50..5cb67f4 100644 --- a/WIKI_EN.md +++ b/WIKI_EN.md @@ -216,7 +216,7 @@ Changes an element at a specific index. - `ESP_OK` - Success - `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer, item pointer, or invalid index) -- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error) +- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex or item is NULL --- @@ -271,7 +271,7 @@ Removes the last element from the vector. **Returns:** - `ESP_OK` - Success -- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or NULL vector pointer or vector is empty) +- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer, NULL vector, or vector is empty) - `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error) **Note:** The deleted item's memory is freed. If the size drops below half of the capacity, the capacity is reduced (same behaviour as zh_vector_delete_item). @@ -289,8 +289,7 @@ Removes the first element from the vector. **Returns:** - `ESP_OK` - Success -- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or NULL vector pointer or vector is empty) -- `ESP_ERR_NO_MEM` - Memory allocation failed (during reallocation) +- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer, NULL vector, or vector is empty) - `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error) **Note:** The deleted item's memory is freed, and all subsequent elements are shifted left by one position. If the size drops below half of the capacity, the capacity is reduced (same behaviour as zh_vector_delete_item). @@ -326,9 +325,9 @@ Finds the first occurrence of a value in a specific field within structures stor - `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL. - `sample_struct` - Pointer to a sample structure of the same type as stored in the vector. Must not be NULL. -- `field` - Pointer to the field within the structure to search. Must be a valid member address within sample_struct. +- `item` - Pointer to the field within the structure (e.g., `&sample.id`). Used to calculate the field offset within the structure. Must not be NULL. - `size` - Size (in bytes) of the field to compare. Must be > 0. -- `item` - Pointer to the value to search for. Must not be NULL. +- `value` - Pointer to the value to search for. Must not be NULL. - `start` - Starting index for the search (0-based). Must be < vector size. - `index` - Pointer to variable to store the found index. Will be set to -1 if value is not found. Must not be NULL. @@ -339,7 +338,7 @@ Finds the first occurrence of a value in a specific field within structures stor - `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error) - `ESP_ERR_NOT_FOUND` - Value not found (index set to -1) -**Note:** This function calculates the offset of the field within the structure using sample_struct and item pointers, then searches for the specified value in that field across all vector elements starting from the given index. Uses memcmp for comparison. Useful for searching in vectors of structures without copying the entire structure. +**Note:** This function calculates the offset of the field within the structure using `item` and `sample_struct` pointers (offset = `item - sample_struct`), then searches for the specified `value` in that field across all vector elements starting from the given index. Uses memcmp for comparison. Useful for searching in vectors of structures without copying the entire structure. --- diff --git a/WIKI_RU.md b/WIKI_RU.md index dfc814d..6010837 100644 --- a/WIKI_RU.md +++ b/WIKI_RU.md @@ -215,8 +215,8 @@ if (ret != ESP_OK) { **Возвращает:** - `ESP_OK` - Успех -- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор, item или неверный индекс) -- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой) +- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на вектор, item или неверный индекс) +- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex или item равен NULL --- @@ -271,7 +271,7 @@ if (ret != ESP_OK) { **Возвращает:** - `ESP_OK` - Успех -- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или NULL указатель на вектор или вектор пуст) +- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на вектор, NULL вектор или вектор пуст) - `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой) **Примечание:** Память удаленного элемента освобождается. Если размер падает ниже половины емкости, емкость уменьшается (такое же поведение, как в zh_vector_delete_item). @@ -289,8 +289,7 @@ if (ret != ESP_OK) { **Возвращает:** - `ESP_OK` - Успех -- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или NULL указатель на вектор или вектор пуст) -- `ESP_ERR_NO_MEM` - Ошибка выделения памяти (при перераспределении) +- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на вектор, NULL вектор или вектор пуст) - `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой) **Примечание:** Память удаленного элемента освобождается, и все последующие элементы сдвигаются влево на одну позицию. Если размер падает ниже половины емкости, емкость уменьшается (такое же поведение, как в zh_vector_delete_item). @@ -326,9 +325,9 @@ if (ret != ESP_OK) { - `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL. - `sample_struct` - Указатель на образец структуры того же типа, что и хранится в векторе. Не должен быть NULL. -- `field` - Указатель на поле внутри структуры для поиска. Должен быть действительным адресом члена внутри sample_struct. +- `item` - Указатель на поле внутри структуры (например, `&sample.id`). Используется для вычисления смещения поля внутри структуры. Не должен быть NULL. - `size` - Размер (в байтах) поля для сравнения. Должен быть > 0. -- `item` - Указатель на значение для поиска. Не должен быть NULL. +- `value` - Указатель на значение для поиска. Не должен быть NULL. - `start` - Начальный индекс для поиска (начиная с 0). Должен быть < размера вектора. - `index` - Указатель на переменную для сохранения найденного индекса. Будет установлен в -1, если значение не найдено. Не должен быть NULL. @@ -339,7 +338,7 @@ if (ret != ESP_OK) { - `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой) - `ESP_ERR_NOT_FOUND` - Значение не найдено (index установлен в -1) -**Примечание:** Эта функция вычисляет смещение поля внутри структуры, используя sample_struct и item указатели, затем ищет указанное значение в этом поле во всех элементах вектора, начиная с заданного индекса. Использует memcmp для сравнения. Полезно для поиска в векторах структур без копирования всей структуры. +**Примечание:** Эта функция вычисляет смещение поля внутри структуры, используя указатели `item` и `sample_struct` (смещение = `item - sample_struct`), затем ищет указанное значение `value` в этом поле во всех элементах вектора, начиная с заданного индекса. Использует memcmp для сравнения. Полезно для поиска в векторах структур без копирования всей структуры. --- diff --git a/include/zh_vector.h b/include/zh_vector.h index 836172a..bcf4c48 100644 --- a/include/zh_vector.h +++ b/include/zh_vector.h @@ -1,15 +1,23 @@ /** * @file zh_vector.h * - * @brief Thread-safe dynamic array (vector) implementation for ESP-IDF. + * @brief Dynamic vector implementation with memory management and thread safety. * - * This vector stores elements by pointer (i.e., it holds `void*` to heap-allocated copies of items). - * The vector is protected by a FreeRTOS mutex, making it safe for use in multi-threaded environments. + * This module provides a generic dynamic vector data structure with automatic + * memory management, element insertion/deletion at any position, duplicate + * removal, and item search capabilities. The implementation is thread-safe + * using FreeRTOS mutexes for all public operations. * - * @note The vector allocates memory for each item on push operations. - * @note The vector does **not** own the lifetime of the data pointed to by the user — it copies the data. - * @warning The `item` parameter of zh_vector_get_item must point to a buffer of at least `unit` bytes. - * The function copies the element *by value* — no pointers are returned. + * Key features: + * - Dynamic capacity with automatic growth and shrinkage + * - Thread-safe operations via FreeRTOS mutex + * - Support for arbitrary data types through void pointers + * - Flexible insertion at front or back + * - Duplicate removal and item search functionality + * - Memory allocation with ESP32 heap capabilities + * + * @note All public functions require a valid vector pointer obtained through zh_vector_init(). + * @note All operations are protected by internal mutex for thread safety. */ #pragma once @@ -26,276 +34,224 @@ extern "C" #endif /** - * @brief Opaque vector type. + * @brief Forward declaration of the vector structure. + * + * Opaque handle representing a dynamic vector with internal state + * including items storage, capacity, size, element size, and mutex. */ typedef struct _zh_vector_t zh_vector_t; /** - * @brief Initializes a vector. + * @brief Initialize a new vector with specified element size. * - * Allocates memory for the vector structure, internal item pointer array and a FreeRTOS mutex. + * Allocates memory for the vector structure and creates an internal mutex. + * The vector starts with zero capacity and will grow dynamically on first use. * - * @note Safe to call with `*vector == NULL` or with `*vector` pointing to uninitialized memory. - * @warning Do **not** call this function twice on the same variable without an intervening `zh_vector_free()` — it will return `ESP_ERR_INVALID_STATE`. - * To reuse a vector, call `zh_vector_free(&v);` then `zh_vector_init(&v, ...);`. + * @param[out] vector Pointer to receive the initialized vector handle (must be NULL) + * @param[in] unit Size of each element in bytes (must be > 0) * - * @param[out] vector Double pointer to vector structure (`zh_vector_t **`). On success, `*vector` points to newly allocated structure. - * @param[in] unit Size (in bytes) of each item. Must be > 0. - * - * @return ESP_OK on success. - * @return ESP_ERR_NO_MEM if memory allocation fails (vector struct or mutex). - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `unit == 0`. - * @return ESP_ERR_INVALID_STATE if `*vector != NULL` (already initialized). + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if vector is NULL or unit is zero + * @return ESP_ERR_INVALID_STATE if vector is already initialized + * @return ESP_ERR_NO_MEM if memory allocation fails */ esp_err_t zh_vector_init(zh_vector_t **vector, uint16_t unit); /** - * @brief Deinitializes and frees the entire vector. + * @brief Free all resources associated with the vector. * - * Frees all item copies, the internal pointer array, and the mutex. Sets `*vector` to `NULL` before returning. + * Frees all allocated elements, the items array, the mutex, and the vector + * structure itself. The vector pointer is set to NULL after deletion. * - * @warning This function is NOT thread-safe. It must not be called concurrently with any other vector operation - * from different tasks or interrupts. The caller is responsible for ensuring that all other accesses to - * the vector have completed before invoking `zh_vector_free`. In a multi‑tasking environment, use an external - * mutex to protect the vector's lifecycle if concurrent calls are possible. + * @param[in,out] vector Pointer to the vector handle to be freed (must not be NULL) * - * @warning After this call, all previously obtained item pointers become invalid. - * @note Safe to call with `vector == NULL` or `*vector == NULL` (returns `ESP_OK`). - * - * @param[in,out] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. Set to `NULL` on exit. - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `*vector == NULL` (not initialized). - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @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 */ esp_err_t zh_vector_free(zh_vector_t **vector); /** - * @brief Gets the current number of items in the vector. + * @brief Get the current number of elements in the vector. * - * @note Thread-safe: internally locks/unlocks the mutex. + * @param[in] vector Pointer to the vector (must not be NULL) + * @param[out] size Pointer to receive the current size (must not be NULL) * - * @param[in] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[out] size Pointer to store the number of items. Must not be `NULL`. - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `size == NULL` or `*vector == NULL` (not initialized). - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if any parameter is NULL + * @return ESP_ERR_INVALID_STATE if mutex acquisition fails */ esp_err_t zh_vector_get_size(zh_vector_t **vector, uint16_t *size); /** - * @brief Gets the current allocated capacity (maximum number of elements without reallocation). + * @brief Get the current capacity of the vector. * - * @note Thread-safe: internally locks/unlocks the mutex. - * @note Capacity may be larger than the current size (e.g., after deletions), and is reduced - * lazily via _resize() only when size < capacity/2 (or when size becomes 0). - * @note When the vector is empty (size == 0), capacity is guaranteed to be 0. + * @param[in] vector Pointer to the vector (must not be NULL) + * @param[out] capacity Pointer to receive the current capacity (must not be NULL) * - * @param[in] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[out] capacity Pointer to store the allocated capacity. Must not be `NULL`. - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL`, `capacity == NULL`, or `*vector == NULL` (not initialized). - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if any parameter is NULL + * @return ESP_ERR_INVALID_STATE if mutex acquisition fails */ esp_err_t zh_vector_get_capacity(zh_vector_t **vector, uint16_t *capacity); /** - * @brief Adds a copy of the item to the beginning of the vector. + * @brief Insert an element at the beginning of the vector. * - * A copy of `item` is allocated on the heap and stored in the vector. - * The vector owns the lifetime of this copy (freed on `zh_vector_free()` or `zh_vector_delete_item()`). - * All existing items are shifted one position to the right. - * This may involve reallocation of the internal pointer array via `heap_caps_realloc`. + * Shifts all existing elements one position forward and inserts + * a copy of the provided item at index 0. The vector capacity + * will be increased if necessary. * - * @note Memory for the item and potential reallocation is done via `heap_caps_calloc`/`heap_caps_realloc` (`MALLOC_CAP_8BIT`). + * @param[in,out] vector Pointer to the vector (must not be NULL) + * @param[in] item Pointer to the item to insert (must not be NULL) * - * @param[in,out] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[in] item Pointer to the data to copy into the new item. Must not be `NULL`. - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `item == NULL` or `*vector == NULL` (not initialized). - * @return ESP_ERR_NO_MEM if memory allocation fails. - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @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_NO_MEM if vector is full or element allocation fails */ esp_err_t zh_vector_push_front(zh_vector_t **vector, const void *item); /** - * @brief Adds a copy of the item to the end of the vector. + * @brief Append an element to the end of the vector. * - * A copy of `item` is allocated on the heap and stored in the vector. - * The vector owns the lifetime of this copy (freed on `zh_vector_free()` or `zh_vector_delete_item()`). - * This may involve reallocation of the internal pointer array via `heap_caps_realloc`. + * Creates a copy of the provided item and adds it to the end of the vector. + * The vector capacity will be increased if necessary. * - * @note Memory for the item and potential reallocation is done via `heap_caps_calloc`/`heap_caps_realloc` (`MALLOC_CAP_8BIT`). + * @param[in,out] vector Pointer to the vector (must not be NULL) + * @param[in] item Pointer to the item to append (must not be NULL) * - * @param[in,out] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[in] item Pointer to the data to copy into the new item. Must not be `NULL`. - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `item == NULL` or `*vector == NULL` (not initialized). - * @return ESP_ERR_NO_MEM if memory allocation fails. - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @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_NO_MEM if vector is full or element allocation fails */ esp_err_t zh_vector_push_back(zh_vector_t **vector, const void *item); /** - * @brief Replaces the item at the specified index with a copy of the provided data. + * @brief Replace an element at the specified index. * - * @warning Modifies the existing item *in-place* — no new memory allocation occurs. - * Only the data is overwritten (via `memcpy`). The item size must match the vector’s `unit`. + * 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 Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[in] index Index of the item to replace (must be < `(*vector)->size`). - * @param[in] item Pointer to new data to copy into the item. Must not be `NULL`. + * @param[in,out] vector Pointer to the vector (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 `vector == NULL`, `item == NULL`, `index >= (*vector)->size` or `*vector == NULL` (not initialized). - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @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 */ esp_err_t zh_vector_change_item(zh_vector_t **vector, uint16_t index, const void *item); /** - * @brief Retrieves the item at the specified index by copying it into the user-provided buffer. + * @brief Retrieve a copy of the element at the specified index. * - * @note Thread-safe: internally locks/unlocks the mutex. - * @note This function **copies** the element — no pointers are returned or stored. + * @param[in] vector Pointer to the vector (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) * - * @param[in] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[in] index Index of the item to retrieve (0-based). - * @param[out] item Pointer to a buffer of at least `unit` bytes, where the item will be copied. - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL`, `item == NULL`, `index >= (*vector)->size` or `*vector == NULL` (not initialized). - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @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 */ esp_err_t zh_vector_get_item(zh_vector_t **vector, uint16_t index, void *item); /** - * @brief Deletes the item at the specified index and shifts subsequent items one position left. + * @brief Delete the element at the specified index. * - * The deleted item’s memory is freed. - * If the size drops below half of the capacity (and size > 0), the capacity is reduced via `_resize(size)`. - * If the size reaches 0, capacity is set to 0 and the pointer array is freed. + * Frees the element's memory, shifts remaining elements, and updates size. + * Capacity may be reduced if it exceeds twice the current size. * - * @note Thread-safe: internally locks/unlocks the mutex. + * @param[in,out] vector Pointer to the vector (must not be NULL) + * @param[in] index Index of the element to delete (must be < size) * - * @param[in,out] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[in] index Index of the item to delete (must be < `(*vector)->size`). - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL`, `index >= (*vector)->size` or `*vector == NULL` (not initialized). - * @return ESP_ERR_NO_MEM if memory allocation fails. - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). + * @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 */ esp_err_t zh_vector_delete_item(zh_vector_t **vector, uint16_t index); /** - * @brief Removes the last element from the vector. + * @brief Delete the last element of the vector. * - * The element's memory is freed. If the size drops below half of the capacity, - * the capacity is reduced (same behaviour as zh_vector_delete_item). + * Frees the last element's memory and updates size. + * Capacity may be reduced if it exceeds twice the current size. * - * @note Thread-safe: internally locks/unlocks the mutex. + * @param[in,out] vector Pointer to the vector (must not be NULL) * - * @param[in,out] vector Double pointer to vector structure (must be initialized). - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `*vector == NULL` (not initialized) or vector is empty. - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired. + * @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 */ esp_err_t zh_vector_delete_back(zh_vector_t **vector); /** - * @brief Removes the first element from the vector. + * @brief Delete the first element of the vector. * - * The element's memory is freed, and all subsequent elements are shifted left by one. - * If the size drops below half of the capacity, the capacity is reduced (same behaviour as zh_vector_delete_item). + * Frees the first element's memory, shifts remaining elements, and updates size. + * Capacity may be reduced if it exceeds twice the current size. * - * @note Thread-safe: internally locks/unlocks the mutex. + * @param[in,out] vector Pointer to the vector (must not be NULL) * - * @param[in,out] vector Double pointer to vector structure (must be initialized). - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `*vector == NULL` (not initialized) or vector is empty. - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired. + * @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 */ esp_err_t zh_vector_delete_front(zh_vector_t **vector); /** - * @brief Removes duplicate items from the vector, keeping only the first occurrence of each value. + * @brief Remove duplicate elements from the vector. * - * The function compares items byte-by-byte using `memcmp(..., unit)`. All elements that are equal - * to a previously seen element are deleted. The relative order of the remaining elements is preserved. + * Compares elements using memcmp and removes subsequent duplicates. + * Elements are compared based on their raw binary content. * - * @note This operation is O(n^2) in the worst case and may be slow for large vectors. - * @note Memory of duplicate elements is freed, and the vector capacity may be reduced if the size drops - * below half of the capacity (same as in zh_vector_delete_item). - * @note Thread-safe: internally locks/unlocks the mutex. + * @param[in,out] vector Pointer to the vector (must not be NULL) * - * @param[in,out] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * - * @return ESP_OK on success. - * @return ESP_ERR_INVALID_ARG if `vector == NULL` or `*vector == NULL` (not initialized). - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, indicates a system error). - * @return ESP_ERR_NO_MEM if memory allocation fails during capacity reduction. + * @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 */ esp_err_t zh_vector_remove_duplicates(zh_vector_t **vector); /** - * @brief Searches for an item equal to the given data in the vector. + * @brief Find the index of the first matching element. * - * Compares the provided item with each element in the vector using `memcmp`. - * If a match is found, its index (0‑based) is written to `*index` and `ESP_OK` is returned. - * If no match is found, `*index` is set to `-1` and `ESP_ERR_NOT_FOUND` is returned. + * 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. * - * @note Thread-safe: internally locks/unlocks the mutex. - * @note The search is linear O(n). + * @param[in] vector Pointer to the vector (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) * - * @param[in] vector Double pointer to vector structure (`zh_vector_t **`). Must not be `NULL`. - * @param[in] item Pointer to the data to search for. Must not be `NULL`. - * @param[out] index Pointer to store the found index (0‑based) or `-1` if not found. Must not be `NULL`. - * - * @return ESP_OK if the item is found. - * @return ESP_ERR_NOT_FOUND if the item is not present in the vector. - * @return ESP_ERR_INVALID_ARG if any pointer argument is `NULL` or `*vector == NULL` (not initialized). - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired (rare, system error). + * @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_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); /** - * @brief Searches for an item in a vector of structures by comparing a specific field. + * @brief Find an element by comparing a specific field within structures. * - * This function is intended for vectors that store structures (not primitive types). - * It takes a sample structure and a pointer to one of its fields, computes the field offset, - * and then scans the vector starting from the given index. For each element, it compares - * the bytes of the field with the provided value using `memcmp`. If a match is found, - * its index (0‑based) is written to `*index` and `ESP_OK` is returned. If no match is found, - * `*index` is set to `-1` and `ESP_ERR_NOT_FOUND` is returned. + * Searches for an element where a specified field (identified by offset + * and size) matches the provided value. Comparison starts from the + * specified index. * - * @note The vector must contain elements of a structure type. The `sample_struct` and `field` - * pointers must belong to the same structure definition as the elements stored in the vector. - * The function computes the field offset as `(uint8_t*)field - (uint8_t*)sample_struct`. - * @note The `size` parameter must be the exact size in bytes of the field (e.g., `sizeof(sample.id)`). If `size` is 0, the function returns an error. - * @note This function is thread-safe: internally locks/unlocks the mutex for the entire search. - * @note The search is linear O(n) and starts from the given `start` index to the end. - * @note To find all occurrences, call this function repeatedly with an updated `start` index (found index + 1) until `ESP_ERR_NOT_FOUND` is returned. + * @param[in] vector Pointer to the vector 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] 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) * - * @param[in] vector Double pointer to the vector structure (must be initialized). - * @param[in] sample_struct Pointer to a sample structure of the same type as stored in the vector. Used only to compute the field offset. The contents are not used. - * @param[in] field Pointer to the specific field inside the `sample_struct`. - * @param[in] size Size in bytes of the field. Must be > 0. - * @param[in] item Pointer to the value to search for in the field. - * @param[in] start Index (0‑based) from which to start searching. Must be < vector size. - * @param[out] index Pointer to store the found index (0‑based) or `-1` if not found. - * - * @return ESP_OK if a matching element is found (index is set). - * @return ESP_ERR_NOT_FOUND if no element with a matching field value is found. - * @return ESP_ERR_INVALID_ARG if any pointer argument is NULL, `*vector` is NULL (not initialized), `size == 0`, `start >= vector size`, or the field extends beyond the element size. - * @return ESP_ERR_INVALID_STATE if the internal mutex cannot be acquired. + * @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_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 *field, size_t size, const void *item, 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, size_t size, const void *value, uint16_t start, int32_t *index); #ifdef __cplusplus } diff --git a/zh_vector.c b/zh_vector.c index dcd98c3..0942ee5 100644 --- a/zh_vector.c +++ b/zh_vector.c @@ -14,49 +14,67 @@ static const char *TAG = "zh_vector"; } /** - * @brief Internal representation of a thread-safe, dynamically resizing vector of *copied* elements. + * @brief Internal structure representing a dynamic vector. * - * Stores pointers to heap-allocated *copies* of elements (not raw pointers). - * Elements (stored in items[i]) are allocated via heap_caps_calloc(1, unit, ...) and freed via heap_caps_free. - * Supports push front/back, change, delete and size queries. - * Thread safety via FreeRTOS mutex. + * Contains a dynamically allocated array of item pointers, capacity tracking, + * element size, and a FreeRTOS mutex for thread-safe operations. + * Each element in the items array points to separately allocated memory + * containing the actual item data. * - * @note Opaque type: only zh_vector_t (typedef struct _zh_vector_t* zh_vector_t) is exposed. - * @note Elements are deep-copied — ownership of input item is NOT transferred. - * @note All public functions lock the mutex internally — no external locking required. - * Internal functions _resize() and _calc_new_capacity() are not thread-safe and must be called - * only while the vector's mutex is held. - * @note Internal function _resize() guarantees invariant: 0 ≤ size ≤ capacity. - * In particular, _resize(..., 0) always sets size = 0, capacity = 0, and frees all element buffers. - * @note The capacity may exceed size after deletions (memory is not shrunk immediately), - * and is reduced only via _resize() on explicit request (e.g., during delete). - * - * @warning Do not access fields directly — always use public API. - * @warning unit must match the actual element size — mismatch causes heap corruption. - * @note capacity and size are limited to uint16_t (max 65535 elements), to save memory on embedded devices. - * @note items is allocated via heap_caps_realloc as `void*`, but logically treated as `void**`. - * Each valid items[i] (0 ≤ i < size) points to a heap buffer of exactly `unit` bytes (allocated via heap_caps_calloc(1, unit, ...)). - * Only the first `unit` bytes are used for the copied item. - * @warning Actual memory usage depends on allocator overhead and alignment. - * Rough estimate: `sizeof(zh_vector_t) + capacity * sizeof(void*) + size * (unit + item_overhead)`. - * Typical heap overhead per item: ~16 bytes (metadata + alignment). - * @note Capacity reduction is triggered when `size < capacity / 2` (integer division). For example: - * - capacity=4, size=1 → capacity=1 (4/2=2 > 1) - * - capacity=2, size=1 → capacity stays 2 (2/2=1 not > 1) - * - capacity=1, size=0 → capacity=0 (handled separately: size==0 && capacity>0) - * @note When size reaches 0, capacity is always set to 0, even without explicit resize request. + * @note All public operations acquire the mutex before accessing internal state. + * @warning The items array uses separate allocations for each element to support + * variable-sized data structures stored within the vector. */ struct _zh_vector_t { - void **items; /*!< Array of element pointers. items[0..size-1] are valid and non-NULL. Initially NULL; allocated via heap_caps_realloc (as `void*`, cast to `void**`). When capacity > old_capacity, new slots (items[old_capacity..capacity-1]) are zeroed (NULL). */ - uint16_t capacity; /*!< Current allocated capacity (number of slots). Grows on insertion, shrinks on delete (see _resize). Limited to uint16_t (max 65535). */ - uint16_t size; /*!< Current number of valid elements (0 ≤ size ≤ capacity). Always kept in sync: _resize(..., 0) sets size = 0; after push operations size++; after delete size--. */ - uint16_t unit; /*!< Size (in bytes) of a single element. Set once in zh_vector_init() and immutable. Must match the actual element size — mismatch causes heap corruption. */ - SemaphoreHandle_t mutex; /*!< FreeRTOS mutex. Created in zh_vector_init() and deleted in zh_vector_free(). All public functions acquire it before accessing/modifying the vector state. The mutex itself does not affect logical state (`size`, `capacity`). */ + void **items; /*!< Array of pointers to individual item allocations */ + uint16_t capacity; /*!< Current allocated capacity of the items array */ + 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 */ }; +/** + * @brief Resize the vector's internal items array. + * + * Allocates a new array with the specified capacity. When capacity is zero, + * all elements and the array itself are freed. When growing, new slots + * are initialized to NULL. + * + * @param vector Pointer to the vector to resize + * @param capacity New capacity for the items array + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if capacity is less than current size + * @return ESP_ERR_NO_MEM if memory reallocation fails + */ 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. + * + * @param vector Pointer to the vector + * @param index Index of the element to delete + * + * @return ESP_OK on success + * @return ESP_ERR_INVALID_ARG if index is out of bounds + */ static esp_err_t _delete(zh_vector_t *vector, uint16_t index); + +/** + * @brief Calculate the new capacity for the vector when growing. + * + * Returns initial capacity of 4 for empty vectors, UINT16_MAX if doubling + * would overflow, or double the current capacity otherwise. + * + * @param current Current capacity value + * + * @return New capacity value (4, UINT16_MAX, or current * 2) + */ static inline uint16_t _calc_new_capacity(uint16_t current); esp_err_t zh_vector_init(zh_vector_t **vector, uint16_t unit) // -V2008