]> git.sur5r.net Git - openldap/commitdiff
Add lutil_str2bin() for arbitrary length decimal-to-binary conversion
authorHoward Chu <hyc@openldap.org>
Mon, 1 Oct 2007 06:50:19 +0000 (06:50 +0000)
committerHoward Chu <hyc@openldap.org>
Mon, 1 Oct 2007 06:50:19 +0000 (06:50 +0000)
include/lutil.h
libraries/liblutil/utils.c

index 9e40663201d3133e00fff5c2e5e9c24d4883828d..2400624159d060fb1d1e5d55eb430e884e1d3496 100644 (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 );
index 2cd105ed92cf6dd4ddbb4f1c6ea1a7713b24a0dc..1c6fd0e294970a111f19ee1b8a322623c58d5369 100644 (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 */