Files
2026-10-17 09:08:58 +03:00

218 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Описание компонента 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"
```