You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6.9 KiB
6.9 KiB
Binary Serialization Library
1. Назначение
Библиотека бинарной сериализации C-структур с динамическими полями (строки, массивы, singly-linked списки) в компактный бинарный буфер и обратно. Предназначена для кодирования/декодирования сообщений сетевого протокола (контрольный сервер, синхронизация БД, обмен конфигурацией).
Что сериализуется:
- Фиксированные поля (целые, флаги, структуры) — копируются «как есть».
- ASCIIZ-строки (
char*) — длина определяется черезstrlen, сохраняется с терминальным нулём. - Динамические массивы (
uint8_t*) — счётчик элементов в полеUINT8/UINT16/UINT32или ровно 1 элемент (ARRAY_FIXED). - Singly-linked списки —
next-указатели не сериализуются, сохраняются толькоcount+data.
Формат: header (header_len байт, передаётся отдельно) + данные полей. Длина переменных полей кодируется 2 байтами (uint16_t, big-endian).
Потокобезопасность: нет, однопоточное использование.
2. Как пользоваться
#include "../lib/serialize.h"
// --- 1. Определяем структуру ---
typedef struct Node {
uint32_t id;
struct Node *next;
} Node;
typedef struct {
uint8_t version;
uint8_t name_len;
char *name; // ASCIIZ (elem_size=1)
uint16_t addrs_cnt;
uint8_t *addrs; // массив байт (elem_size=1)
Node *list; // linked list
} Message;
// --- 2. Описываем схему ---
static const struct SerializeField msg_fields[] = {
{offsetof(Message, version), sizeof(uint8_t), SERIALIZE_TYPE_FIXED, 0},
{offsetof(Message, name), 1, SERIALIZE_TYPE_ARRAY_U8, offsetof(Message, name_len)},
{offsetof(Message, addrs), 1, SERIALIZE_TYPE_ARRAY_U16, offsetof(Message, addrs_cnt)},
{offsetof(Message, list), sizeof(Node), SERIALIZE_TYPE_LINKED, offsetof(Node, next)}
};
static const struct SerializeSchema msg_schema = {
.field_count = 4,
.struct_size = sizeof(Message),
.max_size = 4096, // 0 = без лимита
.header_len = 4, // байты заголовка (напр. версия протокола)
.fields = msg_fields
};
// --- 3. Сериализация ---
Message msg = { .version = 1, .name = "test", .name_len = 4, .list = NULL };
msg.addrs_cnt = 2;
msg.addrs = u_malloc(2); msg.addrs[0] = 0xAA; msg.addrs[1] = 0xBB;
uint8_t header[] = {0x01, 0x00, 0x00, 0x00}; // 4-байтный заголовок
uint8_t *buf = NULL;
size_t len;
if (serialize_encode(&msg, &msg_schema, header, &buf, &len) != SERIALIZE_ERR_OK)
goto fail;
// buf содержит: header (4) + version (1) + [len=2]name(4) + '\0' + [len=2]addrs(2)
// --- 4. Десериализация (buf без заголовка!) ---
Message *restored = NULL;
uint8_t *data_ptr = buf + msg_schema.header_len; // пропускаем header
if (serialize_decode(data_ptr, len - msg_schema.header_len, &msg_schema, (void**)&restored) != SERIALIZE_ERR_OK)
goto fail;
// restored->name == "test", restored->name_len == 4
// restored->addrs[0] == 0xAA, restored->addrs_cnt == 2
// restored->list == NULL (список пуст — не падает)
// --- 5. Очистка ---
serialize_free(&msg_schema, (void**)&restored); // освобождает всё: name, addrs, list, структуру
u_free(buf);
Ключевые нюансы:
- Поля в
schema.fieldsдолжны идти в порядке возрастанияoffsetв структуре. serialize_decodeпринимает буфер без заголовка — передаётсяbuf + schema.header_len.serialize_freeосвобождает структуру и все вложенные динамические поля (строки, массивы, списки).- При ошибке
serialize_decodeавтоматически освобождает всю частично выделенную память →*structure = NULL. max_size > 0— жёсткий лимит, при превышенииSERIALIZE_ERR_SIZE.- Длина переменных полей всегда 2 байта (максимум 65535 элементов).
- ASCIIZ-строки (
elem_size=1) сохраняются с терминальным\0; пустая строка → 1 байт (\0). - Для linked list поле
len_offsetуказывает смещениеnext-указателя внутри узла. - Передавать буфер в
serialize_decodeбез заголовка — он не знает проheader_len.
3. API
| Функция | Назначение |
|---|---|
serialize_encode(structure, schema, header, &buf, &len) |
Кодирует структуру. header копируется в начало buf. |
serialize_decode(in_buf, in_len, schema, &structure) |
Декодирует буфер без заголовка. При ошибке всё освобождает. |
serialize_free(schema, &structure) |
Рекурсивно освобождает структуру и динамические поля, затем обнуляет указатель. |
Типы полей (SERIALIZE_TYPE_*):
| Тип | data |
elem_size |
len_offset |
|---|---|---|---|
FIXED (0) |
Встроенные данные | Размер поля | Не исп. |
ASCIIZ (1) |
char* |
1 | Не исп. (длина = strlen) |
ARRAY_FIXED (2) |
uint8_t* (ровно 1 элемент) |
Размер элемента | Не исп. |
ARRAY_U8 (3) |
uint8_t* |
Размер элемента | Смещение uint8_t-счётчика |
ARRAY_U16 (4) |
uint8_t* |
Размер элемента | Смещение uint16_t-счётчика |
ARRAY_U32 (5) |
uint8_t* |
Размер элемента | Смещение uint32_t-счётчика |
LINKED (6) |
void* (голова списка) |
Размер узла | Смещение next внутри узла |
Коды возврата:
SERIALIZE_ERR_OK (0)— успех.SERIALIZE_ERR_BUF (-1)— ошибка выделения памяти.SERIALIZE_ERR_NULL (-2)— NULL-указатель во входных параметрах.SERIALIZE_ERR_SIZE (-3)— превышенmax_size.SERIALIZE_ERR_NOTSUP (-4)— неподдерживаемый тип поля.