Consolidate serialization format and ownership documentation in header
This commit is contained in:
1 parent
ace9cb70d0
commit
1c4010ebed
2 files changed
+15
-118
No files matched your search
+15
-1
@@ -32,6 +32,8 @@
|
||||
* • elem_size=1 добавляет NUL и для byte-массивов.
|
||||
* • Linked nodes копируются целиком, без рекурсивного кодирования вложенных указателей.
|
||||
* • Этот формат зависит от ABI сырых полей; сериализация не нормализует размер/выравнивание структур.
|
||||
* • Схема доверенная: offsets, типы, размеры и выравнивание полей/next проверяет её автор, не decode.
|
||||
* • Независимые операции не используют общее изменяемое состояние; доступ к одной структуре согласует владелец.
|
||||
*
|
||||
* @example
|
||||
* // 1. Описание структуры
|
||||
@@ -116,7 +118,7 @@ struct SerializeSchema {
|
||||
uint16_t struct_size; ///< размер основной структуры
|
||||
uint16_t max_size; ///< максимальный размер сериализованных данных (0 = без ограничения)
|
||||
uint16_t header_len; ///< длина заголовка
|
||||
const struct SerializeField* fields;
|
||||
const struct SerializeField* fields; ///< field_count описателей в порядке записи; схема заимствуется на время вызова
|
||||
};
|
||||
|
||||
#define SERIALIZE_ERR_OK 0 ///< успех
|
||||
@@ -125,16 +127,28 @@ struct SerializeSchema {
|
||||
#define SERIALIZE_ERR_SIZE -3 ///< encode: превышен max_size
|
||||
#define SERIALIZE_ERR_NOTSUP -4 ///< encode: неподдерживаемый тип поля
|
||||
|
||||
/* Кодирует structure по schema; при header_len > 0 header указывает минимум на header_len байт.
|
||||
* При успехе *out_buf — новый блок u_malloc (освободить u_free), *out_len — фактически записанная длина с header.
|
||||
* Входная структура и её вложенные данные остаются у владельца. На ошибке выходные значения не меняются.
|
||||
* Возврат: OK, NULL (обязательные аргументы), BUF (OOM), SIZE (max_size), NOTSUP (неизвестный тип).
|
||||
* ASCIIZ требует elem_size=1; порядок fields задаёт формат, сортировка по offset не требуется. */
|
||||
int serialize_encode(const void* structure,
|
||||
const struct SerializeSchema* schema,
|
||||
const uint8_t* header,
|
||||
uint8_t** out_buf,
|
||||
size_t* out_len);
|
||||
|
||||
/* Декодирует in_len байт БЕЗ header: передавать buf + header_len, len - header_len.
|
||||
* При успехе *structure — новая структура с выделенными полями; очищать serialize_free с той же схемой.
|
||||
* При ошибке частично созданные объекты освобождаются, *structure = NULL. Старый *structure не освобождает.
|
||||
* max_size не ограничивает decode; хвост после последнего поля игнорируется.
|
||||
* Возврат: OK, NULL (аргументы), BUF (OOM, обрезанные данные или неизвестный тип). */
|
||||
int serialize_decode(const uint8_t* in_buf, size_t in_len,
|
||||
const struct SerializeSchema* schema,
|
||||
void** structure);
|
||||
|
||||
// Освобождает выделенную через u_malloc структуру, поля схемы и узлы списков, обнуляет *structure; NULL допустим.
|
||||
// Вложенные указатели внутри элементов массивов/узлов не обходит; к структуре на стеке применять нельзя.
|
||||
void serialize_free(const struct SerializeSchema* schema, void** structure);
|
||||
|
||||
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
# 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. Как пользоваться
|
||||
|
||||
```c
|
||||
#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)` — неподдерживаемый тип поля.
|
||||
Reference in new issue
Block a user