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.
348 lines
12 KiB
348 lines
12 KiB
// serialize.c - Universal binary serialization with dynamic allocation |
|
#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); |
|
out[1] = (uint8_t)val; |
|
} |
|
return 2; |
|
} |
|
|
|
static uint16_t length_decode(const uint8_t* in, size_t in_len, size_t* consumed) { |
|
if (!in || in_len < 2) { |
|
if (consumed) *consumed = 0; |
|
return 0; |
|
} |
|
if (consumed) *consumed = 2; |
|
return ((uint16_t)in[0] << 8) | in[1]; |
|
} |
|
|
|
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->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_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->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, |
|
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->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); |
|
count = str ? (uint32_t)strlen(str) : 0; |
|
} else { |
|
count = get_var_count(data, f); |
|
} |
|
uint16_t len = (count > 0xFFFFu) ? 0xFFFFu : (uint16_t)count; |
|
total += 2; |
|
if (f->elem_size == 1) { |
|
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; |
|
|
|
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->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; |
|
} |
|
|
|
ptr += length_encode(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 += 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); |
|
} |
|
} |
|
} |
|
|
|
*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) { |
|
if (structure) *structure = NULL; |
|
return SERIALIZE_ERR_NULL; |
|
} |
|
|
|
void* obj = u_malloc(schema->struct_size); |
|
if (!obj) { |
|
*structure = NULL; |
|
return SERIALIZE_ERR_BUF; |
|
} |
|
memset(obj, 0, schema->struct_size); |
|
|
|
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->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; |
|
} |
|
} 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); |
|
} |
|
*(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) goto decode_error; |
|
ptr += consumed; |
|
|
|
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 { |
|
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; |
|
} |
|
|
|
if (f->type >= SERIALIZE_TYPE_ARRAY_U8 && f->type <= SERIALIZE_TYPE_ARRAY_U32) { |
|
set_var_count(data, f, len); |
|
} |
|
} else if (f->type == SERIALIZE_TYPE_LINKED) { |
|
uint16_t len = length_decode(ptr, end - ptr, &consumed); |
|
if (consumed == 0) goto decode_error; |
|
ptr += consumed; |
|
|
|
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; |
|
} |
|
|
|
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; |
|
} |
|
|
|
memcpy(new_node, ptr, f->elem_size); |
|
// обязательно обнуляем next в новом узле |
|
*(void**)((uint8_t*)new_node + f->len_offset) = NULL; |
|
|
|
if (!head) { |
|
head = new_node; |
|
} else { |
|
*(void**)((uint8_t*)prev + f->len_offset) = new_node; |
|
} |
|
prev = new_node; |
|
ptr += f->elem_size; |
|
} |
|
|
|
*(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) { |
|
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->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; |
|
} |
|
*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; |
|
} |
|
} |
|
} |
|
|
|
u_free(*structure); |
|
*structure = NULL; |
|
}
|
|
|