Algorithm-LUHN_XS
view release on metacpan or search on metacpan
#define MAX_ERROR_LEN 200
/* you may be asking...why? */
/* well, windows and older versions of MacOS don't have strndup, so... */
unsigned char * _al_substr(unsigned const char* src, const int offset, const int len) {
unsigned char * sub = (unsigned char*)malloc(len+1);
memcpy(sub, src + offset, len);
sub[len] = 0;
return sub;
}
/* not thread safe, don't use this module with perl threads */
int _al_init_vc(SV* hash_ref) {
HV* hash;
HE* hash_entry;
int num_keys, i;
SV* sv_key;
SV* sv_val;
for (i=0;i<256;++i) {
_al_vc[i]=-1;
}
hash = (HV*)SvRV(hash_ref);
num_keys = hv_iterinit(hash);
for (i = 0; i < num_keys; i++) {
hash_entry = hv_iternext(hash);
sv_key = hv_iterkeysv(hash_entry);
sv_val = hv_iterval(hash, hash_entry);
_al_vc[(SvPV(sv_key,PL_na))[0]]=atoi(SvPV(sv_val,PL_na));
}
return 1;
}
int check_digit_rff(unsigned char *input) {
int len=strlen(input)-1;
if (len < 1) {
return -1;
}
static int deltas[]={ 0, 1, 2, 3, 4, -4, -3, -2, -1, 0 };
int checksum=0;
int flip=0;
int i,j;
for (i = len; i >=0; --i) {
j=input[i]-48;
/* only handle numeric input */
if (j > 9 || j < 0) {return -1;}
checksum += j;
if (flip ^= 1) {
checksum += deltas[j];
}
}
checksum *= 9;
return(checksum%10);
}
int check_digit_fast(unsigned char *input) {
int i, sum, ch, num, twoup, len;
len = strlen(input);
if (len < 1) {
char err[MAX_ERROR_LEN];
snprintf(err,MAX_ERROR_LEN,"check_digit_fast: No input string.");
SV *error;
error=get_sv("Algorithm::LUHN_XS::ERROR",GV_ADD);
sv_setpv(error,err);
return -1;
}
sum = 0;
twoup = 1;
for (i = len - 1; i >= 0; --i) {
num=_al_vc[input[i]];
if (num == -1) {
/* Don't change the error text, perl tests depend on the exact words */
unsigned char err[MAX_ERROR_LEN];
snprintf(err,MAX_ERROR_LEN,"Invalid character '%c', in check_digit calculation string [%s]",input[i],input);
SV *error;
error=get_sv("Algorithm::LUHN_XS::ERROR",GV_ADD);
sv_setpv(error,err);
return -1;
}
if (!(twoup = !twoup)) {
num *= 2;
}
while (num) {
sum += num % 10;
num=num/10;
}
}
return((10-(sum %10)) % 10);
}
SV* check_digit(unsigned char *input) {
int len=strlen(input);
if (len < 1) {
return &PL_sv_undef;
}
int rv=check_digit_fast(input);
if (rv == -1) {
return &PL_sv_undef;
} else {
return newSViv(rv);
}
}
SV* is_valid(unsigned char *input) {
int len=strlen(input);
if (len < 2) {
unsigned char err[MAX_ERROR_LEN];
snprintf(err,MAX_ERROR_LEN,
"is_valid: you must supply input of at least 2 characters");
SV *error;
error=get_sv("Algorithm::LUHN_XS::ERROR",GV_ADD);
sv_setpv(error,err);
SV* rv=newSVpv(NULL,1);
return rv;
}
unsigned char *leftmost=_al_substr(input,0,len-1);
unsigned char cd=input[len-1];
unsigned char c=check_digit_fast(leftmost)+'0';
free(leftmost);
if (c < 48) {
SV* rv=newSVpv(NULL,1);
return rv;
} else {
if (cd == c) {
return(newSViv(1));
} else {
unsigned char err[MAX_ERROR_LEN];
snprintf(err,MAX_ERROR_LEN,
"Check digit incorrect. Expected %c",c);
SV *error;
error=get_sv("Algorithm::LUHN_XS::ERROR",GV_ADD);
sv_setpv(error,err);
SV* rv=newSVpv(NULL,1);
return rv;
}
}
}
int is_valid_fast(unsigned char *input) {
int len=strlen(input);
if (len < 2) {
return 0;
}
unsigned char *leftmost=_al_substr(input,0,len-1);
unsigned char cd=input[len-1];
unsigned char c=check_digit_fast(leftmost)+'0';
free(leftmost);
if (c < 48) {
return 0;
} else {
if (cd == c) {
return 1;
} else {
unsigned char err[MAX_ERROR_LEN];
snprintf(err,MAX_ERROR_LEN,
"Check digit incorrect. Expected %c",c);
SV *error;
error=get_sv("Algorithm::LUHN_XS::ERROR",GV_ADD);
sv_setpv(error,err);
return 0;
}
}
}
int is_valid_rff(unsigned char *input) {
unsigned char csum;
int len=strlen(input);
if (len < 2) {
return 0;
}
unsigned char cd=input[len-1];
unsigned char *leftmost=_al_substr(input,0,len-1);
int d=check_digit_rff(leftmost);
csum=d+'0';
free(leftmost);
if (csum < 48) {
return 0;
} else {
if (cd == csum) {
return 1;
} else {
return 0;
}
}
}
MODULE = Algorithm::LUHN_XS PACKAGE = Algorithm::LUHN_XS
PROTOTYPES: DISABLE
int
_al_init_vc (hash_ref)
SV * hash_ref
int
check_digit_rff (input)
unsigned char * input
int
check_digit_fast (input)
unsigned char * input
SV*
check_digit(input)
unsigned char * input
SV*
is_valid(input)
unsigned char * input
int
is_valid_fast(input)
unsigned char * input
int
is_valid_rff(input)
unsigned char * input
( run in 0.648 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )