Skip to content

Instantly share code, notes, and snippets.

@jtbonhomme
Created May 29, 2013 15:06
Show Gist options
  • Save jtbonhomme/5671005 to your computer and use it in GitHub Desktop.
Save jtbonhomme/5671005 to your computer and use it in GitHub Desktop.
perl digest crc16
package Digest::CRC;
use strict;
use vars qw($VERSION $XS_VERSION @ISA @EXPORT_OK %_typedef);
require Exporter;
@ISA = qw(Exporter);
@EXPORT_OK = qw(
crc8 crcccitt crc16 crc32 crc64 crc
crc_hex crc_base64
crcccitt_hex crcccitt_base64
crc8_hex crc8_base64
crc16_hex crc16_base64
crc32_hex crc32_base64
crc64_hex crc64_base64
);
$VERSION = '0.16';
$XS_VERSION = $VERSION;
$VERSION = eval $VERSION;
eval {
# PERL_DL_NONLAZY must be false, or any errors in loading will just
# cause the perl code to be tested
local $ENV{PERL_DL_NONLAZY} = 0 if $ENV{PERL_DL_NONLAZY};
require DynaLoader;
local @ISA = qw(DynaLoader);
bootstrap Digest::CRC $XS_VERSION;
1
};
sub _reflectperl {
my ($in, $width) = @_;
my $out = 0;
for(my $i=1; $i < ($width+1); $i++) {
$out |= 1 << ($width-$i) if ($in & 1);
$in=$in>>1;
}
$out;
}
# Only load the non-XS stuff on demand
defined &_crc or eval <<'ENOXS' or die $@;
sub _reflect($$) {
my ($in, $width) = @_;
my $out = 0;
for(my $i=1; $i < ($width+1); $i++) {
$out |= 1 << ($width-$i) if ($in & 1);
$in=$in>>1;
}
$out;
}
sub _tabinit($$$) {
my ($width,$poly_in,$ref) = @_;
my @crctab;
my $poly = $poly_in;
if ($ref) {
$poly = _reflect($poly,$width);
}
for (my $i=0; $i<256; $i++) {
my $r = $i<<($width-8);
$r = $i if $ref;
for (my $j=0; $j<8; $j++) {
if ($ref) {
$r = ($r>>1)^($r&1&&$poly)
} else {
if ($r&(1<<($width-1))) {
$r = ($r<<1)^$poly
} else {
$r = ($r<<1)
}
}
}
my $x=$r&2**$width-1;
push @crctab, $x;
}
\@crctab;
}
sub _crc($$$$$$$) {
my ($message,$width,$init,$xorout,$refin,$refout,$tab) = @_;
my $crc = $init;
if ($refin == 1) {
$crc = _reflect($crc,$width);
} elsif ($refin > 1 and $refin <= $width) {
$crc = _reflect($crc,$refin);
}
my $pos = -length $message;
my $mask = 2**$width-1;
while ($pos) {
if ($refin) {
$crc = ($crc>>8)^$tab->[($crc^ord(substr($message, $pos++, 1)))&0xff]
} else {
$crc = (($crc<<8))^$tab->[(($crc>>($width-8))^ord(substr $message,$pos++,1))&0xff]
}
}
if ($refout && !$refin) {
if ($refout == 1) {
$crc = _reflect($crc,$width);
} elsif ($refout > 1 and $refout <= $width) {
$crc = _reflect($crc,$refout);
}
}
$crc = $crc ^ $xorout;
$crc & $mask;
}
1;
ENOXS
%_typedef = (
# name, [width,init,xorout,refout,poly,refin);
crc8 => [8,0,0,0,0x07,0],
crcccitt => [16,0xffff,0,0,0x1021,0],
crc16 => [16,0,0,1,0x8005,1],
crc32 => [32,0xffffffff,0xffffffff,1,0x04C11DB7,1],
);
sub new {
my $that=shift;
my %params=@_;
my $class = ref($that) || $that;
my $self = {map { ($_ => $params{$_}) }
qw(type width init xorout poly refin refout cont)};
bless $self, $class;
$self->reset();
map { if (defined($params{$_})) { $self->{$_} = $params{$_} } }
qw(type width init xorout poly refin refout cont);
$self
}
sub reset {
my $self = shift;
my $typeparams;
# default is crc32 if no type and no width is defined
if (!defined($self->{type}) && !defined($self->{width})) {
$self->{type} = "crc32";
}
if (defined($self->{type}) && exists($_typedef{$self->{type}})) {
$typeparams = $_typedef{$self->{type}};
$self->{width} = $typeparams->[0],
$self->{init} = $typeparams->[1],
$self->{xorout} = $typeparams->[2],
$self->{refout} = $typeparams->[3],
$self->{poly} = $typeparams->[4],
$self->{refin} = $typeparams->[5],
}
$self->{_tab} = _tabinit($self->{width}, $self->{poly}, $self->{refin});
$self->{_data} = undef;
$self
}
#########################################
# Private output converter functions:
sub _encode_hex { sprintf "%x", $_[0] }
sub _encode_base64 {
my $res;
while ($_[0] =~ /(.{1,45})/gs) {
$res .= substr pack('u', $1), 1;
chop $res;
}
$res =~ tr|` -_|AA-Za-z0-9+/|;#`
chop $res; chop $res;
$res
}
#########################################
# OOP interface:
sub add {
my $self = shift;
$self->{_data} .= join '', @_ if @_;
$self
}
sub addfile {
my ($self,$fh) = @_;
if (!ref($fh) && ref(\$fh) ne "GLOB") {
require Symbol;
$fh = Symbol::qualify($fh, scalar caller);
}
my $read = 0;
my $buffer = '';
my $crc;
my $oldinit = $self->{init};
while ($read = read $fh, $buffer, 32*1024) {
$self->add($buffer);
$crc = $self->digest;
$self->{cont}=1;
$self->{init}=$crc;
}
$self->{init} = $oldinit;
$self->{_crc} = $crc;
die __PACKAGE__, " read failed: $!" unless defined $read;
$self
}
sub add_bits {
}
sub digest {
my $self = shift;
my $crc;
if (!$self->{_crc}) {
my $init = $self->{init};
if ($self->{cont}) {
$init = ($self->{init} ^ $self->{xorout});
$init = _reflect($init, $self->{width}) if $self->{refin};
}
$crc =_crc($self->{_data},$self->{width},$init,$self->{xorout},
$self->{refin},$self->{refout},$self->{_tab});
} else {
$crc = $self->{_crc};
$self->{_crc} = undef;
}
$self->{_data} = undef;
$crc
}
sub hexdigest {
_encode_hex($_[0]->digest)
}
sub b64digest {
_encode_base64($_[0]->digest)
}
sub clone {
my $self = shift;
my $clone = {
type => $self->{type},
width => $self->{width},
init => $self->{init},
xorout => $self->{xorout},
poly => $self->{poly},
refin => $self->{refin},
refout => $self->{refout},
_data => $self->{_data}
};
bless $clone, ref $self || $self;
}
#########################################
# Procedural interface:
sub crc {
my ($message,$width,$init,$xorout,$refout,$poly,$refin) = @_;
_crc($message,$width,$init,$xorout,$refin,$refout,_tabinit($width,$poly,$refin));
}
# CRC8
# poly: 07, width: 8, init: 00, revin: no, revout: no, xorout: no
sub crc8 { crc($_[0],@{$_typedef{crc8}}) }
# CRC-CCITT standard
# poly: 1021, width: 16, init: ffff, refin: no, refout: no, xorout: no
sub crcccitt { crc($_[0],@{$_typedef{crcccitt}}) }
# CRC16
# poly: 8005, width: 16, init: 0000, revin: yes, revout: yes, xorout: no
sub crc16 { crc($_[0],@{$_typedef{crc16}}) }
# CRC32
# poly: 04C11DB7, width: 32, init: FFFFFFFF, revin: yes, revout: yes,
# xorout: FFFFFFFF
# equivalent to: cksum -o3
sub crc32 { crc($_[0],@{$_typedef{crc32}}) }
# CRC64
# special XS implementation (_crc64)
sub crc64 { _crc64($_[0]) }
sub crc_hex { _encode_hex &crc }
sub crc_base64 { _encode_base64 &crc }
sub crc8_hex { _encode_hex &crc8 }
sub crc8_base64 { _encode_base64 &crc8 }
sub crcccitt_hex { _encode_hex &crcccitt }
sub crcccitt_base64 { _encode_base64 &crcccitt }
sub crc16_hex { _encode_hex &crc16 }
sub crc16_base64 { _encode_base64 &crc16 }
sub crc32_hex { _encode_hex &crc32 }
sub crc32_base64 { _encode_base64 &crc32 }
sub crc64_hex { _encode_hex &crc64 }
sub crc64_base64 { _encode_base64 &crc64 }
1;
__END__
=head1 NAME
Digest::CRC - Generic CRC functions
=head1 SYNOPSIS
# Functional style
use Digest::CRC qw(crc64 crc32 crc16 crcccitt crc crc8);
$crc = crc64("123456789");
$crc = crc32("123456789");
$crc = crc16("123456789");
$crc = crcccitt("123456789");
$crc = crc8("123456789");
$crc = crc($input,$width,$init,$xorout,$refout,$poly,$refin);
# OO style
use Digest::CRC;
$ctx = Digest::CRC->new(type=>"crc16");
$ctx = Digest::CRC->new(width=>16, init=>0x2345, xorout=>0x0000,
poly=>0x8005, refin=>1, refout=>1, cont=>1);
$ctx->add($data);
$ctx->addfile(*FILE);
$digest = $ctx->digest;
$digest = $ctx->hexdigest;
$digest = $ctx->b64digest;
=head1 DESCRIPTION
The B<Digest::CRC> module calculates CRC sums of all sorts.
It contains wrapper functions with the correct parameters for CRC-CCITT,
CRC-16, CRC-32 and CRC-64.
=head1 AUTHOR
Oliver Maul, [email protected]
=head1 COPYRIGHT
CRC algorithm code taken from "A PAINLESS GUIDE TO CRC ERROR DETECTION
ALGORITHMS".
The author of this package disclaims all copyrights and
releases it into the public domain.
=cut
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment