zlibにおけるcrc 32のプラットフォーム間問題について
crc検査
学校のcrc検査の基礎知識と結びつけて、zlib/crc 32関数を直接使うのは少しも敷居がないはずです.次はzlibです.hに添付された例と説明:
crcチェックは2つの方法をサポートします.元のbufferは、crc 32に入力されて検証コードを生成し、bufferの末尾に検証コードを追加してファイルを送信または書き込みます.読み出し時に元のbufferと検証コードをそれぞれ解析し、再びcrc 32を行う.結果が受信したチェックコードと一致するかどうかを見て、bufferが完全に破損していないかどうかを判断します. 元のbuffer、crc 32は検証を行い、その後、検証コードをbufferの末尾に逆追跡してファイルを送信または書き込みます.内容全体(buffer+crcReverseCode)を読み込み、crcをして結果が0 xFFFFFFFFFFかどうかをチェックします.
方式1操作が少しぼんやりしていて、
我々は一般的に方式2を採用し,ターゲットが0 xFFFFFFFFである以上crc 32が処理するのは32ビットの数字に違いないことを説明する.
私の疑問
今日再びこのコードを見て、crc 32の32が32ビットの意味である以上、なぜuLongを使うのかと疑問に思った.(uLongの定義はtypedef unsigned long uLong)では、uLongは明らかにプラットフォーム間性を持っていない.具体的には、unsigned longの問題はニマが穴をあけているのか分からない.この疑問を持って、64ビットlinuxで以下のテストをした.
関数の最初のブロックは1つの文字列を検証し、文字列と検証コードをバイナリファイルに書き込み、2番目のセグメントはバイナリファイルから文字列と検証コードを読み出し、crc検証を行って文字列が正しいかどうかを判断します.
プログラム出力結果:
CRC_Partial_Check_TestはuLongを使っても問題ありません.毛?crc 32チェックコードはunsigned intタイプで、ファイルを書くときにunsigned long 64ビットに昇格します.すなわち、8,190,65,252,0 0バイトの低いバイトから高いバイトまで、ファイルから読み取り、最後に比較するとuLongタイプで、結果に影響しません.
CRC_Full_Check_Test()は私たちが正常に使用している方法ですCRC_Full_Check_Test呼び出し時にcrcチェックに失敗し、問題はcrcCodeの取反操作にあり、uLongに昇格した後に取反結果が非常に大きい.再度buffer全体に対してcrcチェック失敗~~.
奇妙なシフト結果
テスト中にシフトしたピットが見つかりました.
18446744073642442752に対応する16進形はff ff ff ff fc 00 00である.奇妙なシフトの結果、なんと高位が1で埋められた.しかし0 xfc<<8の結果は確かに高位が0で充填された.
vs 2013で同様の試みを行い、同様の結果も得られた.
学校のcrc検査の基礎知識と結びつけて、zlib/crc 32関数を直接使うのは少しも敷居がないはずです.次はzlibです.hに添付された例と説明:
Update a running CRC-32 with the bytes buf[0..len-1] and return the
updated CRC-32. If buf is NULL, this function returns the required initial
value for the for the crc. Pre- and post-conditioning (one's complement) is
performed within this function so it shouldn't be done by the application.
Usage example:
uLong crc = crc32(0L, Z_NULL, 0);
while (read_buffer(buffer, length) != EOF) {
crc = crc32(crc, buffer, length);
}
if (crc != original_crc) error();
crcチェックは2つの方法をサポートします.
方式1操作が少しぼんやりしていて、
我々は一般的に方式2を採用し,ターゲットが0 xFFFFFFFFである以上crc 32が処理するのは32ビットの数字に違いないことを説明する.
私の疑問
今日再びこのコードを見て、crc 32の32が32ビットの意味である以上、なぜuLongを使うのかと疑問に思った.(uLongの定義はtypedef unsigned long uLong)では、uLongは明らかにプラットフォーム間性を持っていない.具体的には、unsigned longの問題はニマが穴をあけているのか分からない.この疑問を持って、64ビットlinuxで以下のテストをした.
// g++ crc_test.cpp -o exec_crc_test -L /usr/lib64/ -lz
#include
#include
#include
#include
void CRC_Partial_Check_Test();
template void CRC_Full_Check_Test();
// crc32 use uLong, typedef unsigned long uLong;
typedef uLong ulong_t; // 32bit, 64bit.
int main()
{
CRC_Partial_Check_Test();
CRC_Full_Check_Test();
CRC_Full_Check_Test();
printf("any key pressed to exit...
");
getchar();
return 0;
}
typedef union
{
ulong_t val;
unsigned char buf[sizeof(ulong_t)];
} trans_un_t;
void CRC_Partial_Check_Test()
{
printf("=============================>>CRC_Partial_ChecK_Test
");
char* buffer = "hello world, i am renyafei";
int buffer_sz = strlen(buffer);
{
FILE* fp = fopen("crc.dat", "wb");
fwrite(buffer, 1, buffer_sz, fp);
ulong_t crc_code = crc32(0, (const Bytef*)buffer, buffer_sz);
printf("crc_code : %lu
", crc_code);
fwrite(&crc_code, 1, sizeof(ulong_t), fp);
fflush(fp);
fclose(fp);
}
{
FILE* fp = fopen("crc.dat", "rb");
unsigned char content[1024] = {0};
int read_bytes = fread(content, 1, buffer_sz+sizeof(ulong_t), fp);
ulong_t crc_code = 0;
{
// get crc code
unsigned char* pch = content + buffer_sz;
trans_un_t trans;
for(int k=0; k
void CRC_Full_Check_Test()
{
printf("=============================>>CRC_Full_ChecK_Test
");
char* buffer = "hello world, i am renyafei";
int buffer_sz = strlen(buffer);
typedef Type dest_t;
{
FILE* fp = fopen("crc.dat", "wb");
fwrite(buffer, 1, buffer_sz, fp);
ulong_t crc = crc32(0, (const Bytef*)buffer, buffer_sz);
dest_t rever_crc = (dest_t)~crc;
printf("crc = %lu, reverse_crc_code : %lu
", crc, rever_crc);
fwrite(&rever_crc, 1, sizeof(dest_t), fp);
fflush(fp);
fclose(fp);
}
{
FILE* fp = fopen("crc.dat", "rb");
unsigned char content[1024] = {0};
int read_bytes = fread(content, 1, buffer_sz+sizeof(dest_t), fp);
{
// get crc code
unsigned char* pch = content + buffer_sz;
trans_un_t trans;
memcpy(trans.buf, pch, sizeof(ulong_t));
printf("reverse_crc_code : %lu
", trans.val);
}
ulong_t res = crc32(0, content, read_bytes); printf("res = %lu
", res);
if ( res != 0xFFFFFFFF && res != 0xFFFFFFFFFFFFFFFF )
{
printf("ERROR content.
");
}
else
{
printf("Good Content.
");
}
fclose(fp);
}
}
CRC_Partial_Check_Testは前述の方法1,CRC_Full_Check_Testは方式2です.関数の最初のブロックは1つの文字列を検証し、文字列と検証コードをバイナリファイルに書き込み、2番目のセグメントはバイナリファイルから文字列と検証コードを読み出し、crc検証を行って文字列が正しいかどうかを判断します.
プログラム出力結果:
=============================>>CRC_Partial_ChecK_Test
crc_code : 4232166920
8 190 65 252 0 0 0 0
crc_code : 4232166920
Good Content.
=============================>>CRC_Full_ChecK_Test
crc = 4232166920, reverse_crc_code : 62800375
reverse_crc_code : 62800375
res = 4294967295
Good Content.
=============================>>CRC_Full_ChecK_Test
crc = 4232166920, reverse_crc_code : 18446744069477384695
reverse_crc_code : 18446744069477384695
res = 558161692
ERROR content.
CRC_Partial_Check_TestはuLongを使っても問題ありません.毛?crc 32チェックコードはunsigned intタイプで、ファイルを書くときにunsigned long 64ビットに昇格します.すなわち、8,190,65,252,0 0バイトの低いバイトから高いバイトまで、ファイルから読み取り、最後に比較するとuLongタイプで、結果に影響しません.
CRC_Full_Check_Test()は私たちが正常に使用している方法ですCRC_Full_Check_Test呼び出し時にcrcチェックに失敗し、問題はcrcCodeの取反操作にあり、uLongに昇格した後に取反結果が非常に大きい.再度buffer全体に対してcrcチェック失敗~~.
奇妙なシフト結果
テスト中にシフトしたピットが見つかりました.
ulong_t code = 0xfc << 24;
unsigned int ui_code = 0xfc << 24;
printf("ulong_res = %lu, uint_res = %lu
", code, ui_code);
code = 0xfc << 8;
ui_code = 0xfc << 8;
printf("ulong_res = %lu, uint_res = %lu
", code, ui_code);
の出力結果は次のとおりです.ulong_res = 18446744073642442752, uint_res = 4227858432
ulong_res = 64512, uint_res = 64512
最初の行uLong_res出力結果がおかしい.dump_経由val_hex関数は16進数の形式を出力します.template void dump_val_hex_recur(Type val)
{
if (val == 0)
{
return;
}
unsigned char byte = val & 0xFF;
dump_val_hex_recur(val >> 8);
printf("%02x ", byte);
}
template void dump_val_hex(Type val) // 0xfc000000 => printf : fc 00 00 00
{
if (val == 0)
{
printf("%02x
", 0);
return;
}
dump_val_hex_recur(val); printf("
");
}
18446744073642442752に対応する16進形はff ff ff ff fc 00 00である.奇妙なシフトの結果、なんと高位が1で埋められた.しかし0 xfc<<8の結果は確かに高位が0で充填された.
vs 2013で同様の試みを行い、同様の結果も得られた.
unsigned long long val = 0xfc << 24;
00B332D8 mov dword ptr [val],0FC000000h
00B332DF mov dword ptr [ebp-68h],0FFFFFFFFh
val = 0xfc << 8;
009C32F6 mov dword ptr [val],0FC00h
009C32FD mov dword ptr [ebp-68h],0
結果は非常に奇妙で、しばらく原因が分からないので、とにかく気をつけたほうがいいです.