Blame lib/Digest/CRC.pm

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