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