Algorithm-Hamming-Perl
view release on metacpan or search on metacpan
#
foreach $key (sort { $a <=> $b } keys %Hamming8raw) {
$char = pack("B*",$key);
$Hamming8semi{$char} = $Hamming8raw{$key};
}
%Hamming8rev = reverse(%Hamming8semi);
# hamming_faster - this subroutine builds two large hashes of,
# %Hamming8by2 2 byte data -> 3 byte Hamming code
# %Hamming8by2rev 3 byte Hamming code -> 2 byte data
# for faster encodings and decodings. Running this subroutine is
# optional. If it is used then conversions are faster, however more
# memory is used to store the hashes, and a couple of seconds is added
# to the startup time. If it is not used, conversions are slower -
# taking up to 5 times the usual time. A good measure is the data you
# with to encode - more than 1 Mb would benifit from this subroutine.
#
sub hamming_faster {
#
# Step through 0,0 to 255,255 to build a hash that can convert
# any 2 byte combinations.
#
for ($x=0; $x<256; $x++) {
for ($y=0; $y<256; $y++) {
### Convert numbers into 2 bytes
$char1 = chr($x);
$char2 = chr($y);
$chars = $char1 . $char2;
### Generating 24 bit Hamming code
$ham_text = $Hamming8semi{$char1} .
$Hamming8semi{$char2};
### Make 3 byte Hamming code
$char_out = pack("B*",$ham_text);
### Add to hash
$Hamming8by2{$chars} = $char_out;
}
}
%Hamming8by2rev = reverse(%Hamming8by2);
}
# hamming - this turns data into hamming code. This has been written
# with memory and CPU efficiency in mind (without resorting to C).
#
sub hamming {
my $data = shift; # input data
my $pos; # counter to step through data string
my $char_in1; # first input byte
my $char_in2; # second input byte
my $chars_in; # both input bytes
my $ham_text; # hamming code in binary text "0101.."
my $char_out; # hamming code as bytes
my $output = ""; # full output hamming code as bytes
my $length = length($data);
#
# Step through the $data 2 bytes at a time, generating a
# Hamming encoded $output.
#
for ($pos = 0; $pos < ($length-1); $pos+=2) {
$chars_in = substr($data,$pos,2);
if (defined $Hamming8by2{$chars_in}) {
#
# Fast method
#
$output .= $Hamming8by2{$chars_in};
} else {
#
# Slow method
#
### Get both chars
$char_in1 = substr($data,$pos,1);
$char_in2 = substr($data,$pos+1,1);
### Make a 24 bit hamming binary number
$ham_text = $Hamming8semi{$char_in1} .
$Hamming8semi{$char_in2};
### Turn this number into 3 bytes
$char_out = pack("B*",$ham_text);
### Add to output
$output .= $char_out;
}
}
#
# A leftover byte (if present) is padded with 0's.
#
if ($length == ($pos + 1)) {
### Get the last character
$char_in1 = substr($data,$pos,1);
### Generate padded hamming text
$ham_text = $Hamming8semi{$char_in1} . "0000";
### Turn into 2 bytes
$char_out = pack("B*",$ham_text);
### Add to output
$output .= $char_out;
}
return $output;
}
# unhamming_err - this turns hamming code into data. This has been written
# with memory and CPU efficiencu in mind (without resorting to C).
#
sub unhamming_err {
my $data = shift; # input data
my $pos; # counter to step through data string
my $err; # corrected bit error
my $chars_in; # input bytes
my $ham_text; # hamming code in binary text "0101..", 2 bytes
my $ham_text1; # hamming code for first byte
my $ham_text2; # hamming code for second byte
my $char_out1; # output data byte 1
my $char_out2; # output data byte 2
my $output = ""; # full output data as bytes
my $err_all = 0; # count of corrected bit errors
my $length = length($data);
#
# Step through the $data 3 bytes at a time, decoding it back into
# the $output data.
#
for ($pos = 0; $pos < ($length-2); $pos+=3) {
### Fetch 3 bytes
$chars_in = substr($data,$pos,3);
if (defined $Hamming8by2rev{$chars_in}) {
#
# Fast method
#
$output .= $Hamming8by2rev{$chars_in};
} else {
#
# Slow method
#
### Fetch the 2 Hamming codes
$ham_text = unpack("B*",$chars_in);
$ham_text1 = substr($ham_text,0,12);
$ham_text2 = substr($ham_text,12);
### Convert each code into the original byte
($char_out1,$err) = unhamchar($ham_text1);
$err_all += $err;
($char_out2,$err) = unhamchar($ham_text2);
$err_all += $err;
### Add bytes to output
$output .= $char_out1 . $char_out2;
}
}
#
# Decode leftover bytes (if present).
#
if ($length == ($pos + 2)) {
### Fetch the 2 leftover bytes
$chars_in = substr($data,$pos,2);
### Fetch the Hamming code
$ham_text = unpack("B*",$chars_in);
$ham_text1 = substr($ham_text,0,12);
### Convert the code to the original byte
($char_out1,$err) = unhamchar($ham_text1);
$err_all += $err;
### Add byte to output
$output .= $char_out1;
}
return ($output,$err_all);
}
# unhamming - this is a wrapper around unhamming_err that just returns
# the data.
#
sub unhamming {
my $data = shift;
my ($output,$err);
($output,$err) = unhamming_err($data);
return $output;
}
# unhamchar - this takes a hamming code as binary text "0101..." and returns
# both the char and number (0 or 1) to represent if correction
# occured.
#
sub unhamchar {
my $text = shift;
my $pos = 0; # counter
my $err = 0; # error bit position
my ($bit);
### If okay, return now
if (defined $Hamming8rev{$text}) {
return ($Hamming8rev{$text},0);
}
### Find error bit
my $copy = $text;
while ($copy ne "") {
$pos++;
$bit = chop($copy);
if ($bit eq "1") {
$err = $err ^ $pos;
}
}
### Correct error bit
$copy = $text;
if ($err <= 12) {
$bit = substr($copy,-$err,1);
are auto corrected.
=item unhamming_err (SCALAR)
Returns the original data from the provided Hamming code, and a number counting
the number of bytes that were corrected. Single bit errors are auto corrected.
=back
=head1 OTHER SUBROUTINES
=over 4
=item Algorithm::Hamming::Perl::hamming_faster ()
This is an optional subroutine that will speed Hamming encoding if it is
run once at the start of the program. It does this by using a larger (hash)
cache of preprocessed results. The disadvantage is that it uses more memory,
and can add several seconds to invocation time. Only use this if you are
encoding more than 1 Mb of data.
=back
=head1 INSTALLATION
perl Makefile.PL
make
make test
make install
=head1 DEPENDENCIES
ExtUtils::MakeMaker
=head1 EXAMPLES
See the example perl programs provided with this module "example*".
An encoding and decoding example,
use Algorithm::Hamming::Perl qw(hamming unhamming);
$data = "Hello";
$hamcode = hamming($data);
$original = unhamming($hamcode);
=head1 LIMITATIONS
This is Perl only and can be slow. The Hamming encoding used can only
repair a single bit error within a byte - ie if two bits are damaged within
the one byte then this encoding cannot auto correct the error.
=head1 BUGS
Try not to join Hamming encoded strings together - this may give results
that look like a bug. If an odd number of input byes is encoded, the output
code is short half a byte - and so is padded with '0' bits. Joining these
with a string concatenation will contain the padding bits that will confuse
decoding.
The above problem can occur when inputing and outputing certain lengths
to filehandles. To be safe, my example code uses a buffer of 3072 bytes -
a safe size to use with filehandles.
=head1 COPYRIGHT
Copyright (c) 2003 Brendan Gregg. All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself
=head1 AUTHOR
Brendan Gregg <brendan.gregg@tpg.com.au>
[Sydney, Australia]
=cut
( run in 1.098 second using v1.01-cache-2.11-cpan-39bf76dae61 )