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