From: Richard Kettlewell Date: Sun, 8 Mar 2009 12:03:58 +0000 (+0000) Subject: basen() now takes uint32_t rather than unsigned long, since that's X-Git-Tag: 5.0~180 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/commitdiff_plain/1c814807201c819324a87b186aa9e59279d4fd5d basen() now takes uint32_t rather than unsigned long, since that's what it actually assumes. New function nesab() is the reverse of basen(). --- diff --git a/lib/basen.c b/lib/basen.c index 87d2795..7c1f9ec 100644 --- a/lib/basen.c +++ b/lib/basen.c @@ -31,7 +31,7 @@ * @param nwords Length of bignum * @return !v */ -static int zero(const unsigned long *v, int nwords) { +static int zero(const uint32_t *v, int nwords) { int n; for(n = 0; n < nwords && !v[n]; ++n) @@ -47,7 +47,7 @@ static int zero(const unsigned long *v, int nwords) { * * The quotient is stored in @p v. */ -static unsigned divide(unsigned long *v, int nwords, unsigned long m) { +static unsigned divide(uint32_t *v, int nwords, unsigned long m) { unsigned long r = 0, a, b; int n; @@ -66,6 +66,31 @@ static unsigned divide(unsigned long *v, int nwords, unsigned long m) { return r; } +/** @brief Multiple v by m and add a + * @param v Pointer to bigendian bignum + * @param nwords Length of bignum + * @param m Value to multiply by + * @param a Value to add + * @return 0 on success, non-0 on overflow + * + * Does v = m * v + a. + */ +static int mla(uint32_t *v, int nwords, uint32_t m, uint32_t a) { + int n = nwords - 1; + uint32_t carry = a; + + while(n >= 0) { + const uint64_t p = (uint64_t)v[n] * m + carry; + carry = (uint32_t)(p >> 32); + v[n] = (uint32_t)p; + --n; + } + /* If there is still a carry then we overflowed */ + return !!carry; +} + +static const char basen_chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + /** @brief Convert v to a chosen base * @param v Pointer to bigendian bignum (modified!) * @param nwords Length of bignum @@ -75,26 +100,53 @@ static unsigned divide(unsigned long *v, int nwords, unsigned long m) { * @return 0 on success, -1 if the buffer is too small * * Converts @p v to a string in the given base using decimal digits, lower case - * letter sand upper case letters as digits. + * letters and upper case letters as digits. + * + * The inverse of nesab(). */ -int basen(unsigned long *v, +int basen(uint32_t *v, int nwords, char buffer[], size_t bufsize, unsigned base) { size_t i = bufsize; - static const char chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; do { if(i <= 1) return -1; /* overflow */ - buffer[--i] = chars[divide(v, nwords, base)]; + buffer[--i] = basen_chars[divide(v, nwords, base)]; } while(!zero(v, nwords)); memmove(buffer, buffer + i, bufsize - i); buffer[bufsize - i] = 0; return 0; } +/** @brief Convert a string back to a large integer in an arbitrary base + * @param v Where to store result as a bigendian bignum + * @param nwords Space available in @p v + * @param s Input string + * @param base Number base (2..62) + * @return 0 on success, non-0 on overflow or invalid input + * + * The inverse of basen(). If the number is much smaller than the buffer then + * the first words will be 0. + */ +int nesab(uint32_t *v, + int nwords, + const char *s, + unsigned base) { + /* Initialize to 0 */ + memset(v, 0, nwords * sizeof *v); + while(*s) { + const char *dp = strchr(basen_chars, *s++); + if(!dp) + return -1; + if(mla(v, nwords, base, dp - basen_chars)) + return -1; + } + return 0; +} + /* Local Variables: c-basic-offset:2 diff --git a/lib/basen.h b/lib/basen.h index 717f2ea..c1a60f0 100644 --- a/lib/basen.h +++ b/lib/basen.h @@ -19,7 +19,7 @@ #ifndef BASEN_H #define BASEN_H -int basen(unsigned long *v, +int basen(uint32_t *v, int nwords, char buffer[], size_t bufsize, @@ -29,6 +29,11 @@ int basen(unsigned long *v, * Returns 0 on success or -1 on overflow. */ +int nesab(uint32_t *v, + int nwords, + const char *s, + unsigned base); + #endif /* BASEN_H */ /* diff --git a/lib/random.c b/lib/random.c index 3f8f714..d6be3d9 100644 --- a/lib/random.c +++ b/lib/random.c @@ -75,7 +75,7 @@ void random_get(void *ptr, size_t bytes) { /** @brief Return a random ID string */ char *random_id(void) { - unsigned long words[2]; + uint32_t words[2]; char id[128]; random_get(words, sizeof words); diff --git a/libtests/t-basen.c b/libtests/t-basen.c index 715490c..3cbc044 100644 --- a/libtests/t-basen.c +++ b/libtests/t-basen.c @@ -18,12 +18,22 @@ #include "test.h" static void test_basen(void) { - unsigned long v[64]; + uint32_t v[64]; char buffer[1024]; v[0] = 999; insist(basen(v, 1, buffer, sizeof buffer, 10) == 0); check_string(buffer, "999"); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 1, buffer, 10) == 0); + check_integer(v[0], 999); + check_integer(v[1], 0xFFFFFFFF); + insist(nesab(v, 4, buffer, 10) == 0); + check_integer(v[0], 0); + check_integer(v[1], 0); + check_integer(v[2], 0); + check_integer(v[3], 999); + check_integer(v[4], 0xFFFFFFFF); v[0] = 1+2*7+3*7*7+4*7*7*7; insist(basen(v, 1, buffer, sizeof buffer, 7) == 0); @@ -35,6 +45,24 @@ static void test_basen(void) { v[3] = 0x0C0D0E0F; insist(basen(v, 4, buffer, sizeof buffer, 256) == 0); check_string(buffer, "123456789abcdef"); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 4, buffer, 256) == 0); + check_integer(v[0], 0x00010203); + check_integer(v[1], 0x04050607); + check_integer(v[2], 0x08090A0B); + check_integer(v[3], 0x0C0D0E0F); + check_integer(v[4], 0xFFFFFFFF); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 8, buffer, 256) == 0); + check_integer(v[0], 0); + check_integer(v[1], 0); + check_integer(v[2], 0); + check_integer(v[3], 0); + check_integer(v[4], 0x00010203); + check_integer(v[5], 0x04050607); + check_integer(v[6], 0x08090A0B); + check_integer(v[7], 0x0C0D0E0F); + check_integer(v[8], 0xFFFFFFFF); v[0] = 0x00010203; v[1] = 0x04050607; @@ -42,6 +70,24 @@ static void test_basen(void) { v[3] = 0x0C0D0E0F; insist(basen(v, 4, buffer, sizeof buffer, 16) == 0); check_string(buffer, "102030405060708090a0b0c0d0e0f"); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 4, buffer, 16) == 0); + check_integer(v[0], 0x00010203); + check_integer(v[1], 0x04050607); + check_integer(v[2], 0x08090A0B); + check_integer(v[3], 0x0C0D0E0F); + check_integer(v[4], 0xFFFFFFFF); + memset(v, 0xFF, sizeof v); + insist(nesab(v, 8, buffer, 16) == 0); + check_integer(v[0], 0); + check_integer(v[1], 0); + check_integer(v[2], 0); + check_integer(v[3], 0); + check_integer(v[4], 0x00010203); + check_integer(v[5], 0x04050607); + check_integer(v[6], 0x08090A0B); + check_integer(v[7], 0x0C0D0E0F); + check_integer(v[8], 0xFFFFFFFF); v[0] = 0x00010203; v[1] = 0x04050607;