C – методы сериализации

Я пишу код для сериализации некоторых данных для отправки по сети. В настоящее время я использую эту примитивную процедуру:

  1. создать буфер void*
  2. применять любые операции упорядочения байтов, такие как семейство hton по данным, которые я хочу отправить по сети
  3. используйте memcpy для копирования памяти в буфер
  4. отправлять память по сети

Проблема в том, что с различными структурами данных (которые часто содержат данные void *, поэтому вы не знаете, нужно ли вам заботиться о порядке байтов), код становится действительно раздутым с кодом сериализации, который очень специфичен для каждой структуры данных и не может быть повторно используется вообще.

Каковы некоторые хорошие методы сериализации для C, которые делают это проще / менее уродливым?

Примечание. Я привязан к определенному протоколу, поэтому я не могу свободно выбирать, как сериализовать мои данные.

Для каждой структуры данных используйте функцию serialize_X (где X – это имя структуры), которая принимает указатель на X и указатель на непрозрачную структуру буфера и вызывает соответствующие функции сериализации. Вы должны указать некоторые примитивы, такие как serialize_int, которые записывают в буфер и обновляют выходной индекс. Примитивам нужно будет вызвать что-то вроде reserve_space (N), где N – это количество байтов, которое требуется перед записью любых данных. reserve_space () перераспределяет буфер void *, чтобы сделать его как минимум большим, чем текущий размер плюс N байтов. Чтобы сделать это возможным, структура буфера должна содержать указатель на фактические данные, индекс для записи следующего байта (индекс вывода) и размер, который выделяется для данных. В этой системе все ваши функции serialize_X должны быть довольно простыми, например:

 struct X { int n, m; char *string; } void serialize_X(struct X *x, struct Buffer *output) { serialize_int(x->n, output); serialize_int(x->m, output); serialize_string(x->string, output); } 

И код frameworks будет выглядеть примерно так:

 #define INITIAL_SIZE 32 struct Buffer { void *data; int next; size_t size; } struct Buffer *new_buffer() { struct Buffer *b = malloc(sizeof(Buffer)); b->data = malloc(INITIAL_SIZE); b->size = INITIAL_SIZE; b->next = 0; return b; } void reserve_space(Buffer *b, size_t bytes) { if((b->next + bytes) > b->size) { /* double size to enforce O(lg N) reallocs */ b->data = realloc(b->data, b->size * 2); b->size *= 2; } } 

Из этого, должно быть довольно просто реализовать все функции serialize_ (), которые вам нужны.

EDIT: Например:

 void serialize_int(int x, Buffer *b) { /* assume int == long; how can this be done better? */ x = htonl(x); reserve_space(b, sizeof(int)); memcpy(((char *)b->data) + b->next, &x, sizeof(int)); b->next += sizeof(int); } 

EDIT: Также обратите внимание, что в моем коде есть некоторые потенциальные ошибки. Размер массива буфера хранится в size_t, но индекс является int (я не уверен, что size_t считается разумным типом для индекса). Кроме того, нет никаких предустановок для обработки ошибок и нет функции освобождения буфера после того, как вы закончите, поэтому вам придется сделать это самостоятельно. Я просто продемонстрировал базовую архитектуру, которую я бы использовал.

Я бы сказал, что определенно не пытайтесь реализовать сериализацию самостоятельно. Это было сделано за миллион раз, и вы должны использовать существующее решение. например protobufs: https://github.com/protobuf-c/protobuf-c

Это также имеет то преимущество, что оно совместимо со многими другими языками программирования.

Это помогло бы, если бы мы знали, что такое ограничения протокола, но в целом ваши варианты действительно очень ограничены. Если данные таковы, что вы можете сделать объединение байтового массива sizeof (struct) для каждой структуры, это может упростить вещи, но из вашего описания это звучит так, как будто у вас есть более существенная проблема: если вы передаете указатели (вы упоминаете void *), то эти точки вряд ли будут действительны на принимающей машине. Почему данные будут отображаться в одном месте в памяти?

Я предлагаю использовать библиотеку.

Поскольку я был недоволен существующими, я создал библиотеку Binn, чтобы облегчить нашу жизнь.

Вот пример его использования:

  binn *obj; // create a new object obj = binn_object(); // add values to it binn_object_set_int32(obj, "id", 123); binn_object_set_str(obj, "name", "Samsung Galaxy Charger"); binn_object_set_double(obj, "price", 12.50); binn_object_set_blob(obj, "picture", picptr, piclen); // send over the network send(sock, binn_ptr(obj), binn_size(obj)); // release the buffer binn_free(obj); 
  • Могу ли я заставить XmlSerializer игнорировать пространство имен при десериализации?
  • Почему Java нуждается в интерфейсе Serializable?
  • Циркулярная ссылка обнаружила исключение при сериализации объекта в JSON
  • Как использовать Gson API Google для десериализации JSON правильно?
  • JSON.NET как сериализатор OAP для WebAPI 2 и ODataMediaTypeFormatter
  • Имеются ли атрибуты DataContract для WCF
  • Gson сериализует список полиморфных объектов
  • Обтекание с помощью раздела CData - Сериализация XML C #
  • Сериализовать значение nullable int
  • Сделать пользовательский class Serializable в Objective-c / iPhone?
  • Передача сложных навигационных параметров с помощью MvvmCross ShowViewModel
  • Давайте будем гением компьютера.