私の意見では、関数 like の主な欠点は、htonl()
シリアル化の半分の作業しか行わないことです。マシンがリトルエンディアンの場合、マルチバイト整数でバイトを反転するだけです。シリアライズ時に実行しなければならないもう 1 つの重要なことは、アライメントの処理ですが、これらの関数はそれを行いません。
多くの CPU は、アドレスがバイト単位の整数の倍数ではないメモリ位置に格納されていないマルチバイト整数に (効率的に) アクセスできません。これが、構造体オーバーレイを使用してネットワーク パケットを (非) シリアル化しない理由です。これが「インプレース変換」の意味かどうかはわかりません。
私は組み込みシステムで多くの作業を行っており、ネットワーク パケット (またはその他の I/O: ディスク、RS232 など) を生成または解析するときに常に使用する独自のライブラリに関数があります。
/* Serialize an integer into a little or big endian byte buffer, resp. */
void SerializeLeInt(uint64_t value, uint8_t *buffer, size_t nrBytes);
void SerializeBeInt(uint64_t value, uint8_t *buffer, size_t nrBytes);
/* Deserialize an integer from a little or big endian byte buffer, resp. */
uint64_t DeserializeLeInt(const uint8_t *buffer, size_t nrBytes);
uint64_t DeserializeBeInt(const uint8_t *buffer, size_t nrBytes);
これらの関数に加えて、次のように定義された一連のマクロがあります。
#define SerializeBeInt16(value, buffer) SerializeBeInt(value, buffer, sizeof(int16_t))
#define SerializeBeUint16(value, buffer) SerializeBeInt(value, buffer, sizeof(uint16_t))
#define DeserializeBeInt16(buffer) DeserializeBeType(buffer, int16_t)
#define DeserializeBeUint16(buffer) DeserializeBeType(buffer, uint16_t)
(デ) シリアル化関数は値をバイト単位で読み書きするため、アラインメントの問題は発生しません。署名についても心配する必要はありません。まず第一に、最近のすべてのシステムは 2 の補数を使用します (いくつかの ADC を除いて、おそらくこれらの関数は使用されません)。ただし、(私の知る限り)符号付き整数は符号なしにキャストされると2の補数に変換されるため(および関数は符号なし整数を受け入れ/返すため)、1の補数を使用するシステムでも機能するはずです。
あなたの別の議論は、それらが8ビットバイトとexact-sizeの存在に依存しているということですuint_N_t
。これは私の関数にも当てはまりますが、私の意見では、これは問題ではありません (これらの型は、私が使用するシステムとそのコンパイラに対して常に定義されています)。関数プロトタイプを調整して、必要に応じて or の代わりに使用するunsigned char
こともできます。uint8_t
long long
uint_least64_t
uint64_t