14 KiB
Описание компонента zh_vector
Общие сведения
zh_vector — это компонент для ESP-IDF (фреймворк для программирования микроконтроллеров ESP32), реализующий динамический вектор (динамический массив) с автоматическим управлением памятью и потокобезопасностью.
Компонент предоставляет универсальную структуру данных «вектор», которая может хранить элементы любого типа через указатели void, автоматически увеличивает и уменьшает свою ёмкость, а также обеспечивает потокобезопасную работу с помощью мьютексов FreeRTOS.
Основные возможности
- Универсальность типов — поддержка любых типов данных через указатели
void. - Динамическая ёмкость — автоматическое увеличение и уменьшение выделенной памяти.
- Максимальная ёмкость — до 65 535 элементов (используется 16-битный индекс
uint16_t). - Потокобезопасность — все операции защищены мьютексом FreeRTOS.
- Автоматическое управление памятью — используется ESP-IDF функция
heap_capsдля работы с кучей ESP32. - Гибкая вставка — поддержка вставки элементов как в начало, так и в конец вектора.
- Удаление дубликатов и поиск — возможность удаления дубликатов и поиска элементов по содержимому или по полю внутри структуры.
Внутреннее устройство
Структура 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() |
Ищет элемент по значению конкретного поля внутри структуры. Позволяет искать по любому полю структуры, вычисляя смещение поля от начала структуры. |
Потокобезопасность
Все публичные функции работают с вектором через двухуровневую систему синхронизации:
-
_zh_vector_take()/_zh_vector_give()— механизм счётчика ссылок.- Перед началом любой операции вызывается
_zh_vector_take(), который увеличиваетrefcountи проверяет, что вектор не удаляется (флагdeletingравенfalse). - После завершения операции вызывается
_zh_vector_give(), который уменьшаетrefcount. - Функция
zh_vector_free()устанавливаетdeleting = trueи ожидает, покаrefcountне станет нулём. Это гарантирует, что все текущие операции завершатся перед освобождением памяти.
- Перед началом любой операции вызывается
-
Мьютекс FreeRTOS — защищает внутренние данные вектора (
items,capacity,size) от одновременного доступа из разных потоков. -
Критическая секция (
portMUX_TYPE) — используется для атомарных операций со счётчиком ссылок и флагом удаления (на уровне одного ядра, без блокировки других ядер).
Алгоритмы изменения ёмкости
Увеличение ёмкости
- При первой вставке элемента начальная ёмкость устанавливается в 4 элемента.
- При каждом последующем заполнении ёмкость удваивается (
capacity * 2). - Максимальная ёмкость ограничена значением
UINT16_MAX(65 535).
Уменьшение ёмкости
- Ёмкость уменьшается при удалении элемента, если:
capacity > 4иcapacity / 2 >= size * 2(то есть выделенная память больше нужной более чем в 2 раза).- Новая ёмкость рассчитывается как
size * 2, но не менее 4.
- При достижении
size == 0вся память полностью освобождается.
Примеры использования
Базовый пример (целые числа)
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);
Работа со структурами
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". Уровень логирования можно настроить:
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 |
Элемент не найден |
Ограничения
- Максимум 65 535 элементов — из-за использования
uint16_tдля индексов и ёмкости. - Побайтовое сравнение —
memcmpсравнивает сырые байты, что может не учитывать выравнивание структур. - Копирование данных — все операции вставки и получения копируют данные, что может быть неэффективно для очень больших элементов.
- Зависимость от ESP-IDF — компонент работает только в среде ESP-IDF с FreeRTOS.
Требования для сборки
- ESP-IDF (фреймворк для ESP32)
- FreeRTOS (входит в состав ESP-IDF)
- Компонент должен находиться в директории
components/zh_vectorпроекта
Установка
Для добавления компонента в проект:
cd ваш_проект/components
git clone https://github.com/aZholtikov/zh_vector
Затем в коде подключить заголовочный файл:
#include "zh_vector.h"