ネットワーク上で送信するデータをシリアル化するコードを書いています。現在、私はこの原始的な手順を使用しています:
void*
バッファーを作成しますhton
ファミリなどのバイト順序操作を適用しますmemcpy
を使用して、メモリをバッファにコピーします問題は、さまざまなデータ構造(多くの場合void *データが含まれているため、バイト順を気にする必要があるかどうかわからない)で、コードがveryそれぞれに固有のシリアル化コードで本当に肥大化することですデータ構造であり、まったく再利用できません。
これを簡単にする/ lessくしないCの優れたシリアル化手法は何ですか?
-
注:特定のプロトコルにバインドされているため、データをシリアル化する方法を自由に選択できません。
各データ構造に対して、Xへのポインターと不透明なバッファー構造へのポインターを受け取り、適切なシリアル化関数を呼び出すserialize_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がインデックスの適切なタイプと見なされるかどうかはわかりません)。また、エラー処理のための準備がなく、完了後にバッファを解放する機能がないため、これを自分で行う必要があります。使用する基本的なアーキテクチャのデモを行っただけです。
ライブラリを使用することをお勧めします。
私は既存のものに満足していなかったので、私たちの生活を楽にするために 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);
シリアル化を自分で実装しようとしないでください。これは何十億回も行われているため、既存のソリューションを使用する必要があります。例えばprotobufs: https://github.com/protobuf-c/protobuf-c
また、他の多くのプログラミング言語と互換性があるという利点もあります。
プロトコルの制約が何であるかを知っていれば役立ちますが、一般的に、オプションは非常に限られています。データが構造体ごとにバイト配列sizeof(struct)の和集合を作成できるようなものである場合、物事を単純化するかもしれませんが、説明からは、より本質的な問題があるように聞こえます:ポインターを転送する場合(言及void * data)これらのポイントが受信側のマシンで有効になる可能性は非常に低いです。データがメモリ内の同じ場所に表示されるのはなぜですか?