Adler-32 Calculations for the PNG Format
Written: 2012-04-21
Introduction
The PNG (Portable Network Graphics) image file format stores image data using the ZLIB compression format. ZLIB uses the Adler-32 algorithm to calculate a checksum that is stored at the end of the image data stream within the PNG file. When creating PNG files, it is necessary to be able to perform the Adler-32 algorithm calculations on data to obtain the required checksum value.
This article shows a working example of the Adler-32 algorithm, written in the Perl programming language. More details about the actual Adler-32 algorithm are available at various other locations on the internet.
Initialization
First, we need two persistant variables that keep track of the running Adler-32 value and a method to reset those variables back to their initial state:
package Adler32; our $s1 = 1; our $s2 = 0; sub clear { $s1 = 1; $s2 = 0; }
This code only keeps track of a single Adler-32 calculation at a time. If multiple calculations need to be performed simultaneously, you would want to encapsulate these functions in a class, where each instance of the class maintains it’s own variables.
Process the Data
Now we need a function that takes a string of bytes and steps through it one byte at a time. The Adler-32 calculation is performed repeatedly for each byte of data:
sub add { my($data_ptr) = @_; my @bytes = unpack("C*", $$data_ptr); foreach my $byte (@bytes) { $s1 = ($s1 + $byte) % 0xfff1; $s2 = ($s2 + $s1) % 0xfff1; } }
The data is passed into this function as a scalar reference ($data_ptr
),
often called a pointer in other programming languages. It then has to be de-referenced
($$
) when passed to the unpack()
function.
Getting the Check Value
Finally, we need a function to return the final Adler-32 check value:
sub result { return (($s2 << 16) | $s1); }
Putting it All Together
The complete Perl module for performing Adler-32 calculations is now:
package Adler32; our $s1 = 1; our $s2 = 0; sub clear { $s1 = 1; $s2 = 0; } sub add { my($data_ptr) = @_; my @bytes = unpack("C*", $$data_ptr); foreach my $byte (@bytes) { $s1 = ($s1 + $byte) % 0xfff1; $s2 = ($s2 + $s1) % 0xfff1; } } sub result { return (($s2 << 16) | $s1); } 1;
This code can be used in the following way:
#!/usr/bin/perl use Adler32; my $data = 'Hello World'; Adler32::clear(); Adler32::add(\$data); my $check_value = Adler32::result(); printf("Check value (in hex): %x\n", $check_value);
Remember, the data must be passed to the add()
function as a scalar reference
(a pointer).