Compare commits
10
Commits
b61ea6f80b
...
1a1351b60f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a1351b60f | ||
|
|
9e81485984 | ||
|
|
1891599e66 | ||
|
|
288aca9905 | ||
|
|
865acc77a2 | ||
|
|
fb17cd1844 | ||
|
|
f17658bcff | ||
|
|
e0d0a2894d | ||
|
|
1632cb4d28 | ||
|
|
f46ab3f5fb |
No files matched your search
@@ -1,29 +1,20 @@
|
||||
# ESP32 ESP-IDF component for vector (dynamic array)
|
||||
|
||||
## Wiki
|
||||
|
||||
[EN](WIKI_EN.md) | [RU](WIKI_RU.md)
|
||||
|
||||
## Tested on
|
||||
|
||||
1. [ESP32 ESP-IDF v6.0.0](https://docs.espressif.com/projects/esp-idf/en/v6.0/esp32/index.html)
|
||||
|
||||
## SAST Tools
|
||||
|
||||
[PVS-Studio](https://pvs-studio.com/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
|
||||
|
||||
## 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
|
||||
|
||||
In an existing project, run the following command to install the component:
|
||||
|
||||
```text
|
||||
```bash
|
||||
cd ../your_project/components
|
||||
git clone https://github.com/aZholtikov/zh_vector
|
||||
```
|
||||
@@ -36,4 +27,307 @@ In the application, add the component:
|
||||
|
||||
## Examples
|
||||
|
||||
See Wiki [EN](WIKI_EN.md#usage-examples) | [RU](WIKI_RU.md#примеры-использования)
|
||||
### Basic Example: Integer Vector
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
zh_vector_t *vector = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 30;
|
||||
zh_vector_push_front(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
uint16_t size = 0;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Vector size: %u\n", size);
|
||||
}
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int item_value = 0;
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, &item_value);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
printf("Element %d: %d\n", i, item_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error getting element %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
int new_val = 100;
|
||||
zh_vector_change_item(&vector, 1, &new_val);
|
||||
zh_vector_delete_item(&vector, 0);
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
### Struct Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
char name[32];
|
||||
float value;
|
||||
} my_struct_t;
|
||||
|
||||
zh_vector_t *vector = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(my_struct_t));
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
my_struct_t item1 = {1, "Item 1", 1.5f};
|
||||
my_struct_t item2 = {2, "Item 2", 2.5f};
|
||||
zh_vector_push_front(&vector, &item1);
|
||||
zh_vector_push_back(&vector, &item2);
|
||||
my_struct_t item_value = {0};
|
||||
esp_err_t err = zh_vector_get_item(&vector, 0, &item_value);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
item_value.value = 10.5f;
|
||||
zh_vector_change_item(&vector, 0, &item_value);
|
||||
}
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
### String Example (Char Arrays)
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
zh_vector_t *vector = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
esp_err_t ret = zh_vector_init(&vector, 100);
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
char *str1 = malloc(100);
|
||||
char *str2 = malloc(100);
|
||||
if (str1 && str2)
|
||||
{
|
||||
strcpy(str1, "Hello");
|
||||
strcpy(str2, "World");
|
||||
zh_vector_push_back(&vector, str1);
|
||||
zh_vector_push_back(&vector, str2);
|
||||
free(str1);
|
||||
free(str2);
|
||||
}
|
||||
uint16_t size = 0;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
char str_value[100] = {0};
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, str_value);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
printf("String %d: %s\n", i, str_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error getting string %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
zh_vector_remove_duplicates(&vector);
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
### Remove Duplicates Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
zh_vector_t *vector = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 10;
|
||||
int val4 = 30;
|
||||
int val5 = 20;
|
||||
zh_vector_push_back(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
zh_vector_push_back(&vector, &val4);
|
||||
zh_vector_push_back(&vector, &val5);
|
||||
uint16_t size = 0;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Vector size before removing duplicates: %u\n", size);
|
||||
}
|
||||
zh_vector_remove_duplicates(&vector);
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Vector size after removing duplicates: %u\n", size);
|
||||
}
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
int item_value = 0;
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, &item_value);
|
||||
if (err == ESP_OK)
|
||||
{
|
||||
printf("Element %d: %d\n", i, item_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Error getting element %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Find Item Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
zh_vector_t *vector = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 30;
|
||||
zh_vector_push_back(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
int search_item = 20;
|
||||
int32_t found_index = -1;
|
||||
ret = zh_vector_find_item(&vector, &search_item, &found_index);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Element %d found at index %d\n", search_item, (int)found_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Element %d not found (index=%d)\n", search_item, (int)found_index);
|
||||
}
|
||||
search_item = 100;
|
||||
found_index = -1;
|
||||
ret = zh_vector_find_item(&vector, &search_item, &found_index);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Element %d found at index %d\n", search_item, (int)found_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Element %d not found (index=%d)\n", search_item, (int)found_index);
|
||||
}
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Find Item in Field Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int id;
|
||||
char name[32];
|
||||
float value;
|
||||
} my_struct_t;
|
||||
|
||||
zh_vector_t *vector = NULL;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(my_struct_t));
|
||||
if (ret != ESP_OK)
|
||||
{
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
my_struct_t item1 = {1, "Item 1", 1.5f};
|
||||
my_struct_t item2 = {2, "Item 2", 2.5f};
|
||||
my_struct_t item3 = {3, "Item 3", 3.5f};
|
||||
zh_vector_push_back(&vector, &item1);
|
||||
zh_vector_push_back(&vector, &item2);
|
||||
zh_vector_push_back(&vector, &item3);
|
||||
int search_id = 2;
|
||||
int32_t found_index = -1;
|
||||
my_struct_t sample = {0};
|
||||
ret = zh_vector_find_item_in_field(&vector, &sample, &sample.id, sizeof(sample.id), &search_id, 0, &found_index);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Struct with id=%d found at index %d\n", search_id, (int)found_index);
|
||||
my_struct_t found_item = {0};
|
||||
ret = zh_vector_get_item(&vector, (uint16_t)found_index, &found_item);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Found item: id=%d, name=%s, value=%.1f\n", found_item.id, found_item.name, found_item.value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Struct with id=%d not found (index=%d)\n", search_id, (int)found_index);
|
||||
}
|
||||
search_id = 100;
|
||||
found_index = -1;
|
||||
ret = zh_vector_find_item_in_field(&vector, &sample, &sample.id, sizeof(sample.id), &search_id, 0, &found_index);
|
||||
if (ret == ESP_OK)
|
||||
{
|
||||
printf("Struct with id=%d found at index %d\n", search_id, (int)found_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Struct with id=%d not found (index=%d)\n", search_id, (int)found_index);
|
||||
}
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
-699
@@ -1,699 +0,0 @@
|
||||
# zh_vector - Vector (Dynamic Array) Component for ESP-IDF
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [API Reference](#api-reference)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Technical Specifications](#technical-specifications)
|
||||
- [Error Codes](#error-codes)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
`zh_vector` is a lightweight, efficient vector (dynamic array) component for ESP-IDF (Espressif IoT Development Framework). It provides a flexible container that can store elements of any data type with automatic memory management. The vector automatically resizes as items are added or removed, making it ideal for dynamic data management in embedded systems.
|
||||
|
||||
The component is designed specifically for ESP32 microcontrollers and uses ESP-IDF's heap management functions for optimal memory allocation with different memory caps (internal/external RAM).
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Type Agnostic**: Supports any data type (integers, floats, structs, custom types, etc.)
|
||||
- **Automatic Memory Management**: Automatic memory allocation and deallocation
|
||||
- **Dynamic Resizing**: Vector capacity grows and shrinks as needed
|
||||
- **Maximum Capacity**: Up to 65,535 elements (16-bit index limit)
|
||||
- **ESP-IDF Optimized**: Uses heap_caps functions for memory allocation with memory caps
|
||||
- **Error Handling**: Comprehensive error checking with detailed logging
|
||||
- **Thread-Safe**: Thread-safe (uses FreeRTOS mutex)
|
||||
- **Minimal Overhead**: Low memory and CPU overhead
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Navigate to your project's components directory:
|
||||
|
||||
```bash
|
||||
cd ../your_project/components
|
||||
```
|
||||
|
||||
Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/aZholtikov/zh_vector
|
||||
```
|
||||
|
||||
In your application, include the header:
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
```
|
||||
|
||||
The component will be automatically built with your project.
|
||||
|
||||
---
|
||||
|
||||
## API Reference
|
||||
|
||||
All functions in this library use double pointer (`zh_vector_t **`) for the vector parameter to allow for proper memory management and thread-safe operations.
|
||||
|
||||
### zh_vector_t Structure
|
||||
|
||||
The structure is declared as `typedef struct _zh_vector_t zh_vector_t;` and encapsulates internal implementation details.
|
||||
|
||||
**Fields (internal):**
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `items` | `void **` | Array of element pointers. Items[0..size-1] are valid. Allocated via heap_caps_calloc, reallocated via heap_caps_realloc. |
|
||||
| `capacity` | `uint16_t` | Current allocated capacity (number of slots). Grows on insertion - may exceed size after deletions. |
|
||||
| `size` | `uint16_t` | Current number of elements (0 ≤ size ≤ capacity). |
|
||||
| `unit` | `uint16_t` | Size (in bytes) of a single element. Set once in zh_vector_init() and immutable. |
|
||||
| `mutex` | `SemaphoreHandle_t` | FreeRTOS mutex. Created in zh_vector_init(), deleted in zh_vector_free. Automatically locked/unlocked in public functions. |
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_init()
|
||||
|
||||
Initializes the vector.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). If the vector pointer is NULL, memory will be allocated.
|
||||
- `unit` - Size of each element in bytes
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or zero unit size)
|
||||
- `ESP_ERR_INVALID_STATE` - Vector already initialized
|
||||
- `ESP_ERR_NO_MEM` - Memory allocation failed
|
||||
|
||||
**Example:**
|
||||
|
||||
```c
|
||||
zh_vector_t *vector = NULL;
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int)); // For integers
|
||||
if (ret != ESP_OK) {
|
||||
// Handle error
|
||||
}
|
||||
// Don't forget to free: zh_vector_free(&vector);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_free()
|
||||
|
||||
Deinitializes the vector and frees all allocated memory. Sets the vector pointer to NULL.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or NULL vector)
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
**Note:** All dynamically allocated element memory is also freed. The vector pointer is set to NULL after deinitialization.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_get_capacity()
|
||||
|
||||
Gets the current allocated capacity of the vector (maximum number of elements without reallocation).
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `capacity` - Pointer to variable to store the capacity. Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or capacity pointer)
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
**Note:** Capacity may be larger than the current size (e.g., after deletions), and is reduced lazily when size < capacity/2 (or when size becomes 0).
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_get_size()
|
||||
|
||||
Gets the current number of elements in the vector.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `size` - Pointer to variable to store the size. Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or size pointer)
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_push_front()
|
||||
|
||||
Adds an element to the beginning of the vector.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `item` - Pointer to the element to add. Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or item pointer)
|
||||
- `ESP_ERR_NO_MEM` - Memory allocation failed
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
**Note:** The function allocates memory for a copy of the item and copies the data. All existing elements are shifted right by one position.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_push_back()
|
||||
|
||||
Adds an element to the end of the vector.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `item` - Pointer to the element to add. Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or item pointer)
|
||||
- `ESP_ERR_NO_MEM` - Memory allocation failed
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
**Note:** The function allocates memory for a copy of the item and copies the data.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_change_item()
|
||||
|
||||
Changes an element at a specific index.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `index` - Index of the element to change (0-based). Must be < vector size.
|
||||
- `item` - Pointer to the new element data. Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `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)
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_get_item()
|
||||
|
||||
Retrieves an element at a specific index by copying it into the user-provided buffer.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `index` - Index of the element to get (0-based).
|
||||
- `item` - Pointer to a buffer of at least `unit` bytes where the element will be copied. Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer, NULL item pointer, or invalid index)
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
**Note:** This function copies the element data into the provided buffer. No pointers are returned.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_delete_item()
|
||||
|
||||
Deletes an element at a specific index and shifts all subsequent elements.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `index` - Index of the element to delete (0-based). Must be < vector size.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or invalid index)
|
||||
- `ESP_ERR_NO_MEM` - Memory allocation failed (during reallocation)
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
**Note:** All elements after the deleted index are shifted left by one position. The deleted item's memory is freed.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_find_item()
|
||||
|
||||
Finds the first occurrence of an element in the vector.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
- `item` - Pointer to the element to find. Must not be NULL.
|
||||
- `index` - Pointer to variable to store the found index. Will be set to -1 if element is not found. Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success (element found)
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer, NULL item pointer, or NULL index pointer)
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
- `ESP_ERR_NOT_FOUND` - Element not found (index set to -1)
|
||||
|
||||
**Note:** Uses memcmp to compare elements. Returns the index of the first occurrence. The index variable is set to -1 if the element is not found.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_find_item_in_field()
|
||||
|
||||
Finds the first occurrence of a value in a specific field within structures stored in the vector.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `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.
|
||||
- `size` - Size (in bytes) of the field to compare. Must be > 0.
|
||||
- `item` - 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.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success (value found)
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL pointer, zero size, or invalid start index)
|
||||
- `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.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_remove_duplicates()
|
||||
|
||||
Removes duplicate elements from the vector, keeping only the first occurrence of each element.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `vector` - Pointer to pointer to vector structure (`zh_vector_t **`). Must not be NULL.
|
||||
|
||||
**Returns:**
|
||||
|
||||
- `ESP_OK` - Success
|
||||
- `ESP_ERR_INVALID_ARG` - Invalid argument (NULL vector pointer or NULL vector)
|
||||
- `ESP_ERR_INVALID_STATE` - Failed to acquire mutex (rare system error)
|
||||
|
||||
**Note:** Uses memcmp to compare elements. Only the first occurrence of each unique element is preserved. The function preserves the order of first occurrences.
|
||||
|
||||
---
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Example: Integer Vector
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Initialize vector for integers
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
// Add elements
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 30;
|
||||
zh_vector_push_front(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
size_t size;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Vector size: %zu\n", size);
|
||||
}
|
||||
// Access elements
|
||||
for (int i = 0; i < size; i++) {
|
||||
int item_value;
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, &item_value);
|
||||
if (err == ESP_OK) {
|
||||
printf("Element %d: %d\n", i, item_value);
|
||||
} else {
|
||||
printf("Error getting element %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
// Change element
|
||||
int new_val = 100;
|
||||
zh_vector_change_item(&vector, 1, &new_val);
|
||||
// Delete element
|
||||
zh_vector_delete_item(&vector, 0);
|
||||
// Cleanup
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Struct Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[32];
|
||||
float value;
|
||||
} my_struct_t;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Initialize vector for structs
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(my_struct_t));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
// Add struct elements
|
||||
my_struct_t item1 = {1, "Item 1", 1.5f};
|
||||
my_struct_t item2 = {2, "Item 2", 2.5f};
|
||||
zh_vector_push_front(&vector, &item1);
|
||||
zh_vector_push_back(&vector, &item2);
|
||||
// Access and modify
|
||||
my_struct_t item_value;
|
||||
esp_err_t err = zh_vector_get_item(&vector, 0, &item_value);
|
||||
if (err == ESP_OK) {
|
||||
item_value.value = 10.5f;
|
||||
zh_vector_change_item(&vector, 0, &item_value);
|
||||
}
|
||||
// Cleanup
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### String Example (Char Arrays)
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
char buffer[100] = {0};
|
||||
// Initialize vector for strings (100 char max)
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(buffer));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
// Add strings
|
||||
strcpy(buffer, "Hello");
|
||||
zh_vector_push_front(&vector, &buffer);
|
||||
strcpy(buffer, "World");
|
||||
zh_vector_push_back(&vector, &buffer);
|
||||
size_t size;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
// Print all strings
|
||||
for (int i = 0; i < size; i++) {
|
||||
char str_value[100];
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, str_value);
|
||||
if (err == ESP_OK) {
|
||||
printf("String %d: %s\n", i, str_value);
|
||||
} else {
|
||||
printf("Error getting string %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove duplicates (if any)
|
||||
zh_vector_remove_duplicates(&vector);
|
||||
// Cleanup
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Remove Duplicates Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Initialize vector for integers
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
// Add elements with duplicates
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 10;
|
||||
int val4 = 30;
|
||||
int val5 = 20;
|
||||
zh_vector_push_back(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
zh_vector_push_back(&vector, &val4);
|
||||
zh_vector_push_back(&vector, &val5);
|
||||
uint16_t size;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Vector size before removing duplicates: %zu\n", size);
|
||||
}
|
||||
// Remove duplicates
|
||||
zh_vector_remove_duplicates(&vector);
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Vector size after removing duplicates: %zu\n", size);
|
||||
}
|
||||
// Access remaining elements
|
||||
for (int i = 0; i < size; i++) {
|
||||
int item_value;
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, &item_value);
|
||||
if (err == ESP_OK) {
|
||||
printf("Element %d: %d\n", i, item_value);
|
||||
} else {
|
||||
printf("Error getting element %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
// Cleanup
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Find Item Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Initialize vector for integers
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
// Add elements
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 30;
|
||||
zh_vector_push_back(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
// Find element
|
||||
int search_item = 20;
|
||||
int16_t found_index = -1;
|
||||
ret = zh_vector_find_item(&vector, &search_item, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Element %d found at index %d\n", search_item, found_index);
|
||||
} else {
|
||||
printf("Element %d not found (index=%d)\n", search_item, found_index);
|
||||
}
|
||||
// Find non-existing element
|
||||
search_item = 100;
|
||||
found_index = -1;
|
||||
ret = zh_vector_find_item(&vector, &search_item, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Element %d found at index %d\n", search_item, found_index);
|
||||
} else {
|
||||
printf("Element %d not found (index=%d)\n", search_item, found_index);
|
||||
}
|
||||
// Cleanup
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Find Item in Field Example
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[32];
|
||||
float value;
|
||||
} my_struct_t;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Initialize vector for structs
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(my_struct_t));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Vector initialization error\n");
|
||||
return;
|
||||
}
|
||||
// Add struct elements
|
||||
my_struct_t item1 = {1, "Item 1", 1.5f};
|
||||
my_struct_t item2 = {2, "Item 2", 2.5f};
|
||||
my_struct_t item3 = {3, "Item 3", 3.5f};
|
||||
zh_vector_push_back(&vector, &item1);
|
||||
zh_vector_push_back(&vector, &item2);
|
||||
zh_vector_push_back(&vector, &item3);
|
||||
// Find struct by field value (searching by id field)
|
||||
int search_id = 2;
|
||||
int16_t found_index = -1;
|
||||
// Calculate offset of 'id' field within the structure
|
||||
my_struct_t sample;
|
||||
ret = zh_vector_find_item_in_field(&vector, &sample, &sample.id, sizeof(sample.id), &search_id, 0, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Struct with id=%d found at index %d\n", search_id, found_index);
|
||||
// Get the found struct
|
||||
my_struct_t found_item;
|
||||
ret = zh_vector_get_item(&vector, (uint16_t)found_index, &found_item);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Found item: id=%d, name=%s, value=%.1f\n", found_item.id, found_item.name, found_item.value);
|
||||
}
|
||||
} else {
|
||||
printf("Struct with id=%d not found (index=%d)\n", search_id, found_index);
|
||||
}
|
||||
// Find non-existing id
|
||||
search_id = 100;
|
||||
found_index = -1;
|
||||
ret = zh_vector_find_item_in_field(&vector, &sample, &sample.id, sizeof(sample.id), &search_id, 0, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Struct with id=%d found at index %d\n", search_id, found_index);
|
||||
} else {
|
||||
printf("Struct with id=%d not found (index=%d)\n", search_id, found_index);
|
||||
}
|
||||
// Cleanup
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Technical Specifications
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| **Maximum Capacity** | 65,535 elements |
|
||||
| **Index Type** | uint16_t (16-bit) |
|
||||
| **Memory Management** | heap_caps_calloc, heap_caps_realloc, heap_caps_free |
|
||||
| **Memory Caps** | MALLOC_CAP_8BIT |
|
||||
| **Thread Safety** | Thread-safe (uses FreeRTOS mutex) |
|
||||
| **ESP-IDF Version** | >= 5.0 |
|
||||
| **Platform** | ESP32 series |
|
||||
| **Language** | C (C99) |
|
||||
|
||||
---
|
||||
|
||||
## Error Codes
|
||||
|
||||
| Error Code | Description |
|
||||
|------------|-------------|
|
||||
| `ESP_OK` | Operation successful |
|
||||
| `ESP_ERR_INVALID_ARG` | Invalid argument (NULL pointer or zero size) |
|
||||
| `ESP_ERR_INVALID_STATE` | Failed to acquire mutex (rare system error) |
|
||||
| `ESP_ERR_NO_MEM` | Memory allocation failed (out of memory) |
|
||||
| `ESP_ERR_NOT_FOUND` | Element not found (for zh_vector_find_item) |
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! To contribute:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
Please ensure your code follows the existing style and includes appropriate documentation.
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License, Version 2.0 - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
### Apache License, Version 2.0
|
||||
|
||||
Copyright (c) 2026 Alexey Zholtikov
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
---
|
||||
|
||||
## Additional Notes
|
||||
|
||||
- **Memory Overhead**: Each element requires additional memory for the pointer in the internal pointer array
|
||||
- **Performance**: O(1) for get/set by index, O(n) for insert/delete in the middle
|
||||
- **Best Practices**:
|
||||
- Always initialize the vector before use
|
||||
- Free the vector when done to avoid memory leaks
|
||||
- Consider the maximum size limit (65,535 elements)
|
||||
- For string handling, consider using fixed-size buffers
|
||||
|
||||
---
|
||||
|
||||
*Generated for zh_vector v2.4.0*
|
||||
-699
@@ -1,699 +0,0 @@
|
||||
# zh_vector - Компонент вектора (динамического массива) для ESP-IDF
|
||||
|
||||
## Содержание
|
||||
|
||||
- [Обзор](#обзор)
|
||||
- [Возможности](#возможности)
|
||||
- [Установка](#установка)
|
||||
- [Справочник API](#справочник-api)
|
||||
- [Примеры использования](#примеры-использования)
|
||||
- [Технические характеристики](#технические-характеристики)
|
||||
- [Коды ошибок](#коды-ошибок)
|
||||
- [Вклад в проект](#вклад-в-проект)
|
||||
- [Лицензия](#лицензия)
|
||||
|
||||
---
|
||||
|
||||
## Обзор
|
||||
|
||||
`zh_vector` — это легковесный и эффективный компонент вектора (динамического массива) для ESP-IDF (Espressif IoT Development Framework). Он предоставляет гибкий контейнер, который может хранить элементы любого типа данных с автоматическим управлением памятью. Вектор автоматически изменяет размер при добавлении или удалении элементов, что делает его идеальным для динамического управления данными во встраиваемых системах.
|
||||
|
||||
Компонент разработан специально для микроконтроллеров ESP32 и использует функции управления кучей ESP-IDF для оптимального выделения памяти с различными параметрами памяти (внутренняя/внешняя RAM).
|
||||
|
||||
---
|
||||
|
||||
## Возможности
|
||||
|
||||
- **Независимость от типа данных**: Поддерживает любой тип данных (целые числа, числа с плавающей точкой, структуры, пользовательские типы и т.д.)
|
||||
- **Автоматическое управление памятью**: Автоматическое выделение и освобождение памяти
|
||||
- **Динамическое изменение размера**: Емкость вектора увеличивается и уменьшается по мере необходимости
|
||||
- **Максимальная емкость**: До 65 535 элементов (ограничение 16-битного индекса)
|
||||
- **Оптимизировано под ESP-IDF**: Использует функции heap_caps для выделения памяти с параметрами памяти
|
||||
- **Обработка ошибок**: Комплексная проверка ошибок с детальным логированием
|
||||
- **Потокобезопасность**: Является потокобезопасным (использует FreeRTOS mutex)
|
||||
- **Минимальные накладные расходы**: Низкие накладные расходы на память и процессор
|
||||
|
||||
---
|
||||
|
||||
## Установка
|
||||
|
||||
Перейдите в каталог компонентов вашего проекта:
|
||||
|
||||
```bash
|
||||
cd ../ваш_проект/components
|
||||
```
|
||||
|
||||
Клонируйте репозиторий:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/aZholtikov/zh_vector
|
||||
```
|
||||
|
||||
В вашем приложении подключите заголовочный файл:
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
```
|
||||
|
||||
Компонент будет автоматически собран вместе с вашим проектом.
|
||||
|
||||
---
|
||||
|
||||
## Справочник API
|
||||
|
||||
Все функции в этой библиотеке используют double pointer (`zh_vector_t **`) для параметра вектора, чтобы обеспечить правильное управление памятью и потокобезопасные операции.
|
||||
|
||||
### Структура zh_vector_t
|
||||
|
||||
Структура объявляется как `typedef struct _zh_vector_t zh_vector_t;` и инкапсулирует внутренние детали реализации.
|
||||
|
||||
**Поля (внутренние):**
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
|------|-----|----------|
|
||||
| `items` | `void **` | Массив указателей на элементы вектора. Items[0..size-1] являются валидными. Выделяется через heap_caps_calloc, перераспределяется через heap_caps_realloc. |
|
||||
| `capacity` | `uint16_t` | Текущая выделенная емкость (количество слотов). Увеличивается при вставке — может превышать size после удалений. |
|
||||
| `size` | `uint16_t` | Текущее количество элементов (0 ≤ size ≤ capacity). |
|
||||
| `unit` | `uint16_t` | Размер (в байтах) одного элемента. Устанавливается при инициализации и не должен меняться. |
|
||||
| `mutex` | `SemaphoreHandle_t` | FreeRTOS mutex. Создается в zh_vector_init, удаляется в zh_vector_free. Автоматически блокируется/разблокируется в публичных функциях. |
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_init()
|
||||
|
||||
Инициализирует вектор.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Если указатель на вектор равен NULL, будет выделена память.
|
||||
- `unit` - Размер каждого элемента в байтах
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на вектор или нулевой размер unit)
|
||||
- `ESP_ERR_INVALID_STATE` - Вектор уже инициализирован
|
||||
- `ESP_ERR_NO_MEM` - Ошибка выделения памяти
|
||||
|
||||
**Пример:**
|
||||
|
||||
```c
|
||||
zh_vector_t *vector = NULL;
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int)); // Для целых чисел
|
||||
if (ret != ESP_OK) {
|
||||
// Обработка ошибки
|
||||
}
|
||||
// Не забудьте освободить: zh_vector_free(&vector);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_free()
|
||||
|
||||
Деинициализирует вектор и освобождает всю выделенную память. Устанавливает указатель на вектор в NULL.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или NULL указатель на вектор)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
**Примечание:** Вся динамически выделенная память элементов также освобождается. Указатель на вектор устанавливается в NULL после деинициализации.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_get_capacity()
|
||||
|
||||
Получает текущую выделенную емкость вектора (максимальное количество элементов без перераспределения).
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `capacity` - Указатель на переменную для сохранения емкости. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или capacity)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
**Примечание:** Емкость может быть больше текущего размера (например, после удалений), и уменьшается лениво при size < capacity/2 (или когда size становится 0).
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_get_size()
|
||||
|
||||
Получает текущее количество элементов в векторе.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `size` - Указатель на переменную для сохранения размера. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или size)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_push_front()
|
||||
|
||||
Добавляет элемент в начало вектора.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `item` - Указатель на элемент для добавления. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или item)
|
||||
- `ESP_ERR_NO_MEM` - Ошибка выделения памяти
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
**Примечание:** Функция выделяет память для копии элемента и копирует данные. Все существующие элементы сдвигаются на одну позицию вправо.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_push_back()
|
||||
|
||||
Добавляет элемент в конец вектора.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `item` - Указатель на элемент для добавления. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или item)
|
||||
- `ESP_ERR_NO_MEM` - Ошибка выделения памяти
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
**Примечание:** Функция выделяет память для копии элемента и копирует данные.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_change_item()
|
||||
|
||||
Изменяет элемент по определенному индексу.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `index` - Индекс элемента для изменения (начиная с 0). Должен быть < размера вектора.
|
||||
- `item` - Указатель на новые данные элемента. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор, item или неверный индекс)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_get_item()
|
||||
|
||||
Получает элемент по определенному индексу, копируя его в предоставленный буфер пользователя.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `index` - Индекс элемента для получения (начиная с 0).
|
||||
- `item` - Указатель на буфер размером не менее `unit` байт, куда будет скопирован элемент. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор, NULL указатель item или неверный индекс)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
**Примечание:** Эта функция копирует данные элемента в предоставленный буфер. Указатели не возвращаются.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_delete_item()
|
||||
|
||||
Удаляет элемент по определенному индексу и сдвигает все последующие элементы.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `index` - Индекс элемента для удаления (начиная с 0). Должен быть < размера вектора.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или неверный индекс)
|
||||
- `ESP_ERR_NO_MEM` - Ошибка выделения памяти (при перераспределении)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
**Примечание:** Все элементы после удаляемого индекса сдвигаются влево на одну позицию. Память удаленного элемента освобождается.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_find_item()
|
||||
|
||||
Находит первое вхождение элемента в векторе.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `item` - Указатель на элемент для поиска. Не должен быть NULL.
|
||||
- `index` - Указатель на переменную для сохранения найденного индекса. Будет установлен в -1, если элемент не найден. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех (элемент найден)
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на вектор, NULL указатель item или NULL указатель index)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
- `ESP_ERR_NOT_FOUND` - Элемент не найден (index установлен в -1)
|
||||
|
||||
**Примечание:** Использует memcmp для сравнения элементов. Возвращает индекс первого вхождения. Переменная index устанавливается в -1, если элемент не найден.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_find_item_in_field()
|
||||
|
||||
Находит первое вхождение значения в определенном поле внутри структур, хранящихся в векторе.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
- `sample_struct` - Указатель на образец структуры того же типа, что и хранится в векторе. Не должен быть NULL.
|
||||
- `field` - Указатель на поле внутри структуры для поиска. Должен быть действительным адресом члена внутри sample_struct.
|
||||
- `size` - Размер (в байтах) поля для сравнения. Должен быть > 0.
|
||||
- `item` - Указатель на значение для поиска. Не должен быть NULL.
|
||||
- `start` - Начальный индекс для поиска (начиная с 0). Должен быть < размера вектора.
|
||||
- `index` - Указатель на переменную для сохранения найденного индекса. Будет установлен в -1, если значение не найдено. Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех (значение найдено)
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель, нулевой размер или неверный начальный индекс)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
- `ESP_ERR_NOT_FOUND` - Значение не найдено (index установлен в -1)
|
||||
|
||||
**Примечание:** Эта функция вычисляет смещение поля внутри структуры, используя sample_struct и item указатели, затем ищет указанное значение в этом поле во всех элементах вектора, начиная с заданного индекса. Использует memcmp для сравнения. Полезно для поиска в векторах структур без копирования всей структуры.
|
||||
|
||||
---
|
||||
|
||||
### zh_vector_remove_duplicates()
|
||||
|
||||
Удаляет дублирующиеся элементы из вектора, оставляя только первое вхождение каждого элемента.
|
||||
|
||||
**Параметры:**
|
||||
|
||||
- `vector` - Указатель на указатель на структуру вектора (`zh_vector_t **`). Не должен быть NULL.
|
||||
|
||||
**Возвращает:**
|
||||
|
||||
- `ESP_OK` - Успех
|
||||
- `ESP_ERR_INVALID_ARG` - Неверный аргумент (NULL указатель на указатель на вектор или NULL указатель на вектор)
|
||||
- `ESP_ERR_INVALID_STATE` - Не удалось захватить mutex (редкий системный сбой)
|
||||
|
||||
**Примечание:** Использует memcmp для сравнения элементов. Сохраняется только первое вхождение каждого уникального элемента. Функция сохраняет порядок первых вхождений.
|
||||
|
||||
---
|
||||
|
||||
## Примеры использования
|
||||
|
||||
### Базовый пример: Вектор целых чисел
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Инициализация вектора для целых чисел
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Ошибка инициализации вектора\n");
|
||||
return;
|
||||
}
|
||||
// Добавление элементов
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 30;
|
||||
zh_vector_push_front(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
size_t size;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Размер вектора: %zu\n", size);
|
||||
}
|
||||
// Доступ к элементам
|
||||
for (int i = 0; i < size; i++) {
|
||||
int item_value;
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, &item_value);
|
||||
if (err == ESP_OK) {
|
||||
printf("Элемент %d: %d\n", i, item_value);
|
||||
} else {
|
||||
printf("Ошибка получения элемента %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
// Изменение элемента
|
||||
int new_val = 100;
|
||||
zh_vector_change_item(&vector, 1, &new_val);
|
||||
// Удаление элемента
|
||||
zh_vector_delete_item(&vector, 0);
|
||||
// Очистка
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Пример со структурой
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[32];
|
||||
float value;
|
||||
} my_struct_t;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Инициализация вектора для структур
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(my_struct_t));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Ошибка инициализации вектора\n");
|
||||
return;
|
||||
}
|
||||
// Добавление элементов-структур
|
||||
my_struct_t item1 = {1, "Item 1", 1.5f};
|
||||
my_struct_t item2 = {2, "Item 2", 2.5f};
|
||||
zh_vector_push_front(&vector, &item1);
|
||||
zh_vector_push_back(&vector, &item2);
|
||||
// Доступ и модификация
|
||||
my_struct_t item_value;
|
||||
esp_err_t err = zh_vector_get_item(&vector, 0, &item_value);
|
||||
if (err == ESP_OK) {
|
||||
item_value.value = 10.5f;
|
||||
zh_vector_change_item(&vector, 0, &item_value);
|
||||
}
|
||||
// Очистка
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Пример со строками (массивы символов)
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
char buffer[100] = {0};
|
||||
// Инициализация вектора для строк (макс. 100 символов)
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(buffer));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Ошибка инициализации вектора\n");
|
||||
return;
|
||||
}
|
||||
// Добавление стро��
|
||||
strcpy(buffer, "Привет");
|
||||
zh_vector_push_front(&vector, &buffer);
|
||||
strcpy(buffer, "Мир");
|
||||
zh_vector_push_back(&vector, &buffer);
|
||||
size_t size;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
// Печать всех строк
|
||||
for (int i = 0; i < size; i++) {
|
||||
char str_value[100];
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, str_value);
|
||||
if (err == ESP_OK) {
|
||||
printf("Строка %d: %s\n", i, str_value);
|
||||
} else {
|
||||
printf("Ошибка получения строки %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Удаление дубликатов (если есть)
|
||||
zh_vector_remove_duplicates(&vector);
|
||||
// Очистка
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Пример удаления дубликатов
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Инициализация вектора для целых чисел
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Ошибка инициализации вектора\n");
|
||||
return;
|
||||
}
|
||||
// Добавление элементов с дубликатами
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 10;
|
||||
int val4 = 30;
|
||||
int val5 = 20;
|
||||
zh_vector_push_back(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
zh_vector_push_back(&vector, &val4);
|
||||
zh_vector_push_back(&vector, &val5);
|
||||
uint16_t size;
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Размер вектора перед удалением дубликатов: %zu\n", size);
|
||||
}
|
||||
// Удаление дубликатов
|
||||
zh_vector_remove_duplicates(&vector);
|
||||
ret = zh_vector_get_size(&vector, &size);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Размер вектора после удаления дубликатов: %zu\n", size);
|
||||
}
|
||||
// Доступ к оставшимся элементам
|
||||
for (int i = 0; i < size; i++) {
|
||||
int item_value;
|
||||
esp_err_t err = zh_vector_get_item(&vector, (uint16_t)i, &item_value);
|
||||
if (err == ESP_OK) {
|
||||
printf("Элемент %d: %d\n", i, item_value);
|
||||
} else {
|
||||
printf("Ошибка получения элемента %d: %s\n", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
// Очистка
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Пример поиска элемента
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Инициализация вектора для целых чисел
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(int));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Ошибка инициализации вектора\n");
|
||||
return;
|
||||
}
|
||||
// Добавление элементов
|
||||
int val1 = 10;
|
||||
int val2 = 20;
|
||||
int val3 = 30;
|
||||
zh_vector_push_back(&vector, &val1);
|
||||
zh_vector_push_back(&vector, &val2);
|
||||
zh_vector_push_back(&vector, &val3);
|
||||
// Поиск элемента
|
||||
int search_item = 20;
|
||||
int16_t found_index = -1;
|
||||
ret = zh_vector_find_item(&vector, &search_item, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Элемент %d найден по индексу %d\n", search_item, found_index);
|
||||
} else {
|
||||
printf("Элемент %d не найден (index=%d)\n", search_item, found_index);
|
||||
}
|
||||
// Поиск несуществующего элемента
|
||||
search_item = 100;
|
||||
found_index = -1;
|
||||
ret = zh_vector_find_item(&vector, &search_item, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Элемент %d найден по индексу %d\n", search_item, found_index);
|
||||
} else {
|
||||
printf("Элемент %d не найден (index=%d)\n", search_item, found_index);
|
||||
}
|
||||
// Очистка
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Пример поиска элемента по полю
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[32];
|
||||
float value;
|
||||
} my_struct_t;
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR);
|
||||
zh_vector_t *vector = NULL;
|
||||
// Инициализация вектора для структур
|
||||
esp_err_t ret = zh_vector_init(&vector, sizeof(my_struct_t));
|
||||
if (ret != ESP_OK) {
|
||||
printf("Ошибка инициализации вектора\n");
|
||||
return;
|
||||
}
|
||||
// Добавление элементов-структур
|
||||
my_struct_t item1 = {1, "Item 1", 1.5f};
|
||||
my_struct_t item2 = {2, "Item 2", 2.5f};
|
||||
my_struct_t item3 = {3, "Item 3", 3.5f};
|
||||
zh_vector_push_back(&vector, &item1);
|
||||
zh_vector_push_back(&vector, &item2);
|
||||
zh_vector_push_back(&vector, &item3);
|
||||
// Поиск структуры по значению поля (поиск по полю id)
|
||||
int search_id = 2;
|
||||
int16_t found_index = -1;
|
||||
// Вычисление смещения поля 'id' внутри структуры
|
||||
my_struct_t sample;
|
||||
ret = zh_vector_find_item_in_field(&vector, &sample, &sample.id, sizeof(sample.id), &search_id, 0, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Структура с id=%d найдена по индексу %d\n", search_id, found_index);
|
||||
// Получение найденной структуры
|
||||
my_struct_t found_item;
|
||||
ret = zh_vector_get_item(&vector, (uint16_t)found_index, &found_item);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Найденный элемент: id=%d, name=%s, value=%.1f\n", found_item.id, found_item.name, found_item.value);
|
||||
}
|
||||
} else {
|
||||
printf("Структура с id=%d не найдена (index=%d)\n", search_id, found_index);
|
||||
}
|
||||
// Поиск несуществующего id
|
||||
search_id = 100;
|
||||
found_index = -1;
|
||||
ret = zh_vector_find_item_in_field(&vector, &sample, &sample.id, sizeof(sample.id), &search_id, 0, &found_index);
|
||||
if (ret == ESP_OK) {
|
||||
printf("Структура с id=%d найдена по индексу %d\n", search_id, found_index);
|
||||
} else {
|
||||
printf("Структура с id=%d не найдена (index=%d)\n", search_id, found_index);
|
||||
}
|
||||
// Очистка
|
||||
zh_vector_free(&vector);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Технические характеристики
|
||||
|
||||
| Параметр | Значение |
|
||||
|----------|----------|
|
||||
| **Максимальная емкость** | 65 535 элементов |
|
||||
| **Тип индекса** | uint16_t (16-бит) |
|
||||
| **Управление памятью** | heap_caps_calloc, heap_caps_realloc, heap_caps_free |
|
||||
| **Параметры памяти** | MALLOC_CAP_8BIT |
|
||||
| **Потокобезопасность** | Является потокобезопасным (использует FreeRTOS mutex) |
|
||||
| **Версия ESP-IDF** | >= 5.0 |
|
||||
| **Платформа** | Серия ESP32 |
|
||||
| **Язык** | C (C99) |
|
||||
|
||||
---
|
||||
|
||||
## Коды ошибок
|
||||
|
||||
| Код ошибки | Описание |
|
||||
|------------|----------|
|
||||
| `ESP_OK` | Операция выполнена успешно |
|
||||
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL указатель или нулевой размер) |
|
||||
| `ESP_ERR_INVALID_STATE` | Не удалось захватить mutex (редкий системный сбой) |
|
||||
| `ESP_ERR_NO_MEM` | Ошибка выделения памяти (не хватает памяти) |
|
||||
| `ESP_ERR_NOT_FOUND` | Элемент не найден (для zh_vector_find_item) |
|
||||
|
||||
---
|
||||
|
||||
## Вклад в проект
|
||||
|
||||
Вклад приветствуется! Чтобы внести свой вклад:
|
||||
|
||||
1. Сделайте форк репозитория
|
||||
2. Создайте ветку функции (`git checkout -b feature/AmazingFeature`)
|
||||
3. Закоммитьте ваши изменения (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. Отправьте в ветку (`git push origin feature/AmazingFeature`)
|
||||
5. Откройте Pull Request
|
||||
|
||||
Пожалуйста, убедитесь, что ваш код следует существующему стилю и включает соответствующую документацию.
|
||||
|
||||
---
|
||||
|
||||
## Лицензия
|
||||
|
||||
Этот проект лицензирован по лицензии Apache, версия 2.0 - см. файл [LICENSE](LICENSE) для подробной информации.
|
||||
|
||||
### Apache License, Version 2.0
|
||||
|
||||
Авторское право (c) 2026 Алексей Жолтиков
|
||||
|
||||
Лицензировано по лицензии Apache License, Version 2.0 (далее — "Лицензия");
|
||||
вы не можете использовать этот файл, кроме случаев, предусмотренных Лицензией.
|
||||
Копию Лицензии можно получить по адресу:
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Если иное не требуется действующим законодательством или не согласовано в письменном виде,
|
||||
программное обеспечение, распространяемое по Лицензии, распространяется на условиях "КАК ЕСТЬ",
|
||||
БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, явных или подразумеваемых, включая, но не ограничиваясь, гарантии
|
||||
ТОВАРНОГО СОСТОЯНИЯ, ПРИГОДНОСТИ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ И НЕНАРУШЕНИЯ ПРАВ.
|
||||
Смотрите Лицензию для получения конкретных прав и ограничений.
|
||||
|
||||
---
|
||||
|
||||
## Дополнительные заметки
|
||||
|
||||
- **Накладные расходы памяти**: Каждый элемент требует дополнительной памяти для указателя во внутреннем массиве указателей
|
||||
- **Производительность**: O(1) для получения/установки по индексу, O(n) для вставки/удаления в середине
|
||||
- **Лучшие практики**:
|
||||
- Всегда инициализируйте вектор перед использованием
|
||||
- Освобождайте вектор по завершении, чтобы избежать утечек памяти
|
||||
- Учитывайте ограничение максимального размера (65 535 элементов)
|
||||
- Для обработки строк рассмотрите использование буферов фиксированного размера
|
||||
|
||||
---
|
||||
|
||||
*Сгенерировано для zh_vector v2.4.0*
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
description: ESP32 ESP-IDF component for vector (dynamic array)
|
||||
url: https://github.com/aZholtikov/zh_vector
|
||||
version: "2.4.0"
|
||||
version: "2.5.1"
|
||||
license: "Apache-2.0"
|
||||
maintainers:
|
||||
- "Alexey Zholtikov <alexey.zholtikov@yandex.ru>"
|
||||
|
||||
+161
-162
@@ -1,19 +1,29 @@
|
||||
/**
|
||||
* @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
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stddef.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_heap_caps.h"
|
||||
@@ -26,244 +36,233 @@ 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.
|
||||
* The vector is marked as deleting and the function waits until all
|
||||
* operations in progress finish (reference counter drops to zero).
|
||||
*
|
||||
* @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 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 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 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 pointer to the vector handle (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 the vector is being deleted or 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 pointer to the vector handle (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 the vector is being deleted or 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 pointer to the vector handle (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 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);
|
||||
|
||||
/**
|
||||
* @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 pointer to the vector handle (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 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);
|
||||
|
||||
/**
|
||||
* @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 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 `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 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 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 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)
|
||||
*
|
||||
* @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 any parameter is NULL or index is out of bounds
|
||||
* @return ESP_ERR_INVALID_STATE if the vector is being deleted or mutex acquisition fails
|
||||
*
|
||||
* @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).
|
||||
* @note The element at the specified index must exist and be non-NULL.
|
||||
* This is guaranteed if the vector was properly initialized and no
|
||||
* internal corruption occurred.
|
||||
*/
|
||||
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 pointer to the vector handle (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 the vector is being deleted or mutex acquisition fails
|
||||
*/
|
||||
esp_err_t zh_vector_delete_item(zh_vector_t **vector, uint16_t index);
|
||||
|
||||
/**
|
||||
* @brief Removes duplicate items from the vector, keeping only the first occurrence of each value.
|
||||
* @brief Delete the last element of 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.
|
||||
* Frees the last element's memory and updates size.
|
||||
* Capacity may be reduced if it exceeds twice the current size.
|
||||
*
|
||||
* @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 pointer to the vector handle (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 or *vector is NULL or vector is empty
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Delete the first element of the vector.
|
||||
*
|
||||
* @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.
|
||||
* 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 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 the vector is being deleted or mutex acquisition fails
|
||||
*/
|
||||
esp_err_t zh_vector_delete_front(zh_vector_t **vector);
|
||||
|
||||
/**
|
||||
* @brief Remove duplicate elements from the vector.
|
||||
*
|
||||
* Compares elements using memcmp and removes subsequent duplicates.
|
||||
* Elements are compared based on their raw binary content.
|
||||
*
|
||||
* @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 the vector is being deleted or 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 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)
|
||||
*
|
||||
* @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 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, int16_t *index);
|
||||
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. 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.
|
||||
*
|
||||
* @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 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 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)
|
||||
*
|
||||
* @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, 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 *field, size_t size, const void *item, uint16_t start, int16_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
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
2.4.0
|
||||
2.5.1
|
||||
+217
@@ -0,0 +1,217 @@
|
||||
# Описание компонента zh_vector
|
||||
|
||||
## Общие сведения
|
||||
|
||||
**zh_vector** — это компонент для ESP-IDF (фреймворк для программирования микроконтроллеров ESP32), реализующий динамический вектор (динамический массив) с автоматическим управлением памятью и потокобезопасностью.
|
||||
|
||||
Компонент предоставляет универсальную структуру данных «вектор», которая может хранить элементы любого типа через указатели `void`, автоматически увеличивает и уменьшает свою ёмкость, а также обеспечивает потокобезопасную работу с помощью мьютексов FreeRTOS.
|
||||
|
||||
## Основные возможности
|
||||
|
||||
1. **Универсальность типов** — поддержка любых типов данных через указатели `void`.
|
||||
2. **Динамическая ёмкость** — автоматическое увеличение и уменьшение выделенной памяти.
|
||||
3. **Максимальная ёмкость** — до 65 535 элементов (используется 16-битный индекс `uint16_t`).
|
||||
4. **Потокобезопасность** — все операции защищены мьютексом FreeRTOS.
|
||||
5. **Автоматическое управление памятью** — используется ESP-IDF функция `heap_caps` для работы с кучей ESP32.
|
||||
6. **Гибкая вставка** — поддержка вставки элементов как в начало, так и в конец вектора.
|
||||
7. **Удаление дубликатов и поиск** — возможность удаления дубликатов и поиска элементов по содержимому или по полю внутри структуры.
|
||||
|
||||
## Внутреннее устройство
|
||||
|
||||
### Структура `zh_vector_t`
|
||||
|
||||
Внутренняя структура вектора содержит следующие поля:
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
|------|-----|----------|
|
||||
| `items` | `void **` | Массив указателей, каждый из которых указывает на отдельно выделенную память для фактических данных элемента |
|
||||
| `capacity` | `uint16_t` | Текущая выделенная ёмкость массива `items` (максимум 65 535) |
|
||||
| `size` | `uint16_t` | Текущее количество элементов в векторе |
|
||||
| `unit` | `uint16_t` | Размер одного элемента в байтах |
|
||||
| `mutex` | `SemaphoreHandle_t` | Мьютекс FreeRTOS для потокобезопасности |
|
||||
| `lock` | `portMUX_TYPE` | Секция критической защиты для флагов удаления и счётчика ссылок |
|
||||
| `deleting` | `bool` | Флаг, указывающий, что вектор удаляется и новые операции должны быть отклонены |
|
||||
| `refcount` | `uint16_t` | Счётчик операций, находящихся в процессе выполнения |
|
||||
|
||||
### Управление памятью
|
||||
|
||||
Каждый элемент вектора хранится как **отдельное выделение памяти**. Это означает, что:
|
||||
- Массив `items` содержит указатели на элементы.
|
||||
- Каждый элемент выделяется отдельно через `heap_caps_calloc()`.
|
||||
- При удалении элемента его память освобождается через `heap_caps_free()`.
|
||||
- При удалении всего вектора последовательно освобождаются все элементы, затем массив `items` и сама структура `zh_vector_t`.
|
||||
|
||||
## API (интерфейс)
|
||||
|
||||
### Инициализация и освобождение
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_vector_init()` | Инициализирует новый вектор. Принимает указатель на указатель вектора и размер элемента в байтах. Создаёт структуру, выделяет нулевую ёмкость и создаёт мьютекс. |
|
||||
| `zh_vector_free()` | Освобождает все ресурсы: элементы, массив, мьютекс и структуру. Устанавливает флаг `deleting` и ожидает завершения всех текущих операций (до тех пор, пока `refcount` не станет нулём). |
|
||||
|
||||
### Получение информации
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_vector_get_size()` | Возвращает текущее количество элементов в векторе. |
|
||||
| `zh_vector_get_capacity()` | Возвращает текущую выделенную ёмкость (максимальное количество элементов, для которого выделена память). |
|
||||
|
||||
### Вставка элементов
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_vector_push_front()` | Вставляет копию элемента в начало вектора (индекс 0). Все существующие элементы сдвигаются на одну позицию вправо. Если ёмкость исчерпана, массив увеличивается. |
|
||||
| `zh_vector_push_back()` | Вставляет копию элемента в конец вектора. Если ёмкость исчерпана, массив увеличивается. |
|
||||
|
||||
### Доступ и модификация
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_vector_get_item()` | Копирует элемент по указанному индексу в предоставленный буфер. |
|
||||
| `zh_vector_change_item()` | Заменяет содержимое элемента по указанному индексу новыми данными. |
|
||||
|
||||
### Удаление элементов
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_vector_delete_item()` | Удаляет элемент по указанному индексу. Освобождает память элемента, сдвигает оставшиеся элементы влево. Обновляет `size`. |
|
||||
| `zh_vector_delete_back()` | Удаляет последний элемент вектора. |
|
||||
| `zh_vector_delete_front()` | Удаляет первый элемент вектора. Сдвигает оставшиеся элементы влево. |
|
||||
|
||||
### Поиск и очистка
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `zh_vector_remove_duplicates()` | Удаляет дубликаты элементов. Сравнивает элементы побайтово через `memcmp`. При обнаружении дубликата он удаляется. |
|
||||
| `zh_vector_find_item()` | Ищет первый элемент, побайтово совпадающий с заданным. Возвращает индекс или `-1`, если элемент не найден. |
|
||||
| `zh_vector_find_item_in_field()` | Ищет элемент по значению конкретного поля внутри структуры. Позволяет искать по любому полю структуры, вычисляя смещение поля от начала структуры. |
|
||||
|
||||
## Потокобезопасность
|
||||
|
||||
Все публичные функции работают с вектором через двухуровневую систему синхронизации:
|
||||
|
||||
1. **`_zh_vector_take()` / `_zh_vector_give()`** — механизм счётчика ссылок.
|
||||
- Перед началом любой операции вызывается `_zh_vector_take()`, который увеличивает `refcount` и проверяет, что вектор не удаляется (флаг `deleting` равен `false`).
|
||||
- После завершения операции вызывается `_zh_vector_give()`, который уменьшает `refcount`.
|
||||
- Функция `zh_vector_free()` устанавливает `deleting = true` и ожидает, пока `refcount` не станет нулём. Это гарантирует, что все текущие операции завершатся перед освобождением памяти.
|
||||
|
||||
2. **Мьютекс FreeRTOS** — защищает внутренние данные вектора (`items`, `capacity`, `size`) от одновременного доступа из разных потоков.
|
||||
|
||||
3. **Критическая секция (`portMUX_TYPE`)** — используется для атомарных операций со счётчиком ссылок и флагом удаления (на уровне одного ядра, без блокировки других ядер).
|
||||
|
||||
## Алгоритмы изменения ёмкости
|
||||
|
||||
### Увеличение ёмкости
|
||||
|
||||
- При первой вставке элемента начальная ёмкость устанавливается в **4 элемента**.
|
||||
- При каждом последующем заполнении ёмкость удваивается (`capacity * 2`).
|
||||
- Максимальная ёмкость ограничена значением `UINT16_MAX` (65 535).
|
||||
|
||||
### Уменьшение ёмкости
|
||||
|
||||
- Ёмкость уменьшается при удалении элемента, если:
|
||||
- `capacity > 4` и `capacity / 2 >= size * 2` (то есть выделенная память больше нужной более чем в 2 раза).
|
||||
- Новая ёмкость рассчитывается как `size * 2`, но не менее 4.
|
||||
- При достижении `size == 0` вся память полностью освобождается.
|
||||
|
||||
## Примеры использования
|
||||
|
||||
### Базовый пример (целые числа)
|
||||
|
||||
```c
|
||||
zh_vector_t *vector = NULL;
|
||||
zh_vector_init(&vector, sizeof(int));
|
||||
|
||||
int val1 = 10, val2 = 20, val3 = 30;
|
||||
zh_vector_push_back(vector, &val1);
|
||||
zh_vector_push_back(vector, &val2);
|
||||
zh_vector_push_back(vector, &val3);
|
||||
|
||||
// Получение элемента
|
||||
int item_value = 0;
|
||||
zh_vector_get_item(vector, 1, &item_value); // item_value = 20
|
||||
|
||||
// Изменение элемента
|
||||
int new_val = 100;
|
||||
zh_vector_change_item(vector, 1, &new_val); // элемент по индексу 1 теперь = 100
|
||||
|
||||
// Удаление элемента
|
||||
zh_vector_delete_item(vector, 0); // удалён элемент со значением 10
|
||||
|
||||
// Очистка
|
||||
zh_vector_free(&vector);
|
||||
```
|
||||
|
||||
### Работа со структурами
|
||||
|
||||
```c
|
||||
typedef struct {
|
||||
int id;
|
||||
char name[32];
|
||||
float value;
|
||||
} my_struct_t;
|
||||
|
||||
zh_vector_t *vector = NULL;
|
||||
zh_vector_init(&vector, sizeof(my_struct_t));
|
||||
|
||||
my_struct_t item1 = {1, "Item 1", 1.5f};
|
||||
zh_vector_push_back(vector, &item1);
|
||||
|
||||
// Поиск по полю структуры
|
||||
int search_id = 1;
|
||||
int32_t found_index = -1;
|
||||
my_struct_t sample = {0};
|
||||
zh_vector_find_item_in_field(vector, &sample, &sample.id, sizeof(sample.id), &search_id, 0, &found_index);
|
||||
// found_index = 0, так как элемент с id=1 находится по индексу 0
|
||||
|
||||
zh_vector_free(&vector);
|
||||
```
|
||||
|
||||
## Логирование
|
||||
|
||||
Компонент использует ESP-IDF логирование с тегом `"zh_vector"`. Уровень логирования можно настроить:
|
||||
|
||||
```c
|
||||
esp_log_level_set("zh_vector", ESP_LOG_ERROR); // только ошибки
|
||||
```
|
||||
|
||||
## Ошибки
|
||||
|
||||
Все функции возвращают `esp_err_t`. Основные коды ошибок:
|
||||
|
||||
| Код | Описание |
|
||||
|-----|----------|
|
||||
| `ESP_OK` | Успех |
|
||||
| `ESP_ERR_INVALID_ARG` | Неверный аргумент (NULL-указатель, некорректный индекс и т.д.) |
|
||||
| `ESP_ERR_INVALID_STATE` | Неверное состояние (вектор удаляется, мьютекс не получен) |
|
||||
| `ESP_ERR_NO_MEM` | Не удалось выделить память |
|
||||
| `ESP_ERR_NOT_FOUND` | Элемент не найден |
|
||||
|
||||
## Ограничения
|
||||
|
||||
1. **Максимум 65 535 элементов** — из-за использования `uint16_t` для индексов и ёмкости.
|
||||
2. **Побайтовое сравнение** — `memcmp` сравнивает сырые байты, что может не учитывать выравнивание структур.
|
||||
3. **Копирование данных** — все операции вставки и получения копируют данные, что может быть неэффективно для очень больших элементов.
|
||||
4. **Зависимость от ESP-IDF** — компонент работает только в среде ESP-IDF с FreeRTOS.
|
||||
|
||||
## Требования для сборки
|
||||
|
||||
- ESP-IDF (фреймворк для ESP32)
|
||||
- FreeRTOS (входит в состав ESP-IDF)
|
||||
- Компонент должен находиться в директории `components/zh_vector` проекта
|
||||
|
||||
## Установка
|
||||
|
||||
Для добавления компонента в проект:
|
||||
|
||||
```bash
|
||||
cd ваш_проект/components
|
||||
git clone https://github.com/aZholtikov/zh_vector
|
||||
```
|
||||
|
||||
Затем в коде подключить заголовочный файл:
|
||||
|
||||
```c
|
||||
#include "zh_vector.h"
|
||||
```
|
||||
+258
-107
@@ -13,73 +13,114 @@ static const char *TAG = "zh_vector";
|
||||
return err; \
|
||||
}
|
||||
|
||||
#define ZH_ERROR_CHECK_VOID(cond, cleanup, msg, ...) \
|
||||
if (!(cond)) \
|
||||
{ \
|
||||
ZH_LOGE(msg, ESP_FAIL, ##__VA_ARGS__); \
|
||||
cleanup; \
|
||||
return; \
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
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.
|
||||
*
|
||||
* 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. 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
|
||||
*
|
||||
* @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
|
||||
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;
|
||||
}
|
||||
@@ -89,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)
|
||||
@@ -113,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;
|
||||
}
|
||||
@@ -124,183 +184,263 @@ 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;
|
||||
}
|
||||
|
||||
esp_err_t zh_vector_push_front(zh_vector_t **vector, const void *item) // -V2008
|
||||
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); _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];
|
||||
}
|
||||
(*vector)->items[0] = heap_caps_calloc(1, (*vector)->unit, MALLOC_CAP_8BIT);
|
||||
ZH_ERROR_CHECK((*vector)->items[0] != NULL, ESP_ERR_NO_MEM, xSemaphoreGive((*vector)->mutex), "Adding item to beginning of vector failed. Element allocation failed.");
|
||||
(*vector)->items[0] = new_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;
|
||||
}
|
||||
|
||||
esp_err_t zh_vector_push_back(zh_vector_t **vector, const void *item) // -V2008
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t zh_vector_change_item(zh_vector_t **vector, uint16_t index, const void *item) // -V2008
|
||||
esp_err_t zh_vector_change_item(zh_vector_t **vector, uint16_t index, const void *item)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t zh_vector_get_item(zh_vector_t **vector, uint16_t index, void *item) // -V2008
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t zh_vector_delete_item(zh_vector_t **vector, uint16_t index) // -V2008
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t zh_vector_remove_duplicates(zh_vector_t **vector) // -V2008
|
||||
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(_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;
|
||||
}
|
||||
|
||||
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(_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t zh_vector_find_item(zh_vector_t **vector, const void *item, int16_t *index)
|
||||
esp_err_t zh_vector_find_item(zh_vector_t **vector, const void *item, int32_t *index)
|
||||
{
|
||||
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 = (int16_t)i;
|
||||
*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, int16_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)
|
||||
{
|
||||
*index = (int16_t)i;
|
||||
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;
|
||||
@@ -335,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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user