zlibにおけるcrc 32のプラットフォーム間問題について


crc検査
学校の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つの方法をサポートします.
  • 元の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で以下のテストをした.
    // 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  
    結果は非常に奇妙で、しばらく原因が分からないので、とにかく気をつけたほうがいいです.