package Convert::Ascii85;

use warnings;
use strict;

our $VERSION = '0.01';

use Exporter qw(import);

our @EXPORT_OK = qw(ascii85_encode ascii85_decode);

my $_space_no = unpack 'N', ' ' x 4;

sub encode {
	my ($in, $opt) = @_;
	my $compress_zero = exists $opt->{compress_zero} ? $opt->{compress_zero} : 1;
	my $compress_space = $opt->{compress_space};

	my $padding = -length($in) % 4;
	$in .= "\0" x $padding;
	my $out = '';

	for my $n (unpack 'N*', $in) {
		if ($n == 0 && $compress_zero) {
			$out .= 'z';
			next;
		}
		if ($n == $_space_no && $compress_space) {
			$out .= 'y';
			next;
		}

		my $tmp = '';
		for my $i (reverse 0 .. 4) {
			my $mod = $n % 85;
			$n = int($n / 85);
			vec($tmp, $i, 8) = $mod + 33;
		}
		$out .= $tmp;
	}

	$padding or return $out;

	$out =~ s/z\z/!!!!!/;
	substr $out, 0, length($out) - $padding
}

*ascii85_encode = \&encode;

sub decode {
	my ($in) = @_;
	for ($in) {
		tr[ \t\r\n\f][]d;
		s/z/!!!!!/g;
		s/y/+<VdL/g;
	}

	my $padding = -length($in) % 5;
	$in .= 'u' x $padding;
	my $out = '';

	for my $n (unpack '(a5)*', $in) {
		my $tmp = 0;
		for my $i (unpack 'C*', $n) {
			$tmp *= 85;
			$tmp += $i - 33;
		}
		$out .= pack 'N', $tmp;
	}

	substr $out, 0, length($out) - $padding
}

*ascii85_decode = \&decode;

1

__END__

=head1 NAME

Convert::Ascii85 - Encoding and decoding of ascii85/base85 strings

=head1 SYNOPSIS

 use Convert::Ascii85;
 
 my $encoded = Convert::Ascii85::encode($data);
 my $decoded = Convert::Ascii85::decode($encoded);

 use Convert::Ascii85 qw(ascii85_encode ascii85_decode);
 
 my $encoded = ascii85_encode($data);
 my $decoded = ascii85_decode($encoded);

=head1 DESCRIPTION

This module implements the I<Ascii85> (also known as I<Base85>) algorithm for
encoding binary data as text. This is done by interpreting each group of four
bytes as a 32-bit integer, which is then converted to a five-digit base-85
representation using the digits from ASCII 33 (C<!>) to 117 (C<u>).

This is similar to L<MIME::Base64> but more space efficient: The overhead is
only 1/4 of the original data (as opposed to 1/3 for Base64).

=head1 FUNCTIONS

=over 4

=item Convert::Ascii85::encode DATA

=item Convert::Ascii85::encode DATA, OPTIONS

Converts the bytes in DATA to Ascii85 and returns the resulting text string.
OPTIONS is a hash reference in which the following keys may be set:

=over

=item compress_zero => 0

By default, four-byte chunks of null bytes (C<"\0\0\0\0">) are converted to
C<'z'> instead of C<'!!!!!'>. This can be avoided by passing a false value for
C<compress_zero> in OPTIONS.

=item compress_space => 1

By default, four-byte chunks of spaces (C<'    '>) are converted to C<'+<VdL'>.
If you pass a true value for C<compress_space> in OPTIONS, they will be
converted to C<'y'> instead.

=back

This function may be exported as C<ascii85_encode> into the caller's namespace.

=item Convert::Ascii85::decode TEXT

Converts the Ascii85-encoded TEXT back to bytes and returns the resulting byte
string. Spaces and linebreaks in TEXT are ignored.

This function may be exported as C<ascii85_decode> into the caller's namespace.

=back

=head1 SEE ALSO

L<http://en.wikipedia.org/wiki/Ascii85>,
L<MIME::Base64>

=head1 AUTHOR

Lukas Mai, C<< <l.mai at web.de> >>

=head1 BUGS

Please report any bugs or feature requests to C<bug-convert-ascii85 at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Convert-Ascii85>.  I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.

=head1 SUPPORT

You can find documentation for this module with the perldoc command.

    perldoc Convert::Ascii85

You can also look for information at:

=over 4

=item * RT: CPAN's request tracker

L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=Convert-Ascii85>

=item * AnnoCPAN: Annotated CPAN documentation

L<http://annocpan.org/dist/Convert-Ascii85>

=item * CPAN Ratings

L<http://cpanratings.perl.org/d/Convert-Ascii85>

=item * Search CPAN

L<http://search.cpan.org/dist/Convert-Ascii85/>

=back

=head1 LICENSE AND COPYRIGHT

Copyright 2011 Lukas Mai.

This program is free software; you can redistribute it and/or modify it
under the terms of either: the GNU General Public License as published
by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.