谈谈zlib中crc32的跨平台问题

crc校验

结合学校的crc校验的基础知识,直接使用zlib/crc32函数应该一点门槛都没有。。下面是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校验支持两种方式:

  1. 原始buffer,传入crc32生成校验码,然后将校验码追加到buffer尾部发送或者写文件。读取时分别解析原始buffer和校验码,再次做crc32.,看结果与接收到的校验码是否一致,来判断buffer是否完好无损。
  2. 原始buffer,crc32做校验,然后将校验码取反追加到buffer尾部发送或者写文件。读取到整个内容(buffer+crcReverseCode),做crc校验看结果是否是0xFFFFFFFF。

方式1操作起来有点呆板,我们一般采用方式2,既然比对目标是0xFFFFFFFF,那么说明crc32处理的一定是32位的数字

我的疑问

今天再次看到这段代码,不由地有个疑问,crc32的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 <stdio.h>
#include <stdlib.h>
#include <zlib.h>
#include <string.h>

void CRC_Partial_Check_Test();
template<class Type> 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<unsigned int>();
	CRC_Full_Check_Test<ulong_t>();

	printf("any key pressed to exit...\n");
	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\n");

	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\n", 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<sizeof(ulong_t); k++)
			{
				printf("%d ", pch[k]);
				trans.buf[k] = pch[k];
			} printf("\n");

			printf("crc_code : %lu\n", trans.val);
			crc_code = trans.val;
		}		

		if ( crc32(0, content, buffer_sz) != crc_code )
		{
			printf("ERROR content.\n");
		}
		else
		{
			printf("Good Content.\n");
		}

		fclose(fp);
	}
}

template<class Type>
void CRC_Full_Check_Test()
{
	printf("=============================>>CRC_Full_ChecK_Test\n");

	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\n", 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\n", trans.val);

		}

		ulong_t res = crc32(0, content, read_bytes);	printf("res = %lu\n", res);

		if ( res != 0xFFFFFFFF && res != 0xFFFFFFFFFFFFFFFF )
		{
			printf("ERROR content.\n");
		}
		else
		{
			printf("Good Content.\n");
		}

		fclose(fp);
	}
}

CRC_Partial_Check_Test为上文中的方式1,CRC_Full_Check_Test为方式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也没有问题。为毛?crc32校验码为unsigned int类型,写文件时提升成unsigned long 64位,即从低到高八个字节为:8 190 65 252 0 0 0 0,然后从文件读取,最后比较都是uLong类型,不影响结果。

CRC_Full_Check_Test<unsigned int>()就是我们正常使用的方式, CRC_Full_Check_Test<uLong>调用时crc校验失败,问题出在crcCode取反操作上,提升成uLong以后取反结果非常大。再次对整个buffer做crc校验失败~_~。

诡异的移位结果

在测试过程中发现了一个移位的坑:

ulong_t code = 0xfc << 24;
unsigned int ui_code = 0xfc << 24;
printf("ulong_res = %lu, uint_res = %lu\n", code, ui_code);

code = 0xfc << 8;
ui_code = 0xfc << 8;
printf("ulong_res = %lu, uint_res = %lu\n", code, ui_code);

输出结果如下:

ulong_res = 18446744073642442752, uint_res = 4227858432
ulong_res = 64512, uint_res = 64512

第一行uLong_res输出结果很奇怪。通过dump_val_hex函数输出一个数字的16进制形式:

template<class Type> 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 <class Type> void dump_val_hex(Type val) // 0xfc000000 => printf : fc 00 00 00
{
	if (val == 0)
	{
		printf("%02x\n", 0);
		return;
	}

	dump_val_hex_recur(val); printf("\n");
}

18446744073642442752对应的16进制形式为ff ff ff ff fc 00 00 00 。诡异的移位结果,居然高位被1填充。但是0xfc << 8 的结果确实高位被0填充。

时间: 2024-11-10 07:03:34

谈谈zlib中crc32的跨平台问题的相关文章

谈谈python 中__name__ = &#39;__main__&#39; 的作用

position:static(静态定位) 当position属性定义为static时,可以将元素定义为静态位置,所谓静态位置就是各个元素在HTML文档流中应有的位置 podisition定位问题.所以当没有定义position属性时,并不说明该元素没有自己的位置,它会遵循默认显示为静态位置,在静态定位状态下无法通过坐标值(top,left,right,bottom)来改变它的位置. position:absolute(绝对定位) 当position属性定义为absolute时,元素会脱离文档流

【原】谈谈css中关于元素定位的属性(positon&z-index)

position position主要是4种设定: static(默认)如果不设置,默认就是staitc元素.其实就是没有position设定,DOM流安排在哪里就是哪里.所有的相关于位置的设定均无效,意味着left, right, z-index等属性设置了也是白设. relative如果只是设置postion: relative; 和static并没有视觉上的区别,但是,一旦设定了,就指定了对应DOM元素"被定位"了,之后就可以设置一些诸如right,left之类的属性. abso

谈谈c++中继承中的虚函数

c++继 承中的虚函数 c++是一种面向对象的编程语言的一个很明显的体现就是对继承机制的支持,c++中继承分很多种,按不同的分类有不同分类方法,比如可以按照基类的个数分为多继承和单继承,可以按照访问权限分为public继承.protected继承和private继承,按照是否是虚拟继承可以分为virtual继承和non-virtual继承.当然这里的分类标准都是有重叠的部分,比如,non-virtual继承又可以分为单继承和多继承.这里要讨论的是虚函数,因此主要从virtual和non-virt

谈谈python中的遍历

谈谈python中的遍历python中也有像java中的for...each....在python中是for...in... 其实感觉差不多,不过python的规则更灵活一点,只要是可以进行迭代的对象都能使用for...in....那么问题来了,什么是可迭代的对象呢?可以使用collections模块的Iterable来判断. from collections import Iterable flag = isinstance('adc',Iterable) print(flag) flag =

用实例谈谈javascript中的this和prototype

本文通过几个实例谈谈js中一些基础概念的问题.首先回顾一下js这门语言的特点:除了对象什么都没有.函数在js语言中被看作一种特殊的数据类型,特殊在包含代码并且可以执行,但落点在它是一种数据类型,与数字.字符串等一样.    理解了这个,那么js语言中的全局作用域和函数作用域.全局变量和局部变量.可以通过全局对象和非全局对象划分.如果函数或其他数据类型,不依托非全局变量,那么就默认依托全局变量,即作为全局对象的属性或方法,否则被当作局部对象方法或属性.this被用作指向属性和方法所依托的对象.搞清

谈谈java中静态变量与静态方法继承的问题

谈谈java中静态变量与静态方法继承的问题 学习的中如果遇到不明白或者不清楚的的时候,就是自己做些测试,自己去试试,这次我就做一个关于静态变量的继承和静态方法继承问题的测试. 首先我先建一个父类: 这样我在建一个子类: 这些都准备好以后,我对子类创建对象,然后用  类名.静态变量/静态方法  和  对象名.静态方法/静态变量  对他们输出的结果进行测试. 这样输出种类有: 这样我输出的结果是: 这样来总结一下: 1. 通过类名来调用子类中的静态变量和静态方法,当父类与子类相同时是,子类会隐藏父类

谈谈JS中的面向对象

请先看看下面这段代 1 <script src="jquery.js"></script> 2 <script type="text/javascript"> 3 /** 4 * Object.create() 最近才添加进了ECMAScript第5版规范,有些浏览器不支持 5 * 这里模拟一个Object.create方法解决兼容性问题 6 * Object.create : 该方法只有一个参数,即原型对象,返回一个新对象 7

谈谈java中成员变量与成员方法继承的问题

谈谈java中成员变量与成员方法继承的问题 关于成员变量和成员方法的的继承问题,我也可以做一个小测试,来看看结果. 首先我们先创建一个父类: 其次再创建一个子类,子类中要比父类中少一个成员方法: 这样我们对子类创建对象,如果不创建对象,我们是无法访问子类的成员变量和成员方法的,因为“无法从静态上下文中引用非静态方法”.我们要输出有: 从以上的数据中,我们就可以得出的输出结果是 这样我们就可以得出结论. 总结: 1.   在结果中可以看出,子类的成员变量和成员方法,与其父类的相同的时候,子类就会覆

谈谈Vim中实用又好记的一些命令

本文的目的在于总结一些日常操作中比较实用.有规律的Vim命令,而不致于介绍一些基础的Vim知识,比如几种插入模式,hjkl移动命令,dd删除本行,p粘贴 等等,故对Vim基本知识不够熟悉的请参见其他教程. 另外,强烈给大家推荐一款强大的Vim插件集“spf13-vim”,这是一款Vim插件集,它把常用的一些Vim插件结合起来了,满足大部分人的需求,开箱即用,而不用自己一个一个的去配置Vim插件,处理各种按键冲突等等,免去你花大量的时间用于配置Vim软件本身(比如博主最初学习Vim的时候),而不是