218 lines
14 KiB
Markdown
218 lines
14 KiB
Markdown
# Описание компонента 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"
|
||
```
|