Browse Source

serializer

congestion
Evgeny 6 months ago
parent
commit
b613e3a8e0
  1. 355
      lib/serialize.c
  2. 114
      lib/serialize.h
  3. 154
      tests/test_serialize.c

355
lib/serialize.c

@ -2,6 +2,9 @@
#include "serialize.h"
#include <string.h>
#define SERIALIZE_ERR_SIZE -3 ///< превышен max_size схемы
#define SERIALIZE_ERR_NOTSUP -4 ///< неподдерживаемый тип
static uint16_t length_encode(uint8_t* out, uint16_t val) {
if (out) {
out[0] = (uint8_t)(val >> 8);
@ -19,41 +22,21 @@ static uint16_t length_decode(const uint8_t* in, size_t in_len, size_t* consumed
return ((uint16_t)in[0] << 8) | in[1];
}
static size_t get_len_field(const uint8_t* data, const struct SerializeField* f) {
if (!f || !data || f->len_type == 0) return 0;
static uint32_t get_var_count(const uint8_t* data, const struct SerializeField* f) {
if (!data || !f) return 0;
const uint8_t* len_ptr = data + f->len_offset;
if (f->len_type == 1) {
return *len_ptr;
} else if (f->len_type == 2) {
return *(const uint16_t*)len_ptr;
} else if (f->len_type == 4) {
return *(const uint32_t*)len_ptr;
}
if (f->type == SERIALIZE_TYPE_ARRAY_U8) return *len_ptr;
if (f->type == SERIALIZE_TYPE_ARRAY_U16) return *(const uint16_t*)len_ptr;
if (f->type == SERIALIZE_TYPE_ARRAY_U32) return *(const uint32_t*)len_ptr;
return 0;
}
static void set_len_field(uint8_t* data, const struct SerializeField* f, size_t len) {
if (!f || !data || f->len_type == 0) return;
static void set_var_count(uint8_t* data, const struct SerializeField* f, uint16_t count) {
if (!data || !f) return;
uint8_t* len_ptr = data + f->len_offset;
if (f->len_type == 1) {
*len_ptr = (uint8_t)len;
} else if (f->len_type == 2) {
*(uint16_t*)len_ptr = (uint16_t)len;
} else if (f->len_type == 4) {
*(uint32_t*)len_ptr = (uint32_t)len;
}
}
static size_t fixed_field_size(const struct SerializeSchema* schema, size_t idx) {
if (idx >= schema->field_count) return 0;
const struct SerializeField* f = &schema->fields[idx];
if (f->elem_size != 0) return 0;
size_t next_offset = (idx + 1 < schema->field_count) ?
schema->fields[idx + 1].offset :
schema->struct_size;
return (next_offset > f->offset) ? (next_offset - f->offset) : 0;
if (f->type == SERIALIZE_TYPE_ARRAY_U8) *len_ptr = (uint8_t)count;
else if (f->type == SERIALIZE_TYPE_ARRAY_U16) *(uint16_t*)len_ptr = count;
else if (f->type == SERIALIZE_TYPE_ARRAY_U32) *(uint32_t*)len_ptr = count;
}
int serialize_encode(const void* structure,
@ -68,30 +51,45 @@ int serialize_encode(const void* structure,
for (size_t i = 0; i < schema->field_count; i++) {
const struct SerializeField* f = &schema->fields[i];
if (f->elem_size == 0) {
total += fixed_field_size(schema, i);
} else {
size_t raw_len;
if (f->elem_size == 1) { // string
if (f->type == SERIALIZE_TYPE_FIXED || f->type == SERIALIZE_TYPE_ARRAY_FIXED) {
total += f->elem_size;
} else if (f->type == SERIALIZE_TYPE_ASCIIZ ||
f->type == SERIALIZE_TYPE_ARRAY_U8 ||
f->type == SERIALIZE_TYPE_ARRAY_U16 ||
f->type == SERIALIZE_TYPE_ARRAY_U32) {
uint32_t count = 0;
if (f->type == SERIALIZE_TYPE_ASCIIZ) {
const char* str = *(const char**)(data + f->offset);
raw_len = (f->len_type != 0) ? get_len_field(data, f)
: (str ? strlen(str) : 0);
} else { // array
const uint8_t* arr = *(const uint8_t**)(data + f->offset);
raw_len = (f->len_type != 0) ? get_len_field(data, f)
: f->max_size;
if (raw_len > 0 && !arr) raw_len = 0;
count = str ? (uint32_t)strlen(str) : 0;
} else {
count = get_var_count(data, f);
}
uint16_t len = (raw_len > f->max_size) ? f->max_size : (uint16_t)raw_len;
total += length_encode(NULL, len);
uint16_t len = (count > 0xFFFFu) ? 0xFFFFu : (uint16_t)count;
total += 2;
if (f->elem_size == 1) {
total += (size_t)len + 1; // data + null terminator
total += (size_t)len + 1; // данные + '\0'
} else {
total += (size_t)len * f->elem_size;
}
} else if (f->type == SERIALIZE_TYPE_LINKED) {
// подсчёт узлов (максимум 65535)
void* cur = *(void**)(data + f->offset);
uint16_t count = 0;
while (cur && count < 0xFFFFu) {
count++;
cur = *(void**)((uint8_t*)cur + f->len_offset);
}
total += 2 + (size_t)count * f->elem_size;
} else {
return SERIALIZE_ERR_NOTSUP;
}
}
if (schema->max_size > 0 && total > schema->max_size) {
return SERIALIZE_ERR_SIZE;
}
uint8_t* buf = u_malloc(total);
if (!buf) return SERIALIZE_ERR_BUF;
@ -105,36 +103,68 @@ int serialize_encode(const void* structure,
const struct SerializeField* f = &schema->fields[i];
const uint8_t* fld = data + f->offset;
if (f->elem_size == 0) {
size_t sz = fixed_field_size(schema, i);
if (sz > 0) {
memcpy(ptr, fld, sz);
ptr += sz;
if (f->type == SERIALIZE_TYPE_FIXED) {
if (f->elem_size > 0) {
memcpy(ptr, fld, f->elem_size);
ptr += f->elem_size;
}
} else if (f->type == SERIALIZE_TYPE_ARRAY_FIXED) {
const uint8_t* arr = *(const uint8_t**)fld;
if (f->elem_size > 0) {
if (arr) memcpy(ptr, arr, f->elem_size);
else memset(ptr, 0, f->elem_size);
}
ptr += f->elem_size;
} else if (f->type == SERIALIZE_TYPE_ASCIIZ ||
f->type == SERIALIZE_TYPE_ARRAY_U8 ||
f->type == SERIALIZE_TYPE_ARRAY_U16 ||
f->type == SERIALIZE_TYPE_ARRAY_U32) {
uint32_t raw_count = 0;
if (f->type == SERIALIZE_TYPE_ASCIIZ) {
const char* str = *(const char**)fld;
raw_count = str ? (uint32_t)strlen(str) : 0;
} else {
raw_count = get_var_count(data, f);
}
uint16_t len = (raw_count > 0xFFFFu) ? 0xFFFFu : (uint16_t)raw_count;
if (len > 0 && f->type == SERIALIZE_TYPE_ASCIIZ) {
if (!*(const char**)fld) len = 0;
} else if (len > 0 && f->type != SERIALIZE_TYPE_ASCIIZ) {
if (!*(const uint8_t**)fld) len = 0;
}
} else if (f->elem_size == 1) { // string
const char* str = *(const char**)fld;
size_t raw_len = (f->len_type != 0) ? get_len_field(data, f)
: (str ? strlen(str) : 0);
uint16_t len = (raw_len > f->max_size) ? f->max_size : (uint16_t)raw_len;
if (len > 0 && !str) len = 0;
ptr += length_encode(ptr, len);
if (len > 0) {
memcpy(ptr, str, len);
ptr += len;
if (f->elem_size == 1) { // ASCIIZ
if (len > 0) {
const char* str = *(const char**)fld;
if (str) memcpy(ptr, str, len);
ptr += len;
}
*ptr++ = '\0';
} else { // array
if (len > 0) {
const uint8_t* arr = *(const uint8_t**)fld;
if (arr) memcpy(ptr, arr, (size_t)len * f->elem_size);
ptr += (size_t)len * f->elem_size;
}
}
} else if (f->type == SERIALIZE_TYPE_LINKED) {
void* cur = *(void**)(data + f->offset);
uint16_t count = 0;
while (cur && count < 0xFFFFu) {
count++;
cur = *(void**)((uint8_t*)cur + f->len_offset);
}
*ptr++ = '\0';
} else { // array
const uint8_t* arr = *(const uint8_t**)fld;
size_t raw_len = (f->len_type != 0) ? get_len_field(data, f)
: f->max_size;
uint16_t len = (raw_len > f->max_size) ? f->max_size : (uint16_t)raw_len;
if (len > 0 && !arr) len = 0;
ptr += length_encode(ptr, len);
if (len > 0 && arr) {
memcpy(ptr, arr, (size_t)len * f->elem_size);
ptr += (size_t)len * f->elem_size;
ptr += length_encode(ptr, count);
cur = *(void**)(data + f->offset);
for (uint16_t i = 0; i < count; i++) {
if (!cur) break;
memcpy(ptr, cur, f->elem_size);
ptr += f->elem_size;
cur = *(void**)((uint8_t*)cur + f->len_offset);
}
}
}
@ -168,94 +198,121 @@ int serialize_decode(const uint8_t* in_buf,
for (size_t i = 0; i < schema->field_count; i++) {
const struct SerializeField* f = &schema->fields[i];
if (f->elem_size == 0) {
size_t sz = fixed_field_size(schema, i);
if (ptr + sz > end) {
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
if (f->type == SERIALIZE_TYPE_FIXED) {
if (ptr + f->elem_size > end) goto decode_error;
if (f->elem_size > 0) {
memcpy(data + f->offset, ptr, f->elem_size);
ptr += f->elem_size;
}
if (sz > 0) {
memcpy(data + f->offset, ptr, sz);
ptr += sz;
} else if (f->type == SERIALIZE_TYPE_ARRAY_FIXED) {
if (ptr + f->elem_size > end) goto decode_error;
uint8_t* arr = NULL;
if (f->elem_size > 0) {
arr = u_malloc(f->elem_size);
if (arr) memcpy(arr, ptr, f->elem_size);
}
} else if (f->elem_size == 1) { // string
*(uint8_t**)(data + f->offset) = arr;
if (f->elem_size > 0 && !arr) goto decode_error;
ptr += f->elem_size;
} else if (f->type == SERIALIZE_TYPE_ASCIIZ ||
f->type == SERIALIZE_TYPE_ARRAY_U8 ||
f->type == SERIALIZE_TYPE_ARRAY_U16 ||
f->type == SERIALIZE_TYPE_ARRAY_U32) {
uint16_t len = length_decode(ptr, end - ptr, &consumed);
if (consumed == 0) {
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
}
if (consumed == 0) goto decode_error;
ptr += consumed;
if (len > f->max_size) len = f->max_size;
size_t needed = (size_t)len + 1;
if ((size_t)(end - ptr) < needed) {
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
}
char* str = NULL;
if (len == 0) {
str = u_malloc(1);
if (str) str[0] = '\0';
int is_str = (f->elem_size == 1);
size_t needed = is_str ? (size_t)len + 1 : (size_t)len * f->elem_size;
if ((size_t)(end - ptr) < needed) goto decode_error;
if (is_str) {
char* str = NULL;
if (len == 0) {
str = u_malloc(1);
if (str) str[0] = '\0';
} else {
str = u_malloc(len + 1);
if (str) {
memcpy(str, ptr, len);
str[len] = '\0';
}
}
*(char**)(data + f->offset) = str;
if (!str) goto decode_error;
ptr += needed;
} else {
str = u_malloc(len + 1);
if (str) {
memcpy(str, ptr, len);
str[len] = '\0';
uint8_t* arr = NULL;
if (needed > 0) {
arr = u_malloc(needed);
if (arr) memcpy(arr, ptr, needed);
}
*(uint8_t**)(data + f->offset) = arr;
if (needed > 0 && !arr) goto decode_error;
ptr += needed;
}
*(char**)(data + f->offset) = str;
if (f->len_type != 0) set_len_field(data, f, len);
if (str == NULL) {
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
if (f->type >= SERIALIZE_TYPE_ARRAY_U8 && f->type <= SERIALIZE_TYPE_ARRAY_U32) {
set_var_count(data, f, len);
}
ptr += needed;
} else { // array
} else if (f->type == SERIALIZE_TYPE_LINKED) {
uint16_t len = length_decode(ptr, end - ptr, &consumed);
if (consumed == 0) {
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
}
if (consumed == 0) goto decode_error;
ptr += consumed;
if (len > f->max_size) len = f->max_size;
size_t arr_sz = (size_t)len * f->elem_size;
if ((size_t)(end - ptr) < arr_sz) {
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
}
void* head = NULL;
void* prev = NULL;
for (uint16_t i = 0; i < len; i++) {
if (ptr + f->elem_size > end) {
// освободить уже созданную часть списка
void* n = head;
while (n) {
void* nxt = *(void**)((uint8_t*)n + f->len_offset);
u_free(n);
n = nxt;
}
goto decode_error;
}
uint8_t* arr = NULL;
if (arr_sz > 0) {
arr = u_malloc(arr_sz);
if (arr) memcpy(arr, ptr, arr_sz);
}
void* new_node = u_malloc(f->elem_size);
if (!new_node) {
void* n = head;
while (n) {
void* nxt = *(void**)((uint8_t*)n + f->len_offset);
u_free(n);
n = nxt;
}
goto decode_error;
}
*(uint8_t**)(data + f->offset) = arr;
if (f->len_type != 0) set_len_field(data, f, len);
memcpy(new_node, ptr, f->elem_size);
// обязательно обнуляем next в новом узле
*(void**)((uint8_t*)new_node + f->len_offset) = NULL;
if (arr_sz > 0 && arr == NULL) {
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
if (!head) {
head = new_node;
} else {
*(void**)((uint8_t*)prev + f->len_offset) = new_node;
}
prev = new_node;
ptr += f->elem_size;
}
ptr += arr_sz;
*(void**)(data + f->offset) = head;
} else {
goto decode_error;
}
}
*structure = obj;
return SERIALIZE_ERR_OK;
decode_error:
serialize_free(schema, &obj);
*structure = NULL;
return SERIALIZE_ERR_BUF;
}
void serialize_free(const struct SerializeSchema* schema, void** structure) {
@ -265,17 +322,23 @@ void serialize_free(const struct SerializeSchema* schema, void** structure) {
for (size_t i = 0; i < schema->field_count; i++) {
const struct SerializeField* f = &schema->fields[i];
if (f->elem_size == 1) {
char* str = *(char**)(data + f->offset);
if (str) {
u_free(str);
*(char**)(data + f->offset) = NULL;
if (f->type == SERIALIZE_TYPE_LINKED) {
void** headp = (void**)(data + f->offset);
void* node = *headp;
while (node) {
void* next = *(void**)((uint8_t*)node + f->len_offset);
u_free(node);
node = next;
}
} else if (f->elem_size > 1) {
uint8_t* arr = *(uint8_t**)(data + f->offset);
if (arr) {
u_free(arr);
*(uint8_t**)(data + f->offset) = NULL;
*headp = NULL;
} else if (f->type == SERIALIZE_TYPE_ASCIIZ ||
f->type == SERIALIZE_TYPE_ARRAY_FIXED ||
(f->type >= SERIALIZE_TYPE_ARRAY_U8 && f->type <= SERIALIZE_TYPE_ARRAY_U32)) {
uint8_t** p = (uint8_t**)(data + f->offset);
if (*p) {
u_free(*p);
*p = NULL;
}
}
}

114
lib/serialize.h

@ -2,26 +2,77 @@
* @file serialize.h
* @brief Бинарная сериализация структур с динамическим выделением памяти
*
* Поддерживает структуры с полями переменной длины (ASCIIZ, ARRAY) через указатели.
* При десериализации автоматически выделяет память через u_malloc.
* Библиотека преобразует C-структуру, содержащую:
* - фиксированные поля,
* - ASCIIZ-строки (char*),
* - динамические массивы (uint8_t* / char*),
* - singly-linked списки
* в компактный бинарный буфер и обратно.
*
* Формат буфера:
* [header_len байт][сериализованные данные]
* Поддерживаемые типы полей (см. 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)
*
* Пример использования:
* @code
* // Сериализация
* Особенности:
* • Все динамические данные (строки, массивы, списки) выделяются через 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};
* serialize_encode(&orig, &schema, header, &buf, &len);
* uint8_t *buf = NULL;
* size_t len;
*
* serialize_encode(&orig, &my_schema, header, &buf, &len);
*
* // Десериализация (данные без заголовка)
* uint8_t* data = buf + schema.header_len;
* serialize_decode(data, len - schema.header_len, &schema, &restored);
* 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(&schema, &restored);
* serialize_free(&my_schema, (void**)&restored);
* u_free(buf);
* @endcode
*/
#ifndef SERIALIZE_H
#define SERIALIZE_H
@ -29,16 +80,22 @@
#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; ///< 0=fixed, 1=ASCIIZ, >1=ARRAY (размер элемента в байтах)
uint16_t max_size; ///< макс. длина для ASCIIZ / макс. кол-во для ARRAY
uint8_t len_type; ///< 0 = нет length field (для строк — strlen, для массивов — max_size)
///< 1 = UINT8, 2 = UINT16, 4 = UINT32 (по смещению len_offset)
uint16_t len_offset; ///< смещение length-поля в структуре (игнорируется при len_type=0)
uint16_t offset; ///< смещение поля в структуре
uint8_t elem_size; ///< размер элемента (для fixed = размер данных, для array/linked = размер одного элемента)
uint8_t type; ///< SERIALIZE_TYPE_*
uint16_t len_offset; ///< смещение поля-счётчика (игнорируется для type 0,1,2)
};
/**
@ -46,14 +103,17 @@ struct SerializeField {
*/
struct SerializeSchema {
uint16_t field_count; ///< число полей
uint16_t struct_size; ///< размер структуры для allocate
uint16_t header_len; ///< длина заголовка (произвольные байты в начале)
const struct SerializeField* fields; ///< массив описаний полей
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_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,
@ -67,4 +127,4 @@ int serialize_decode(const uint8_t* in_buf, size_t in_len,
void serialize_free(const struct SerializeSchema* schema, void** structure);
#endif // SERIALIZE_H
#endif // SERIALIZE_H

154
tests/test_serialize.c

@ -1,8 +1,8 @@
// test_serialize.c - Unit tests for serialize library with dynamic allocation
// test_serialize.c - Unit tests for serialize library (with linked list + error cases)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h> // для offsetof
#include <stddef.h>
#include <assert.h>
#include "../lib/serialize.h"
@ -13,6 +13,11 @@
#define FAIL(msg) do { printf("FAIL: %s\n", msg); exit(1); } while(0)
#pragma pack(push, 1)
typedef struct TestNode {
uint32_t value;
struct TestNode* next;
} TestNode;
typedef struct {
uint8_t id;
uint16_t port;
@ -24,23 +29,26 @@ typedef struct {
char* name;
uint8_t addr_cnt;
uint8_t* addrs;
TestNode* list; // linked list (type 6)
} TestStruct;
#pragma pack(pop)
static const struct SerializeField test_fields[] = {
{offsetof(TestStruct, id), 0, 0, 0, 0}, // fixed
{offsetof(TestStruct, port), 0, 0, 0, 0}, // fixed
{offsetof(TestStruct, ip), 0, 0, 0, 0}, // fixed
{offsetof(TestStruct, big_val), 0, 0, 0, 0}, // fixed
{offsetof(TestStruct, signed_val),0, 0, 0, 0}, // fixed
{offsetof(TestStruct, signed32), 0, 0, 0, 0}, // fixed
{offsetof(TestStruct, name), 1, 64, 1, offsetof(TestStruct, name_len)}, // string + uint8 len
{offsetof(TestStruct, addrs), 1, 16, 1, offsetof(TestStruct, addr_cnt)} // byte array + uint8 len
{offsetof(TestStruct, id), 1, SERIALIZE_TYPE_FIXED, 0},
{offsetof(TestStruct, port), 2, SERIALIZE_TYPE_FIXED, 0},
{offsetof(TestStruct, ip), 4, SERIALIZE_TYPE_FIXED, 0},
{offsetof(TestStruct, big_val), 8, SERIALIZE_TYPE_FIXED, 0},
{offsetof(TestStruct, signed_val),1, SERIALIZE_TYPE_FIXED, 0},
{offsetof(TestStruct, signed32), 4, SERIALIZE_TYPE_FIXED, 0},
{offsetof(TestStruct, name), 1, SERIALIZE_TYPE_ARRAY_U8, offsetof(TestStruct, name_len)},
{offsetof(TestStruct, addrs), 1, SERIALIZE_TYPE_ARRAY_U8, offsetof(TestStruct, addr_cnt)},
{offsetof(TestStruct, list), sizeof(TestNode), SERIALIZE_TYPE_LINKED, offsetof(TestNode, next)}
};
static const struct SerializeSchema test_schema = {
.field_count = sizeof(test_fields) / sizeof(test_fields[0]),
.struct_size = sizeof(TestStruct),
.max_size = 0,
.header_len = 4,
.fields = test_fields
};
@ -57,22 +65,26 @@ static void test_alloc_roundtrip(void) {
.big_val = 100,
.signed_val = 10,
.signed32 = 5000,
.list = NULL
};
orig.name = strdup("hello");
orig.name_len = 5;
orig.addrs = u_malloc(8);
orig.addrs[0] = 192;
orig.addrs[1] = 168;
orig.addrs[2] = 1;
orig.addrs[3] = 1;
orig.addrs[4] = 10;
orig.addrs[5] = 0;
orig.addrs[6] = 0;
orig.addrs[7] = 1;
orig.addrs[0] = 192; orig.addrs[1] = 168; orig.addrs[2] = 1; orig.addrs[3] = 1;
orig.addrs[4] = 10; orig.addrs[5] = 0; orig.addrs[6] = 0; orig.addrs[7] = 1;
orig.addr_cnt = 8;
// === linked list ===
TestNode* n1 = u_malloc(sizeof(TestNode));
TestNode* n2 = u_malloc(sizeof(TestNode));
n1->value = 100;
n1->next = n2;
n2->value = 200;
n2->next = NULL;
orig.list = n1;
uint8_t header[] = {0x01, 0x02, 0x03, 0x04};
TEST("serialize_encode with header");
TEST("serialize_encode with header + linked list");
int r = serialize_encode(&orig, &test_schema, header, &buf, &len);
if (r != SERIALIZE_ERR_OK) FAIL("serialize_encode failed");
PASS();
@ -88,12 +100,8 @@ static void test_alloc_roundtrip(void) {
if (!restored) FAIL("restored is NULL");
PASS();
TEST("check id");
if (restored->id != orig.id) FAIL("id mismatch");
PASS();
TEST("check port");
if (restored->port != orig.port) FAIL("port mismatch");
TEST("check fixed fields");
if (restored->id != orig.id || restored->port != orig.port) FAIL("fixed fields mismatch");
PASS();
TEST("check asciiz");
@ -101,71 +109,87 @@ static void test_alloc_roundtrip(void) {
PASS();
TEST("check array");
if (!restored->addrs || restored->addrs[0] != 192) FAIL("addrs[0] mismatch");
if (!restored->addrs || restored->addrs[0] != 192) FAIL("addrs mismatch");
PASS();
TEST("check name_len");
if (restored->name_len != 5) FAIL("name_len mismatch");
TEST("check name_len / addr_cnt");
if (restored->name_len != 5 || restored->addr_cnt != 8) FAIL("len fields mismatch");
PASS();
TEST("check addr_cnt");
if (restored->addr_cnt != 8) FAIL("addr_cnt mismatch");
TEST("check linked list");
if (!restored->list || restored->list->value != 100) FAIL("list head mismatch");
if (!restored->list->next || restored->list->next->value != 200) FAIL("list next mismatch");
if (restored->list->next->next != NULL) FAIL("list tail not NULL");
PASS();
TEST("serialize_free");
TEST("serialize_free (full structure with list)");
serialize_free(&test_schema, (void**)&restored);
if (restored) FAIL("ptr should be NULL");
if (restored) FAIL("ptr should be NULL after free");
PASS();
u_free(buf);
// очистка orig
free(orig.name);
u_free(orig.addrs);
u_free(n1);
u_free(n2);
}
static void test_free(void) {
static void test_decode_error(void) {
uint8_t* buf = NULL;
size_t len;
TestStruct orig = {
.id = 2,
.port = 9000,
};
orig.name = strdup("test");
orig.name_len = 4;
orig.addrs = u_malloc(4);
orig.addrs[0] = 10;
orig.addrs[1] = 0;
orig.addrs[2] = 0;
orig.addrs[3] = 1;
orig.addr_cnt = 4;
uint8_t header2[] = {0xFF, 0xEE, 0xDD, 0xCC};
TEST("serialize_encode for free");
int r = serialize_encode(&orig, &test_schema, header2, &buf, &len);
if (r != SERIALIZE_ERR_OK) FAIL("serialize_encode failed");
PASS();
TestStruct orig = { .id = 42, .port = 1234, .list = NULL };
orig.name = strdup("error_test");
orig.name_len = 10;
orig.addrs = u_malloc(2);
orig.addrs[0] = 0xAA; orig.addrs[1] = 0xBB;
orig.addr_cnt = 2;
uint8_t header[] = {0xAA, 0xBB, 0xCC, 0xDD};
int r = serialize_encode(&orig, &test_schema, header, &buf, &len);
if (r != SERIALIZE_ERR_OK) FAIL("encode for error test failed");
PASS(); // не выводим, просто подготовка
TestStruct* restored = NULL;
TEST("serialize_decode for free");
uint8_t* data_ptr2 = buf + test_schema.header_len;
r = serialize_decode(data_ptr2, len - test_schema.header_len, &test_schema, (void**)&restored);
if (r != SERIALIZE_ERR_OK) FAIL("serialize_decode failed");
PASS();
TEST("serialize_free");
serialize_free(&test_schema, (void**)&restored);
if (restored) FAIL("ptr should be NULL");
uint8_t* data_ptr = buf + test_schema.header_len;
size_t short_len = (len - test_schema.header_len) / 2; // намеренно неполный буфер
TEST("serialize_decode on short buffer (should free internally)");
r = serialize_decode(data_ptr, short_len, &test_schema, (void**)&restored);
if (r != SERIALIZE_ERR_BUF) FAIL("expected SERIALIZE_ERR_BUF on truncated data");
if (restored != NULL) FAIL("restored must be NULL after error (free was called)");
PASS();
u_free(buf);
free(orig.name);
u_free(orig.addrs);
}
static void test_free_null_and_double(void) {
TestStruct* p = NULL;
TEST("serialize_free on NULL pointer");
serialize_free(&test_schema, (void**)&p); // должно ничего не делать
if (p != NULL) FAIL("NULL pointer was changed");
PASS();
// уже освобождённая структура
TestStruct* dummy = u_malloc(sizeof(TestStruct));
memset(dummy, 0, sizeof(TestStruct));
TEST("serialize_free after already freed");
serialize_free(&test_schema, (void**)&dummy);
if (dummy != NULL) FAIL("pointer not set to NULL");
PASS();
}
/* ------------------------------------------------------------------ */
int main(void) {
printf("=== Serialize Library Tests (updated for new len_type) ===\n\n");
printf("=== Serialize Library Tests (linked list + error handling) ===\n\n");
test_alloc_roundtrip();
test_free();
test_decode_error();
test_free_null_and_double();
printf("\n=== All tests passed ===\n");
return 0;

Loading…
Cancel
Save