/*
Perl Extension for CRC computations
*/
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
/*
* From Matt Dillon's Diablo package. Used with permission.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct crc_hash_t {
U32 h1;
U32 h2;
} crc_hash_t;
typedef struct crc_Hash {
struct crc_Hash *ha_Next;
crc_hash_t ha_Hv;
} crc_Hash;
int crc_HashLimit = 0;
/*
* Poly: 0x00600340.00F0D50A
*/
#define HINIT1 0xFAC432B1UL
#define HINIT2 0x0CD5E44AUL
#define POLY1 0x00600340UL
#define POLY2 0x00F0D50BUL
crc_hash_t CrcXor[256];
crc_hash_t Poly[64+1];
void
crc_init(void)
{
int i;
/*
* Polynomials to use for various crc sizes. Start with the 64 bit
* polynomial and shift it right to generate the polynomials for fewer
* bits. Note that the polynomial for N bits has no bit set above N-8.
* This allows us to do a simple table-driven CRC.
*/
Poly[64].h1 = POLY1;
Poly[64].h2 = POLY2;
for (i = 63; i >= 16; --i) {
Poly[i].h1 = Poly[i+1].h1 >> 1;
Poly[i].h2 = (Poly[i+1].h2 >> 1) | ((Poly[i+1].h1 & 1) << 31) | 1;
}
for (i = 0; i < 256; ++i) {
int j;
int v = i;
crc_hash_t hv = { 0, 0 };
for (j = 0; j < 8; ++j, (v <<= 1)) {
hv.h1 <<= 1;
if (hv.h2 & 0x80000000UL)
hv.h1 |= 1;
hv.h2 = (hv.h2 << 1);
if (v & 0x80) {
hv.h1 ^= Poly[crc_HashLimit].h1;
hv.h2 ^= Poly[crc_HashLimit].h2;
}
}
CrcXor[i] = hv;
}
}
/*
* testhash() - do the CRC. The complexity is simply due to the programmable
* nature of the number of bits. We extract the top 8 bits to
* use as a table lookup to obtain the polynomial XOR 8 bits at
* a time rather then 1 bit at a time.
*/
crc_hash_t
crc_calculate(char *p, int len)
{
crc_hash_t hv = { HINIT1, HINIT2 };
char *e = p + len;
if (crc_HashLimit <= 32) {
int s = crc_HashLimit - 8;
U32 m = (U32)-1 >> (32 - crc_HashLimit);
hv.h1 = 0;
hv.h2 &= m;
while (p < e) {
int i = (hv.h2 >> s) & 255;
/* printf("i = %d %08lx\n", i, CrcXor[i].h2); */
hv.h2 = ((hv.h2 << 8) & m) ^ *p ^ CrcXor[i].h2;
++p;
}
} else if (crc_HashLimit < 32+8) {
int s2 = 32 + 8 - crc_HashLimit; /* bits in byte from h2 */
U32 m = (U32)-1 >> (64 - crc_HashLimit);
hv.h1 &= m;
while (p < e) {
int i = ((hv.h1 << s2) | (hv.h2 >> (32 - s2))) & 255;
hv.h1 = (((hv.h1 << 8) ^ (int)(hv.h2 >> 24)) & m) ^ CrcXor[i].h1;
hv.h2 = (hv.h2 << 8) ^ *p ^ CrcXor[i].h2;
++p;
}
} else {
int s = crc_HashLimit - 40;
U32 m = (U32)-1 >> (64 - crc_HashLimit);
hv.h1 &= m;
while (p < e) {
int i = (hv.h1 >> s) & 255;
hv.h1 = ((hv.h1 << 8) & m) ^ (int)(hv.h2 >> 24) ^ CrcXor[i].h1;
hv.h2 = (hv.h2 << 8) ^ *p ^ CrcXor[i].h2;
++p;
}
}
/* printf("%08lx.%08lx\n", (long)hv.h1, (long)hv.h2); */
return(hv);
}
MODULE = String::CRC PACKAGE = String::CRC
VERSIONCHECK: DISABLE
void
crc(data,bits=32)
PREINIT:
int data_len;
INPUT:
char *data = (char *)SvPV(ST(0),data_len);
int bits;
PPCODE:
{
crc_hash_t h;
U32 *rv;
SV *sv;
if (bits < 16 || bits > 64) {
croak("String::CRC bits must be >= 16 and <= 64");
}
if (bits != crc_HashLimit) {
crc_HashLimit = bits;
crc_init();
}
h = crc_calculate(data, data_len);
if (bits > 32 && GIMME == G_ARRAY) {
EXTEND(sp, 2);
sv = newSV(0);
sv_setuv(sv, (UV)h.h1);
PUSHs(sv_2mortal(sv));
sv = newSV(0);
sv_setuv(sv, (UV)h.h2);
PUSHs(sv_2mortal(sv));
} else if (bits > 32) {
/*
* problem. how to return 64 bits in 32?
* answer: as a string.
* U32 better be == 4 bytes!
*/
EXTEND(sp, 1);
PUSHs(sv_2mortal(newSVpv((char *)&h, 8)));
if (sizeof(U32) != 4) croak("U32 not four bytes!");
} else {
EXTEND(sp, 1);
sv = newSV(0);
sv_setuv(sv, (UV)h.h2);
PUSHs(sv_2mortal(sv));
/* PUSHs(sv_2mortal(newSViv(h.h2)));*/
}
}