2017-05-07 3 views
1

私の目的は、ネットワークバイトオーダーで64ビットの符号なし整数で始まるネットワーク上でデータグラムを送信することです。それはそう64ビットの符号なしintをcharバッファに変換するCの前後

uint64_t deserialize_uint64(unsigned char *buffer) { 
    uint64_t res = 0; 
    printf("*** deserializing buffer:\n"); 
    int i; 
    for (i = 0; i < 8; i++) 
    printf("bufer[%d] = %x\n", i, buffer[i]); 
    for (i = 0; i < 8; i++) 
    res |= buffer[i] << (56 - 8 * i); 


    return res; 
} 

unsigned char * serialize_uint64(unsigned char *buffer, uint64_t value) { 
    printf("**** seriializing PRIu64 value = %"PRIu64"\n", value); 
    int i; 
    for (i = 0; i < 8; i++) 
    buffer[i] = (value >> (56 - 8 * i)) & 0xFF; 
    for (i = 0; i < 8; i++) 
    printf("bufer[%d] = %x\n", i, buffer[i]); 

    return buffer + 8; 
} 

は、その後、私はそれをデシリアライズ:

#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) 
#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) 

は、その後、私はバッファにそれをシリアライズ:だから最初に私はビッグエンディアンに番号を変換するためにマクロを使用します小さな整数でも動作しますが、次のテストコードが正常に動作していません:

uint64_t a = (uint64_t) time(NULL); 
printf("PRIu64: a =%"PRIu64"\n", a); 

uint64_t z = htonll(a); 
uint64_t zz = ntohll(z); 
printf("z = %"PRIu64" ==> zz = %"PRIu64" \n", z, zz); 

unsigned char buffer[1024]; 
serialize_uint64(buffer, z); 
uint64_t b = deserialize_uint64(buffer); 
uint64_t c = ntohll(g); 

私が得るよう

a = 1494157850 
htonll(a) = 1876329069679738880 ==> ntohll(htonll(a)) = 1494157850 
**** seriializing PRIu64 value = 1876329069679738880 
bufer[0] = 1a 
bufer[1] = a 
bufer[2] = f 
bufer[3] = 59 
bufer[4] = 0 
bufer[5] = 0 
bufer[6] = 0 
bufer[7] = 0 
********* 
*** deserializing buffer: 
bufer[0] = 1a 
bufer[1] = a 
bufer[2] = f 
bufer[3] = 59 
bufer[4] = 0 
bufer[5] = 0 
bufer[6] = 0 
bufer[7] = 0 
===> res = 436866905 
c = 6417359100811673600 

バッファが大きく数をキャプチャされていないように思え...

+0

編集:32bitの場合は '2,147,483,647'までうまく動作しますが、' 2,147,483,648'の場合はクラッシュします – micsza

答えて

1

あなたのシリアライザは、本質的に

unsigned char *serialize_u64(unsigned char *buffer, uint64_t value) 
{ 
    buffer[7] = value & 0xFF; 
    value >>= 8; 
    buffer[6] = value & 0xFF; 
    value >>= 8; 
    buffer[5] = value & 0xFF; 
    value >>= 8; 
    buffer[4] = value & 0xFF; 
    value >>= 8; 
    buffer[3] = value & 0xFF; 
    value >>= 8; 
    buffer[2] = value & 0xFF; 
    value >>= 8; 
    buffer[1] = value & 0xFF; 
    value >>= 8; 
    buffer[0] = value & 0xFF; 
    return buffer + 8; 
} 

であり、それはネイティブのバイトオーダーからvalueをシリアライズネットワークバイトオーダー。マクロは必要ありません。

したがって、OPのserialize_uint64()は正常に動作するはずです。バイトオーダーマクロをまったく使用しないでください。

OPのdeserialize_uint64()は、シフトされた結果が64ビットであることを保証するために、シフトする前にbuffer[i]から(uint64_t)をキャストする必要があります。個人的に、私はOPの代わりにres |= ((uint64_t)buffer[i]) << (56 - 8 * i);を使用した場合、OPのような等価の操作を行い

unsigned char *serialize_u64(unsigned char *buffer, uint64_t *valueptr) 
{ 
    uint64_t value = buffer[0]; 
    value <<= 8; 
    value |= buffer[1]; 
    value <<= 8; 
    value |= buffer[2]; 
    value <<= 8; 
    value |= buffer[3]; 
    value <<= 8; 
    value |= buffer[4]; 
    value <<= 8; 
    value |= buffer[5]; 
    value <<= 8; 
    value |= buffer[6]; 
    value <<= 8; 
    value |= buffer[7]; 
    *valueptr = value; 
    return buffer + 8; 
} 

とデシリアライザを書くことを好みます。ここでも、シリアライザとデシリアライザの両方で、ネットバイトオーダーとの間でネットワークバイトオーダーとの間でデータの変換が行われています。バイトオーダーマクロはまったく使用しないでください。

+0

'uint64_t'へのキャストは助けになりました - 多くのありがとう!しかし、バイトオーダーマクロでは、これを移植可能にしたいと思っていて、実行マシンのエンディアンが何であるか分かりません。 – micsza

+0

@micsza:はい、何か不足しています。私が説明したように、コードは、ネイティブバイトオーダーから/オーナネットワークバイトオーダーへの/からの値を変換します。ネイティブバイトオーダーが何であるかは関係ありません。コード自体は完全に移植可能です。 –

+0

@micsza:なぜバイトオーダーの変換が必要ないのかについては、関係する数学を調べる必要があります。ネットワークバイトオーダーでは、値の最初のバイトが最も重要なバイトです。 64ビット値の場合、ビット56〜63を含みます。関数が実装する数学では、ビットシフトとマスキングを使用して、64ビット値からこのバイト値と他のバイト値を直接抽出します。ネイティブバイトオーダーは無関係です。なぜなら、64ビット値で行われる計算はネイティブバイトオーダーに全く依存しないからです。 –

関連する問題