Browse Source
- serialize.h/c: universal binary serialization for structures with variable-length fields (ASCIIZ, ARRAY) using pointers - Auto allocation via u_malloc during deserialization - Support for header (arbitrary bytes at buffer start) - serialize_encode(): serialize structure to buffer - serialize_decode(): deserialize from buffer (without header) - serialize_free(): free structure and all its pointers - tests/test_serialize.c: unit testscongestion
3 changed files with 510 additions and 0 deletions
@ -0,0 +1,234 @@ |
|||||||
|
// serialize.c - Universal binary serialization with dynamic allocation
|
||||||
|
#include "serialize.h" |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
static uint16_t varint_encode(uint8_t* out, uint16_t val) { |
||||||
|
if (val < 0x80) { |
||||||
|
if (out) out[0] = (uint8_t)val; |
||||||
|
return 1; |
||||||
|
} |
||||||
|
if (out) { |
||||||
|
out[0] = (uint8_t)(0x80 | (val >> 8)); |
||||||
|
out[1] = (uint8_t)(val & 0xFF); |
||||||
|
} |
||||||
|
return 2; |
||||||
|
} |
||||||
|
|
||||||
|
static uint16_t varint_decode(const uint8_t* in, size_t in_len, size_t* consumed) { |
||||||
|
if (!in || in_len < 1) { |
||||||
|
if (consumed) *consumed = 0; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if ((in[0] & 0x80) == 0) { |
||||||
|
if (consumed) *consumed = 1; |
||||||
|
return in[0]; |
||||||
|
} |
||||||
|
if (in_len < 2) { |
||||||
|
if (consumed) *consumed = 0; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
if (consumed) *consumed = 2; |
||||||
|
return ((in[0] & 0x3F) << 8) | in[1]; |
||||||
|
} |
||||||
|
|
||||||
|
static size_t get_len_field(const uint8_t* data, const struct SerializeField* f) { |
||||||
|
if (!f || !data || !f->has_len) return 0; |
||||||
|
const uint8_t* len_ptr = data + f->len_offset; |
||||||
|
if (f->len_type == 1) { |
||||||
|
return *len_ptr; |
||||||
|
} |
||||||
|
if (f->len_type == 2) { |
||||||
|
return *(uint16_t*)len_ptr; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
static void set_len_field(uint8_t* data, const struct SerializeField* f, size_t len) { |
||||||
|
if (!f || !data || !f->has_len) 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; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static size_t fixed_field_size(const struct SerializeSchema* schema, size_t idx) { |
||||||
|
if (idx + 1 < schema->field_count) { |
||||||
|
return schema->fields[idx + 1].offset - schema->fields[idx].offset; |
||||||
|
} |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int serialize_encode(const void* structure, |
||||||
|
const struct SerializeSchema* schema, |
||||||
|
const uint8_t* header, |
||||||
|
uint8_t** out_buf, |
||||||
|
size_t* out_len) { |
||||||
|
if (!schema || !structure || !out_buf || !out_len) return SERIALIZE_ERR_NULL; |
||||||
|
|
||||||
|
const uint8_t* data = (const uint8_t*)structure; |
||||||
|
size_t total = schema->header_len; |
||||||
|
|
||||||
|
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 if (f->elem_size == 1) { |
||||||
|
const char* str = *(const char**)(data + f->offset); |
||||||
|
size_t len = f->has_len ? get_len_field(data, f) : (str ? strlen(str) : 0); |
||||||
|
if (len > f->max_size) len = f->max_size; |
||||||
|
total += varint_encode(NULL, (uint16_t)len); |
||||||
|
total += len + 1; |
||||||
|
} else { |
||||||
|
size_t len = f->has_len ? get_len_field(data, f) : f->max_size; |
||||||
|
if (len > f->max_size) len = f->max_size; |
||||||
|
total += varint_encode(NULL, (uint16_t)len); |
||||||
|
total += len * f->elem_size; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t* buf = u_malloc(total); |
||||||
|
if (!buf) return SERIALIZE_ERR_BUF; |
||||||
|
|
||||||
|
uint8_t* ptr = buf; |
||||||
|
if (header && schema->header_len > 0) { |
||||||
|
memcpy(ptr, header, schema->header_len); |
||||||
|
ptr += schema->header_len; |
||||||
|
} |
||||||
|
|
||||||
|
for (size_t i = 0; i < schema->field_count; i++) { |
||||||
|
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); |
||||||
|
memcpy(ptr, fld, sz); |
||||||
|
ptr += sz; |
||||||
|
} else if (f->elem_size == 1) { |
||||||
|
const char* str = *(const char**)fld; |
||||||
|
size_t len = f->has_len ? get_len_field(data, f) : (str ? strlen(str) : 0); |
||||||
|
if (len > f->max_size) len = f->max_size; |
||||||
|
ptr += varint_encode(ptr, (uint16_t)len); |
||||||
|
if (len > 0) { |
||||||
|
memcpy(ptr, str, len); |
||||||
|
ptr += len; |
||||||
|
} |
||||||
|
*ptr++ = 0; |
||||||
|
} else { |
||||||
|
const uint8_t* arr = *(const uint8_t**)fld; |
||||||
|
size_t len = f->has_len ? get_len_field(data, f) : f->max_size; |
||||||
|
if (len > f->max_size) len = f->max_size; |
||||||
|
ptr += varint_encode(ptr, (uint16_t)len); |
||||||
|
if (arr && len > 0) { |
||||||
|
memcpy(ptr, arr, len * f->elem_size); |
||||||
|
ptr += len * f->elem_size; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
*out_buf = buf; |
||||||
|
*out_len = ptr - buf; |
||||||
|
return SERIALIZE_ERR_OK; |
||||||
|
} |
||||||
|
|
||||||
|
int serialize_decode(const uint8_t* in_buf, |
||||||
|
size_t in_len, |
||||||
|
const struct SerializeSchema* schema, |
||||||
|
void** structure) { |
||||||
|
if (!in_buf || !schema || !structure) return SERIALIZE_ERR_NULL; |
||||||
|
|
||||||
|
void* obj = u_malloc(schema->struct_size); |
||||||
|
if (!obj) { |
||||||
|
*structure = NULL; |
||||||
|
return SERIALIZE_ERR_BUF; |
||||||
|
} |
||||||
|
|
||||||
|
const uint8_t* ptr = in_buf; |
||||||
|
const uint8_t* end = in_buf + in_len; |
||||||
|
uint8_t* data = (uint8_t*)obj; |
||||||
|
size_t consumed; |
||||||
|
|
||||||
|
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) { |
||||||
|
u_free(obj); |
||||||
|
return SERIALIZE_ERR_BUF; |
||||||
|
} |
||||||
|
memcpy(data + f->offset, ptr, sz); |
||||||
|
ptr += sz; |
||||||
|
} else if (f->elem_size == 1) { |
||||||
|
uint16_t len = varint_decode(ptr, end - ptr, &consumed); |
||||||
|
if (len > f->max_size) len = f->max_size; |
||||||
|
ptr += consumed; |
||||||
|
size_t copy_len = len < (size_t)(end - ptr) ? len : (size_t)(end - ptr); |
||||||
|
|
||||||
|
char* str = NULL; |
||||||
|
if (len > 0) { |
||||||
|
str = u_malloc(len + 1); |
||||||
|
if (str) { |
||||||
|
if (copy_len > 0) memcpy(str, ptr, copy_len); |
||||||
|
str[copy_len] = 0; |
||||||
|
} |
||||||
|
} else { |
||||||
|
str = u_malloc(1); |
||||||
|
if (str) str[0] = 0; |
||||||
|
} |
||||||
|
*(char**)(data + f->offset) = str; |
||||||
|
if (f->has_len) { |
||||||
|
set_len_field(data, f, len); |
||||||
|
} |
||||||
|
ptr += len + 1; |
||||||
|
} else { |
||||||
|
uint16_t len = varint_decode(ptr, end - ptr, &consumed); |
||||||
|
if (len > f->max_size) len = f->max_size; |
||||||
|
ptr += consumed; |
||||||
|
size_t arr_sz = len * f->elem_size; |
||||||
|
|
||||||
|
uint8_t* arr = NULL; |
||||||
|
if (arr_sz > 0) { |
||||||
|
arr = u_malloc(arr_sz); |
||||||
|
if (arr && ptr + arr_sz <= end) { |
||||||
|
memcpy(arr, ptr, arr_sz); |
||||||
|
} |
||||||
|
} |
||||||
|
*(uint8_t**)(data + f->offset) = arr; |
||||||
|
if (f->has_len) { |
||||||
|
set_len_field(data, f, len); |
||||||
|
} |
||||||
|
ptr += arr_sz; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
*structure = obj; |
||||||
|
return SERIALIZE_ERR_OK; |
||||||
|
} |
||||||
|
|
||||||
|
void serialize_free(const struct SerializeSchema* schema, void** structure) { |
||||||
|
if (!schema || !structure || !*structure) return; |
||||||
|
uint8_t* data = (uint8_t*)*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; |
||||||
|
} |
||||||
|
} 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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
u_free(*structure); |
||||||
|
*structure = NULL; |
||||||
|
} |
||||||
@ -0,0 +1,102 @@ |
|||||||
|
/**
|
||||||
|
* @file serialize.h |
||||||
|
* @brief Бинарная сериализация структур с динамическим выделением памяти |
||||||
|
*
|
||||||
|
* Поддерживает структуры с полями переменной длины (ASCIIZ, ARRAY) через указатели. |
||||||
|
* При десериализации автоматически выделяет память через u_malloc. |
||||||
|
*
|
||||||
|
* Формат буфера: |
||||||
|
* [header_len байт][сериализованные данные] |
||||||
|
*
|
||||||
|
* Пример использования: |
||||||
|
* @code |
||||||
|
* // Сериализация
|
||||||
|
* uint8_t header[] = {0x01, 0x02, 0x03, 0x04}; |
||||||
|
* serialize_encode(&orig, &schema, header, &buf, &len); |
||||||
|
*
|
||||||
|
* // Десериализация (данные без заголовка)
|
||||||
|
* uint8_t* data = buf + schema.header_len; |
||||||
|
* serialize_decode(data, len - schema.header_len, &schema, &restored); |
||||||
|
*
|
||||||
|
* // Освобождение
|
||||||
|
* serialize_free(&schema, &restored); |
||||||
|
* u_free(buf); |
||||||
|
* @endcode |
||||||
|
*/ |
||||||
|
#ifndef SERIALIZE_H |
||||||
|
#define SERIALIZE_H |
||||||
|
|
||||||
|
#include <stdint.h> |
||||||
|
#include <stddef.h> |
||||||
|
#include "../lib/mem.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Описание одного поля структуры для сериализации |
||||||
|
*/ |
||||||
|
struct SerializeField { |
||||||
|
uint16_t offset; ///< смещение указателя в структуре
|
||||||
|
uint8_t elem_size; ///< 0=fixed, 1=ASCIIZ, >1=ARRAY (размер элемента в байтах)
|
||||||
|
uint16_t max_size; ///< макс. длина для ASCIIZ / макс. кол-во для ARRAY
|
||||||
|
uint8_t has_len; ///< 1 если есть length field
|
||||||
|
uint8_t len_type; ///< 1=UINT8, 2=UINT16
|
||||||
|
uint16_t len_offset; ///< смещение length поля в структуре
|
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Схема структуры для сериализации |
||||||
|
*/ |
||||||
|
struct SerializeSchema { |
||||||
|
uint16_t field_count; ///< число полей
|
||||||
|
uint16_t struct_size; ///< размер структуры для allocate
|
||||||
|
uint16_t header_len; ///< длина заголовка (произвольные байты в начале)
|
||||||
|
const struct SerializeField* fields; ///< массив описаний полей
|
||||||
|
}; |
||||||
|
|
||||||
|
#define SERIALIZE_ERR_OK 0 ///< успех
|
||||||
|
#define SERIALIZE_ERR_BUF -1 ///< ошибка выделения памяти
|
||||||
|
#define SERIALIZE_ERR_NULL -2 ///< null указатель
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Сериализация структуры в бинарный буфер |
||||||
|
* @param structure указатель на исходную структуру |
||||||
|
* @param schema схема структуры |
||||||
|
* @param header заголовок (или NULL если header_len=0) |
||||||
|
* @param out_buf [out] выходной буфер (выделяется через u_malloc) |
||||||
|
* @param out_len [out] размер буфера |
||||||
|
* @return SERIALIZE_ERR_OK или код ошибки |
||||||
|
*
|
||||||
|
* Формат выходного буфера: |
||||||
|
* [header_len байт][сериализованные данные] |
||||||
|
*/ |
||||||
|
int serialize_encode(const void* structure, |
||||||
|
const struct SerializeSchema* schema, |
||||||
|
const uint8_t* header, |
||||||
|
uint8_t** out_buf, |
||||||
|
size_t* out_len); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Десериализация из буфера в структуру |
||||||
|
* @param in_buf буфер БЕЗ заголовка (уже смещённый на header_len) |
||||||
|
* @param in_len размер данных (без header) |
||||||
|
* @param schema схема структуры |
||||||
|
* @param structure [out] указатель на восстановленную структуру |
||||||
|
* @return SERIALIZE_ERR_OK или код ошибки |
||||||
|
*
|
||||||
|
* Внимание: in_buf должен быть без заголовка! |
||||||
|
* Буфер со смещением: uint8_t* data = buf + schema.header_len; |
||||||
|
* Длина без header: size_t data_len = total_len - schema.header_len; |
||||||
|
*/ |
||||||
|
int serialize_decode(const uint8_t* in_buf, size_t in_len, |
||||||
|
const struct SerializeSchema* schema, |
||||||
|
void** structure); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Освобождение структуры и всех её указателей |
||||||
|
* @param schema схема структуры |
||||||
|
* @param structure [inout] указатель на структуру (устанавливается в NULL) |
||||||
|
*
|
||||||
|
* Освобождает структуру и все её динамические указатели (ASCIIZ, ARRAY). |
||||||
|
*/ |
||||||
|
void serialize_free(const struct SerializeSchema* schema, void** structure); |
||||||
|
|
||||||
|
#endif // SERIALIZE_H
|
||||||
@ -0,0 +1,174 @@ |
|||||||
|
// test_serialize.c - Unit tests for serialize library with dynamic allocation
|
||||||
|
|
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <string.h> |
||||||
|
#include <assert.h> |
||||||
|
|
||||||
|
#include "../lib/serialize.h" |
||||||
|
|
||||||
|
#define TEST(name) do { \ |
||||||
|
printf("TEST: %-40s ", name); fflush(stdout); \
|
||||||
|
} while(0) |
||||||
|
|
||||||
|
#define PASS() do { puts("PASS"); } while(0) |
||||||
|
#define FAIL(msg) do { printf("FAIL: %s\n", msg); exit(1); } while(0) |
||||||
|
|
||||||
|
#pragma pack(push, 1) |
||||||
|
typedef struct { |
||||||
|
uint8_t id; |
||||||
|
uint16_t port; |
||||||
|
uint32_t ip; |
||||||
|
uint64_t big_val; |
||||||
|
int8_t signed_val; |
||||||
|
int32_t signed32; |
||||||
|
uint8_t name_len; |
||||||
|
char* name; |
||||||
|
uint8_t addr_cnt; |
||||||
|
uint8_t* addrs; |
||||||
|
} TestStruct; |
||||||
|
#pragma pack(pop) |
||||||
|
|
||||||
|
static const struct SerializeField test_fields[] = { |
||||||
|
{offsetof(TestStruct, id), 0, 0, 0, 0, 0}, |
||||||
|
{offsetof(TestStruct, port), 0, 0, 0, 0, 0}, |
||||||
|
{offsetof(TestStruct, ip), 0, 0, 0, 0, 0}, |
||||||
|
{offsetof(TestStruct, big_val), 0, 0, 0, 0, 0}, |
||||||
|
{offsetof(TestStruct, signed_val), 0, 0, 0, 0, 0}, |
||||||
|
{offsetof(TestStruct, signed32), 0, 0, 0, 0, 0}, |
||||||
|
{offsetof(TestStruct, name), 1, 64, 1, 1, offsetof(TestStruct, name_len)}, |
||||||
|
{offsetof(TestStruct, addrs), 1, 16, 1, 1, offsetof(TestStruct, addr_cnt)}, |
||||||
|
}; |
||||||
|
|
||||||
|
static const struct SerializeSchema test_schema = { |
||||||
|
.field_count = sizeof(test_fields) / sizeof(test_fields[0]), |
||||||
|
.struct_size = sizeof(TestStruct), |
||||||
|
.header_len = 4, |
||||||
|
.fields = test_fields |
||||||
|
}; |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */ |
||||||
|
static void test_alloc_roundtrip(void) { |
||||||
|
uint8_t* buf = NULL; |
||||||
|
size_t len; |
||||||
|
|
||||||
|
TestStruct orig = { |
||||||
|
.id = 1, |
||||||
|
.port = 443, |
||||||
|
.ip = 0x01020304, |
||||||
|
.big_val = 100, |
||||||
|
.signed_val = 10, |
||||||
|
.signed32 = 5000, |
||||||
|
}; |
||||||
|
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.addr_cnt = 8; |
||||||
|
|
||||||
|
uint8_t header[] = {0x01, 0x02, 0x03, 0x04}; |
||||||
|
TEST("serialize_encode with header"); |
||||||
|
int r = serialize_encode(&orig, &test_schema, header, &buf, &len); |
||||||
|
if (r != SERIALIZE_ERR_OK) FAIL("serialize_encode failed"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
TestStruct* restored = NULL; |
||||||
|
TEST("serialize_decode (data without header)"); |
||||||
|
uint8_t* data_ptr = buf + test_schema.header_len; |
||||||
|
r = serialize_decode(data_ptr, len - test_schema.header_len, &test_schema, (void**)&restored); |
||||||
|
if (r != SERIALIZE_ERR_OK) FAIL("serialize_decode failed"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
TEST("check restored not NULL"); |
||||||
|
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"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
TEST("check asciiz"); |
||||||
|
if (!restored->name || strcmp(restored->name, "hello") != 0) FAIL("name mismatch"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
TEST("check array"); |
||||||
|
if (!restored->addrs || restored->addrs[0] != 192) FAIL("addrs[0] mismatch"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
TEST("check name_len"); |
||||||
|
if (restored->name_len != 5) FAIL("name_len mismatch"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
TEST("check addr_cnt"); |
||||||
|
if (restored->addr_cnt != 8) FAIL("addr_cnt mismatch"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
TEST("serialize_free"); |
||||||
|
serialize_free(&test_schema, (void**)&restored); |
||||||
|
if (restored) FAIL("ptr should be NULL"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
u_free(buf); |
||||||
|
} |
||||||
|
|
||||||
|
static void test_free(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* 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"); |
||||||
|
PASS(); |
||||||
|
|
||||||
|
u_free(buf); |
||||||
|
free(orig.name); |
||||||
|
u_free(orig.addrs); |
||||||
|
} |
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */ |
||||||
|
int main(void) { |
||||||
|
printf("=== Serialize Library Tests ===\n\n"); |
||||||
|
|
||||||
|
test_alloc_roundtrip(); |
||||||
|
test_free(); |
||||||
|
|
||||||
|
printf("\n=== All tests passed ===\n"); |
||||||
|
return 0; |
||||||
|
} |
||||||
Loading…
Reference in new issue