Php: How to calculate Adler32 checksum for zip?
I am using a combination of Paul Duncans php ZipStream ( http://pablotron.org/software/zipstream-php/ ) on the server side, for generating zips and Fzip on the fly ( http://codeazur.com.br/lab/fzip/ ) client side Flex / Air.
Works fine in the air, but when running Flex in a browser, the zip must include the Adler32 checksum in the FZip read header.
How can I calculate Adler32 checksum for zip in php?
Core ZipStream functions using gzdeflate for compression can be seen below.
Regards / Jonas
function add_file($name, $data, $opt = array(), $deflateLevel=0) {
# compress data
$zdata = gzdeflate($data, $deflateLevel);
# calculate header attributes
$crc = crc32($data);
$zlen = strlen($zdata);
$len = strlen($data);
$meth = 0x08;
# send file header
$this->add_file_header($name, $opt, $meth, $crc, $zlen, $len);
# print data
$this->send($zdata);
}
private function add_file_header($name, $opt, $meth, $crc, $zlen, $len) {
# strip leading slashes from file name
# (fixes bug in windows archive viewer)
$name = preg_replace('/^\\/+/', '', $name);
# calculate name length
$nlen = strlen($name);
# create dos timestamp
$opt['time'] = $opt['time'] ? $opt['time'] : time();
$dts = $this->dostime($opt['time']);
# build file header
$fields = array( # (from V.A of APPNOTE.TXT)
array('V', 0x04034b50), # local file header signature
array('v', (6 << 8) + 3), # version needed to extract
array('v', 0x00), # general purpose bit flag
array('v', $meth), # compresion method (deflate or store)
array('V', $dts), # dos timestamp
array('V', $crc), # crc32 of data
array('V', $zlen), # compressed data length
array('V', $len), # uncompressed data length
array('v', $nlen), # filename length
array('v', 0), # extra data len
);
# pack fields and calculate "total" length
$ret = $this->pack_fields($fields);
$cdr_len = strlen($ret) + $nlen + $zlen;
# print header and filename
$this->send($ret . $name);
# add to central directory record and increment offset
$this->add_to_cdr($name, $opt, $meth, $crc, $zlen, $len, $cdr_len);
}
source to share
Intended for example example in Wikipedia article :
define('MOD_ADLER', 65521);
function adler32($data) {
$a = 1; $b = 0; $len = strlen($data);
for ($index = 0; $index < $len; ++$index) {
$a = ($a + $data[$index]) % MOD_ADLER;
$b = ($b + $a) % MOD_ADLER;
}
return ($b << 16) | $a;
}
And to convert that integer value to bytes:
pack('H*', $checksum);
source to share
For PHP 5> = 5.1.2, you can use the hash function , which returns the hexadecimal string representation of crc:
$dataStr = "abc";
$crcStr = hash('adler32', $dataStr);
UPDATE: There was a bug in the middle of the hash until mid 2009 where the byte order was wrong. The bug seems to be fixed in 5.2.11 and 5.3.0 onwards. But for earlier versions, the byte order of the result must be changed.
UPDATE 2: Here's a wrapper function (and test) that can be used to work around this error:
<?php
error_reporting(E_ALL);
function hash_adler32_wrapper($data) {
$digHexStr = hash("adler32", $data);
// If version is better than 5.2.11 no further action necessary
if (version_compare(PHP_VERSION, '5.2.11', '>=')) {
return $digHexStr;
}
// Workaround #48284 by swapping byte order
$boFixed = array();
$boFixed[0] = $digHexStr[6];
$boFixed[1] = $digHexStr[7];
$boFixed[2] = $digHexStr[4];
$boFixed[3] = $digHexStr[5];
$boFixed[4] = $digHexStr[2];
$boFixed[5] = $digHexStr[3];
$boFixed[6] = $digHexStr[0];
$boFixed[7] = $digHexStr[1];
return implode("", $boFixed);
}
// Test fixture, plus expected output generated using the adler32 from zlib
$data_in = "abc";
$expected_out = 0x024d0127;
// PHP hash function returns a hex hash value as a string so hexdec used to
// convert to number
$hash_out = hexdec(hash("adler32", $data_in));
// Get value via the wrapper function
$wrapper_out = hexdec(hash_adler32_wrapper($data_in));
printf("data_in: %s\n", $data_in);
printf("expected_out: 0x%08x\n", $expected_out);
printf("builtin hash out: 0x%08x, %s\n", $hash_out,
($hash_out == $expected_out)? "OK" : "NOT OK");
printf("wrapper func out: 0x%08x, %s\n", $wrapper_out,
($wrapper_out == $expected_out)? "OK" : "NOT OK");
?>
source to share
Gumbo's solution is almost perfect - but if the parameter is a string instead of a byte array, you will need to use ord () to get the ascii of the character being processed. Like this:
function adler32($data) {
$a = 1; $b = 0; $len = strlen($data);
for ($index = 0; $index < $len; ++$index) {
$a = ($a + ord($data[$index])) % 65521;
$b = ($b + $a) % 65521;
}
return ($b << 16) | $a;
}
source to share