C - методы сериализации - PullRequest
30 голосов
/ 14 мая 2011

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

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

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

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

-

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

Ответы [ 4 ]

33 голосов
/ 14 мая 2011

Для каждой структуры данных имеется функция 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);
}

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

#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_ ().

РЕДАКТИРОВАТЬ: Например:

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);
}

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

5 голосов
/ 13 ноября 2015

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

Поскольку я не был доволен существующими, я создал библиотеку 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);
4 голосов
/ 14 мая 2011

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

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

2 голосов
/ 14 мая 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...