Add lutil_str2bin() for arbitrary length decimal-to-binary conversion

This commit is contained in:
Howard Chu 2007-10-01 06:50:19 +00:00
parent 6827d4b33a
commit 4d58197880
2 changed files with 167 additions and 0 deletions

View File

@ -304,6 +304,9 @@ lutil_atoulx( unsigned long *v, const char *s, int x );
#define lutil_atol(v, s) lutil_atolx((v), (s), 10)
#define lutil_atoul(v, s) lutil_atoulx((v), (s), 10)
LDAP_LUTIL_F (int)
lutil_str2bin( struct berval *in, struct berval *out );
/* Parse and unparse time intervals */
LDAP_LUTIL_F (int)
lutil_parse_time( const char *in, unsigned long *tp );

View File

@ -21,6 +21,7 @@
#include <ac/ctype.h>
#include <ac/unistd.h>
#include <ac/time.h>
#include <ac/errno.h>
#ifdef HAVE_IO_H
#include <io.h>
#endif
@ -602,6 +603,169 @@ lutil_atoulx( unsigned long *v, const char *s, int x )
return 0;
}
/* Multiply an integer by 100000000 and add new */
typedef struct _decnum {
unsigned char *buf;
int bufsiz;
int beg;
int len;
} _decnum;
#define FACTOR1 (100000000&0xffff)
#define FACTOR2 (100000000>>16)
static void
scale( int new, _decnum *prev, unsigned char *tmp )
{
int i, j;
unsigned char *in = prev->buf+prev->beg;
unsigned int part;
unsigned char *out = tmp + prev->bufsiz - prev->len;
memset( tmp, 0, prev->bufsiz );
if ( prev->len ) {
for ( i = prev->len-1; i>=0; i-- ) {
part = in[i] * FACTOR1;
for ( j = i; part; j-- ) {
part += out[j];
out[j] = part & 0xff;
part >>= 8;
}
part = in[i] * FACTOR2;
for ( j = i-2; part; j-- ) {
part += out[j];
out[j] = part & 0xff;
part >>= 8;
}
}
j++;
prev->beg += j;
prev->len -= j;
}
out = tmp + prev->bufsiz - 1;
for ( i = 0; new ; i-- ) {
new += out[i];
out[i] = new & 0xff;
new >>= 8;
if (!new ) {
if ( !prev->len ) {
prev->beg += i;
prev->len = -i;
prev->len++;
}
break;
}
}
AC_MEMCPY( prev->buf+prev->beg, tmp+prev->beg, prev->len );
}
/* Convert unlimited length decimal or hex string to binary.
* Output buffer must be provided, bv_len must indicate buffer size
* Hex input can be "0x1234" or "'1234'H"
*/
int
lutil_str2bin( struct berval *in, struct berval *out )
{
char *pin, *pout, ctmp;
char *end;
long l;
int i, chunk, len, rc = 0, hex = 0;
if ( !out || !out->bv_val || out->bv_len < in->bv_len )
return -1;
pout = out->bv_val;
/* Leading "0x" for hex input */
if ( in->bv_len > 2 && in->bv_val[0] == '0' &&
( in->bv_val[1] == 'x' || in->bv_val[1] == 'X' )) {
len = in->bv_len - 2;
pin = in->bv_val + 2;
hex = 1;
} else if ( in->bv_len > 3 && in->bv_val[0] == '\'' &&
( in->bv_val[in->bv_len-2] == '\'' &&
in->bv_val[in->bv_len-1] == 'H' )) {
len = in->bv_len - 3;
pin = in->bv_val + 1;
hex = 1;
}
if ( hex ) {
#define HEXMAX (2 * sizeof(long))
/* Convert a longword at a time, but handle leading
* odd bytes first
*/
chunk = len & (HEXMAX-1);
if ( !chunk )
chunk = HEXMAX;
while ( len ) {
ctmp = pin[chunk];
pin[chunk] = '\0';
errno = 0;
l = strtol( pin, &end, 16 );
pin[chunk] = ctmp;
if ( errno )
return -1;
chunk++;
chunk >>= 1;
for ( i = chunk; i>=0; i-- ) {
pout[i] = l & 0xff;
l >>= 8;
}
pin += chunk;
pout += sizeof(long);
len -= chunk;
chunk = HEXMAX;
}
out->bv_len = pout + len - out->bv_val;
} else {
/* Decimal */
char tmpbuf[64], *tmp;
_decnum num;
len = in->bv_len;
pin = in->bv_val;
num.buf = out->bv_val;
num.bufsiz = out->bv_len;
num.beg = num.bufsiz-1;
num.len = 0;
#define DECMAX 8 /* 8 digits at a time */
if ( len > sizeof(tmpbuf)) {
tmp = ber_memalloc( len );
} else {
tmp = tmpbuf;
}
chunk = len & (DECMAX-1);
if ( !chunk )
chunk = DECMAX;
while ( len ) {
ctmp = pin[chunk];
pin[chunk] = '\0';
errno = 0;
l = strtol( pin, &end, 10 );
pin[chunk] = ctmp;
if ( errno ) {
rc = -1;
goto decfail;
}
scale( l, &num, tmp );
pin += chunk;
len -= chunk;
chunk = HEXMAX;
}
if ( num.beg )
AC_MEMCPY( num.buf, num.buf+num.beg, num.len );
out->bv_len = num.len;
decfail:
if ( tmp != tmpbuf ) {
ber_memfree( tmp );
}
}
return rc;
}
static char time_unit[] = "dhms";
/* Used to parse and unparse time intervals, not timestamps */