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);
}

      

+2


source to share


3 answers


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);

      

+2


source


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");

?>

      

+2


source


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;
}

      

+1


source







All Articles