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.
130 lines
6.1 KiB
130 lines
6.1 KiB
/** |
|
* @file serialize.h |
|
* @brief Бинарная сериализация структур с динамическим выделением памяти |
|
* |
|
* Библиотека преобразует C-структуру, содержащую: |
|
* - фиксированные поля, |
|
* - ASCIIZ-строки (char*), |
|
* - динамические массивы (uint8_t* / char*), |
|
* - singly-linked списки |
|
* в компактный бинарный буфер и обратно. |
|
* |
|
* Поддерживаемые типы полей (см. SERIALIZE_TYPE_*): |
|
* 0 — фиксированные данные (встроены в структуру) |
|
* 1 — ASCIIZ-строка (char*, длина = strlen) |
|
* 2 — массив ровно из 1 элемента (указатель) |
|
* 3 — массив, количество элементов в UINT8 (по len_offset) |
|
* 4 — массив, количество элементов в UINT16 (по len_offset) |
|
* 5 — массив, количество элементов в UINT32 (по len_offset) |
|
* 6 — singly-linked list (next-указатель по смещению len_offset) |
|
* |
|
* Особенности: |
|
* • Все динамические данные (строки, массивы, списки) выделяются через u_malloc. |
|
* • При encode длина переменных полей кодируется всегда 2 байтами (uint16_t, big-endian). |
|
* • Для linked list в буфере сохраняется только количество узлов + сырые данные узлов |
|
* (next-указатели НЕ сериализуются, они восстанавливаются при decode). |
|
* • serialize_decode принимает буфер БЕЗ заголовка. |
|
* • serialize_free освобождает ВСЮ структуру и все вложенные динамические объекты. |
|
* • Поля в schema.fields должны идти в порядке возрастания offset. |
|
* • max_size (если > 0) — жёсткий лимит размера выходного буфера. |
|
* |
|
* @example |
|
* // 1. Описание структуры |
|
* typedef struct Node { |
|
* uint32_t value; |
|
* struct Node* next; // next по offset = 4 |
|
* } Node; |
|
* |
|
* typedef struct { |
|
* uint8_t id; |
|
* char* name; |
|
* uint8_t name_len; |
|
* uint8_t* data; |
|
* uint16_t data_cnt; |
|
* Node* list; // linked list |
|
* } MyStruct; |
|
* |
|
* // 2. Схема |
|
* static const struct SerializeField my_fields[] = { |
|
* {offsetof(MyStruct, id), 1, SERIALIZE_TYPE_FIXED, 0}, |
|
* {offsetof(MyStruct, name), 1, SERIALIZE_TYPE_ARRAY_U8, offsetof(MyStruct, name_len)}, |
|
* {offsetof(MyStruct, data), 1, SERIALIZE_TYPE_ARRAY_U16, offsetof(MyStruct, data_cnt)}, |
|
* {offsetof(MyStruct, list), sizeof(Node), SERIALIZE_TYPE_LINKED, offsetof(Node, next)} |
|
* }; |
|
* |
|
* static const struct SerializeSchema my_schema = { |
|
* .field_count = 4, |
|
* .struct_size = sizeof(MyStruct), |
|
* .max_size = 4096, // опционально |
|
* .header_len = 4, |
|
* .fields = my_fields |
|
* }; |
|
* |
|
* // 3. Использование |
|
* uint8_t header[] = {0x01, 0x02, 0x03, 0x04}; |
|
* uint8_t *buf = NULL; |
|
* size_t len; |
|
* |
|
* serialize_encode(&orig, &my_schema, header, &buf, &len); |
|
* |
|
* MyStruct *restored = NULL; |
|
* uint8_t *data = buf + my_schema.header_len; |
|
* serialize_decode(data, len - my_schema.header_len, &my_schema, (void**)&restored); |
|
* |
|
* serialize_free(&my_schema, (void**)&restored); |
|
* u_free(buf); |
|
*/ |
|
#ifndef SERIALIZE_H |
|
#define SERIALIZE_H |
|
#include <stdint.h> |
|
#include <stddef.h> |
|
#include "../lib/mem.h" |
|
|
|
#define SERIALIZE_TYPE_FIXED 0 ///< фиксированные данные, elem_size = размер |
|
#define SERIALIZE_TYPE_ASCIIZ 1 ///< char* (ASCIIZ, длина = strlen) |
|
#define SERIALIZE_TYPE_ARRAY_FIXED 2 ///< *array ровно 1 элемент |
|
#define SERIALIZE_TYPE_ARRAY_U8 3 ///< *array, счётчик UINT8 |
|
#define SERIALIZE_TYPE_ARRAY_U16 4 ///< *array, счётчик UINT16 |
|
#define SERIALIZE_TYPE_ARRAY_U32 5 ///< *array, счётчик UINT32 |
|
#define SERIALIZE_TYPE_LINKED 6 ///< linked_list (next по смещению len_offset) |
|
|
|
/** |
|
* @brief Описание одного поля структуры для сериализации |
|
*/ |
|
struct SerializeField { |
|
uint16_t offset; ///< смещение поля в структуре |
|
uint8_t elem_size; ///< размер элемента (для fixed = размер данных, для array/linked = размер одного элемента) |
|
uint8_t type; ///< SERIALIZE_TYPE_* |
|
uint16_t len_offset; ///< смещение поля-счётчика (игнорируется для type 0,1,2) |
|
}; |
|
|
|
/** |
|
* @brief Схема структуры для сериализации |
|
*/ |
|
struct SerializeSchema { |
|
uint16_t field_count; ///< число полей |
|
uint16_t struct_size; ///< размер основной структуры |
|
uint16_t max_size; ///< максимальный размер сериализованных данных (0 = без ограничения) |
|
uint16_t header_len; ///< длина заголовка |
|
const struct SerializeField* fields; |
|
}; |
|
|
|
#define SERIALIZE_ERR_OK 0 ///< успех |
|
#define SERIALIZE_ERR_BUF -1 ///< ошибка выделения памяти |
|
#define SERIALIZE_ERR_NULL -2 ///< NULL-указатель |
|
#define SERIALIZE_ERR_SIZE -3 ///< превышен max_size |
|
#define SERIALIZE_ERR_NOTSUP -4 ///< неподдерживаемый тип |
|
|
|
int serialize_encode(const void* structure, |
|
const struct SerializeSchema* schema, |
|
const uint8_t* header, |
|
uint8_t** out_buf, |
|
size_t* out_len); |
|
|
|
int serialize_decode(const uint8_t* in_buf, size_t in_len, |
|
const struct SerializeSchema* schema, |
|
void** structure); |
|
|
|
void serialize_free(const struct SerializeSchema* schema, void** structure); |
|
|
|
#endif // SERIALIZE_H
|
|
|