Files
zh_vector/wiki_ru.md
T
2026-10-17 09:08:58 +03:00

14 KiB
Raw Blame History

Описание компонента 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 вся память полностью освобождается.

Примеры использования

Базовый пример (целые числа)

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 Элемент не найден

Ограничения

  1. Максимум 65 535 элементов — из-за использования uint16_t для индексов и ёмкости.
  2. Побайтовое сравнение — memcmp сравнивает сырые байты, что может не учитывать выравнивание структур.
  3. Копирование данных — все операции вставки и получения копируют данные, что может быть неэффективно для очень больших элементов.
  4. Зависимость от 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"