diff --git a/WIKI_EN.md b/WIKI_EN.md index 775a1a8..30961b3 100644 --- a/WIKI_EN.md +++ b/WIKI_EN.md @@ -30,7 +30,7 @@ The component is designed specifically for ESP32 microcontrollers and uses ESP-I 4. **Maximum Capacity**: Up to 65,535 elements (16-bit index limit) 5. **ESP-IDF Optimized**: Uses heap_caps functions for memory allocation with memory caps 6. **Error Handling**: Comprehensive error checking with detailed logging -7. **Thread-Safe**: Not thread-safe by default (can be made thread-safe with external mutex) +7. **Thread-Safe**: Thread-safe (uses FreeRTOS mutex) 8. **Minimal Overhead**: Low memory and CPU overhead --- @@ -66,11 +66,12 @@ git clone https://github.com/aZholtikov/zh_vector ```c typedef struct { - void **items; // Array of pointers of vector items - uint16_t capacity; // Maximum capacity of the vector - uint16_t size; // Number of items in the vector - uint16_t unit; // Vector item size (in bytes) - bool is_initialized; // Vector initialization status flag + void **items; // Array of pointers of vector items + uint16_t capacity; // Maximum capacity of the vector + uint16_t size; // Number of items in the vector + uint16_t unit; // Vector item size (in bytes) + bool is_initialized; // Vector initialization status flag + SemaphoreHandle_t mutex; // FreeRTOS mutex for thread safety } zh_vector_t; ``` @@ -337,7 +338,7 @@ void app_main(void) | **Index Type** | uint16_t (16-bit) | | **Memory Management** | heap_caps_calloc, heap_caps_realloc, heap_caps_free | | **Memory Caps** | MALLOC_CAP_8BIT | -| **Thread Safety** | Not thread-safe | +| **Thread Safety** | Thread-safe (uses FreeRTOS mutex) | | **ESP-IDF Version** | >= 5.0 | | **Platform** | ESP32 series | | **Language** | C (C99) | @@ -404,4 +405,4 @@ limitations under the License. --- -*Generated for zh_vector v1.2.0* +*Generated for zh_vector v1.3.0* diff --git a/WIKI_RU.md b/WIKI_RU.md index 460d74d..32af9fd 100644 --- a/WIKI_RU.md +++ b/WIKI_RU.md @@ -30,7 +30,7 @@ 4. **Максимальная емкость**: До 65 535 элементов (ограничение 16-битного индекса) 5. **Оптимизировано под ESP-IDF**: Использует функции heap_caps для выделения памяти с параметрами памяти 6. **Обработка ошибок**: Комплексная проверка ошибок с детальным логированием -7. **Потокобезопасность**: Не является потокобезопасным по умолчанию (можно сделать потокобезопасным с внешним мьютексом) +7. **Потокобезопасность**: Является потокобезопасным (использует FreeRTOS mutex) 8. **Минимальные накладные расходы**: Низкие накладные расходы на память и процессор --- @@ -66,11 +66,12 @@ git clone https://github.com/aZholtikov/zh_vector ```c typedef struct { - void **items; // Массив указателей на элементы вектора - uint16_t capacity; // Максимальная емкость вектора - uint16_t size; // Количество элементов в векторе - uint16_t unit; // Размер элемента вектора (в байтах) - bool is_initialized; // Флаг состояния инициализации вектора + void **items; // Массив указателей на элементы вектора + uint16_t capacity; // Максимальная емкость вектора + uint16_t size; // Количество элементов в векторе + uint16_t unit; // Размер элемента вектора (в байтах) + bool is_initialized; // Флаг состояния инициализации вектора + SemaphoreHandle_t mutex; // FreeRTOS mutex для потокобезопасности } zh_vector_t; ``` @@ -337,7 +338,7 @@ void app_main(void) | **Тип индекса** | uint16_t (16-бит) | | **Управление памятью** | heap_caps_calloc, heap_caps_realloc, heap_caps_free | | **Параметры памяти** | MALLOC_CAP_8BIT | -| **Потокобезопасность** | Не является потокобезопасным | +| **Потокобезопасность** | Является потокобезопасным (использует FreeRTOS mutex) | | **Версия ESP-IDF** | >= 5.0 | | **Платформа** | Серия ESP32 | | **Язык** | C (C99) | @@ -404,4 +405,4 @@ void app_main(void) --- -*Сгенерировано для zh_vector v1.2.0* +*Сгенерировано для zh_vector v1.3.0* diff --git a/idf_component.yml b/idf_component.yml index a513dba..863eb52 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,6 +1,6 @@ description: ESP32 ESP-IDF component for vector (dynamic array) url: https://github.com/aZholtikov/zh_vector -version: "1.2.0" +version: "1.3.0" license: "Apache-2.0" maintainers: - "Alexey Zholtikov " diff --git a/include/zh_vector.h b/include/zh_vector.h index e38ca91..257c28f 100644 --- a/include/zh_vector.h +++ b/include/zh_vector.h @@ -8,6 +8,8 @@ #include "esp_err.h" #include "esp_log.h" #include "esp_heap_caps.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" #ifdef __cplusplus extern "C" @@ -19,11 +21,12 @@ extern "C" */ typedef struct { - void **items; /*!< Array of pointers of vector items. */ - uint16_t capacity; /*!< Maximum capacity of the vector. @note Used to control the size of allocated memory for array of pointers of vector items. Usually equal to the current number of items in the vector. Automatically changes when items are added or deleted. */ - uint16_t size; /*!< Number of items in the vector. */ - uint16_t unit; /*!< Vector item size. */ - bool is_initialized; /*!< Vector initialization status flag. */ + void **items; /*!< Array of pointers of vector items. */ + uint16_t capacity; /*!< Maximum capacity of the vector. @note Used to control the size of allocated memory for array of pointers of vector items. Usually equal to the current number of items in the vector. Automatically changes when items are added or deleted. */ + uint16_t size; /*!< Number of items in the vector. */ + uint16_t unit; /*!< Vector item size. */ + bool is_initialized; /*!< Vector initialization status flag. */ + SemaphoreHandle_t mutex; /*!< FreeRTOS mutex. */ } zh_vector_t; /** diff --git a/version.txt b/version.txt index 867e524..589268e 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.2.0 \ No newline at end of file +1.3.0 \ No newline at end of file diff --git a/zh_vector.c b/zh_vector.c index b1cb59d..685ffa7 100644 --- a/zh_vector.c +++ b/zh_vector.c @@ -20,6 +20,8 @@ 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->is_initialized == false, ESP_ERR_INVALID_STATE, NULL, "Vector initialization failed. Vector is already initialized."); + vector->mutex = xSemaphoreCreateMutex(); + ZH_ERROR_CHECK(vector->mutex != NULL, ESP_ERR_NO_MEM, NULL, "Vector initialization failed. Failed to create mutex."); vector->items = NULL; vector->capacity = 0; vector->size = 0; @@ -34,6 +36,7 @@ esp_err_t zh_vector_free(zh_vector_t *vector) ZH_LOGI("Vector deletion begin."); ZH_ERROR_CHECK(vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Vector deletion failed. Invalid argument."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Vector deletion fail. Vector not initialized."); + ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Vector deletion fail. Failed to acquire mutex."); for (uint16_t i = 0; i < vector->size; ++i) { if (vector->items[i] != NULL) @@ -46,6 +49,8 @@ esp_err_t zh_vector_free(zh_vector_t *vector) vector->items = NULL; vector->size = 0; vector->capacity = 0; + vSemaphoreDelete(vector->mutex); + vector->mutex = NULL; vector->is_initialized = false; ZH_LOGI("Vector deletion success."); return ESP_OK; @@ -56,8 +61,11 @@ esp_err_t zh_vector_get_size(zh_vector_t *vector) ZH_LOGI("Getting vector size begin."); ZH_ERROR_CHECK(vector != NULL, ESP_FAIL, NULL, "Getting vector size fail. Invalid argument."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_FAIL, NULL, "Getting vector size fail. Vector not initialized."); + ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Getting vector size fail. Failed to acquire mutex."); + size_t size = vector->size; + xSemaphoreGive(vector->mutex); ZH_LOGI("Getting vector size success."); - return vector->size; + return (esp_err_t)size; } esp_err_t zh_vector_push_front(zh_vector_t *vector, void *item) // -V2008 @@ -65,18 +73,20 @@ esp_err_t zh_vector_push_front(zh_vector_t *vector, void *item) // -V2008 ZH_LOGI("Adding item to beginning of vector begin."); ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to beginning of vector fail. Invalid argument."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to beginning of vector fail. Vector not initialized."); + ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Adding item to beginning of vector fail. Failed to acquire mutex."); if (vector->capacity == vector->size) { - ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, NULL, "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap."); + ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap."); } for (uint16_t i = vector->size; i > 0; --i) { vector->items[i] = vector->items[i - 1]; } vector->items[0] = heap_caps_calloc(1, vector->unit, MALLOC_CAP_8BIT); - ZH_ERROR_CHECK(vector->items[0] != NULL, ESP_ERR_NO_MEM, NULL, "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap."); + ZH_ERROR_CHECK(vector->items[0] != NULL, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to beginning of vector fail. Memory allocation fail or no free memory in the heap."); memcpy(vector->items[0], item, vector->unit); vector->size++; + xSemaphoreGive(vector->mutex); ZH_LOGI("Adding item to beginning of vector success."); return ESP_OK; } @@ -86,31 +96,35 @@ esp_err_t zh_vector_push_back(zh_vector_t *vector, void *item) // -V2008 ZH_LOGI("Adding item to vector begin."); ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Adding item to vector fail. Invalid argument."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Adding item to vector fail. Vector not initialized."); + ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Adding item to vector fail. Failed to acquire mutex."); if (vector->capacity == vector->size) { - ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, NULL, "Adding item to vector fail. Memory allocation fail or no free memory in the heap."); + ZH_ERROR_CHECK(_resize(vector, vector->capacity + 1) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to vector fail. Memory allocation fail or no free memory in the heap."); } 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, NULL, "Adding item to vector fail. Memory allocation fail or no free memory in the heap."); + ZH_ERROR_CHECK(vector->items[idx] != NULL, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Adding item to vector fail. Memory allocation fail or no free memory in the heap."); memcpy(vector->items[idx], item, vector->unit); vector->size++; + xSemaphoreGive(vector->mutex); ZH_LOGI("Adding item to vector success."); return ESP_OK; } -esp_err_t zh_vector_change_item(zh_vector_t *vector, uint16_t index, void *item) +esp_err_t zh_vector_change_item(zh_vector_t *vector, uint16_t index, void *item) // -V2008 { ZH_LOGI("Changing item in vector begin."); ZH_ERROR_CHECK(vector != NULL && item != NULL, ESP_ERR_INVALID_ARG, NULL, "Changing item in vector fail. Invalid argument."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Changing item in vector fail. Vector not initialized."); + ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Changing item in vector fail. Failed to acquire mutex."); ZH_ERROR_CHECK(index < vector->size, ESP_FAIL, NULL, "Changing item in vector fail. Index does not exist."); memcpy(vector->items[index], item, vector->unit); + xSemaphoreGive(vector->mutex); ZH_LOGI("Changing item in vector success."); return ESP_OK; } -void *zh_vector_get_item(zh_vector_t *vector, uint16_t index) +void *zh_vector_get_item(zh_vector_t *vector, uint16_t index) // -V2008 { ZH_LOGI("Getting item from vector begin."); if (vector == NULL) @@ -123,13 +137,22 @@ void *zh_vector_get_item(zh_vector_t *vector, uint16_t index) ZH_LOGE("Getting item from vector fail. Vector not initialized.", ESP_ERR_INVALID_STATE); return NULL; } + if (xSemaphoreTake(vector->mutex, portMAX_DELAY) != pdTRUE) + { + ZH_LOGE("Getting item from vector fail. Failed to acquire mutex.", ESP_FAIL); + return NULL; + } + void *item = NULL; if (index >= vector->size) { + xSemaphoreGive(vector->mutex); ZH_LOGE("Getting item from vector fail. Index does not exist.", ESP_FAIL); return NULL; } + item = vector->items[index]; + xSemaphoreGive(vector->mutex); ZH_LOGI("Getting item from vector success."); - return vector->items[index]; + return item; } esp_err_t zh_vector_delete_item(zh_vector_t *vector, uint16_t index) // -V2008 @@ -137,6 +160,7 @@ esp_err_t zh_vector_delete_item(zh_vector_t *vector, uint16_t index) // -V2008 ZH_LOGI("Deleting item in vector begin."); ZH_ERROR_CHECK(vector != NULL, ESP_ERR_INVALID_ARG, NULL, "Deleting item in vector fail. Invalid argument."); ZH_ERROR_CHECK(vector->is_initialized == true, ESP_ERR_INVALID_STATE, NULL, "Deleting item in vector fail. Vector not initialized."); + ZH_ERROR_CHECK(xSemaphoreTake(vector->mutex, portMAX_DELAY) == pdTRUE, ESP_FAIL, NULL, "Deleting item in vector fail. Failed to acquire mutex."); ZH_ERROR_CHECK(index < vector->size, ESP_FAIL, NULL, "Deleting item in vector fail. Index does not exist."); void *freed_item = vector->items[index]; for (uint16_t i = index; i < (vector->size - 1); ++i) @@ -147,8 +171,9 @@ esp_err_t zh_vector_delete_item(zh_vector_t *vector, uint16_t index) // -V2008 vector->items[--vector->size] = NULL; if (vector->capacity > vector->size) { - ZH_ERROR_CHECK(_resize(vector, vector->size) == ESP_OK, ESP_ERR_NO_MEM, NULL, "Deleting item in vector fail. Memory allocation fail or no free memory in the heap."); + ZH_ERROR_CHECK(_resize(vector, vector->size) == ESP_OK, ESP_ERR_NO_MEM, xSemaphoreGive(vector->mutex), "Deleting item in vector fail. Memory allocation fail or no free memory in the heap."); } + xSemaphoreGive(vector->mutex); ZH_LOGI("Deleting item in vector success."); return ESP_OK; }