C – 序列化技术
我正在编写一些代码来序列化一些数据通过networking发送。 目前,我使用这个原始程序:
- 创build一个
void*
缓冲区 - 对我想通过networking发送的数据应用任何字节sorting操作,如
hton
系列 - 使用
memcpy
将内存复制到缓冲区中 - 通过networking发送内存
问题在于,对于各种数据结构(通常包含void *数据,所以您不知道是否需要关心字节sorting),代码变得非常臃肿,序列化代码对于每个数据结构都是非常特定的,不能完全重用。
C的一些好的序列化技术是什么使这更容易/不那么难看?
–
注:我绑定到一个特定的协议,所以我不能自由select如何序列化我的数据。
对于每个数据结构,都有一个serialize_X函数(其中X是结构体名称),它指向一个X和一个指向不透明缓冲区结构的指针,并调用相应的序列化函数。 你应该提供一些诸如serialize_int之类的原语来写入缓冲区并更新输出索引。 原语必须调用像reserve_space(N),其中N是写入任何数据之前所需的字节数。 reserve_space()将重新分配void *缓冲区,使其至less与当前大小加上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; } }
从这个angular度来看,实现所有需要的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是否被认为是索引的合理types)。 此外,没有规定的error handling,没有function释放缓冲区后,你必须自己做这个。 我只是在演示一下我将要使用的基本架构。
我会说绝对不要尝试自己实现序列化。 已经完成了数十万次,您应该使用现有的解决scheme。 例如protobufs: https : //github.com/protobuf-c/protobuf-c
它还具有与许多其他编程语言兼容的优点。
如果我们知道协议的约束条件,这将有所帮助,但总的来说,你的select是非常有限的。 如果数据是这样的,你可以为每个结构做一个字节数组sizeof(struct)的联合,这可能会简化一些事情,但从你的描述来看,这听起来像是你有一个更重要的问题:如果你转移指针void * data),那么这些点在接收机上是不太可能有效的。 为什么数据碰巧出现在内存中的相同位置?
我build议使用库(并避免重新发明轮子)。
这是一个使用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);