Dispersion Design

< Back

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