tags:

views:

56

answers:

3

i'm trying to convert an md5 string (base 16) to a base 62 string in c++. every solution i've found so far for converting to base 62 only works if you can represent your number as a 64 bit integer or smaller. an md5 string is 128 bits and i'm not getting anywhere with this on my own.

should i just include a bigint library and be done with it?

A: 

GMP provides a convenient c++ binding for arbitrary precision integers

TokenMacGuy
+1  A: 

For simplicity, you can use my uint128_t c++ class (http://www.codef00.com/code/uint128.h). With it, a base converter would look pretty much as simple as this:

#include "uint128.h"
#include <iostream>
#include <algorithm>

int main() {
    char a[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    uint128_t x = U128_C(0x130eb6003540debd012d96ce69453aed);

    std::string r;
    r.reserve(22); // shouldn't result in more than 22 chars 
                   // 6-bits per 62-bit value means (128 / 6 == 21.3)

    while(x != 0) {
        r += a[(x % 62).to_integer()];
        x /= 62;
    }

    // when converting bases by division, the digits are reversed...fix that :-)
    std::reverse(r.begin(), r.end());
    std::cout << r << std::endl;
}

This prints:

J7JWEJ0YbMGqaJFCGkUxZ
Evan Teran
i love your uint128 class. it would suit my short term goals nicely. the only thing i regret is the dependency on boost. won't it be nice when boost is part of the standard library?
rev
Sorry you can't make use of boost. The boost stuff is mostly for convenience, I use it to "auto-magically" create the various operators. Either way, I hope you can use it as an example.
Evan Teran
+2  A: 

Let's see. 128/log2(62)=21.497. That means you'd need 22 "digits" for a base-62 representation.

If you're just interested in a string representation that's not longer than 22 characters and doesn't use more than 62 different characters, you don't need a real base-62 representation. You can break up 128 bits into smaller pieces and code the pieces separately. This way you won't need any 128 bit arithmetics. You could split the 128 bits to 2x64 bits and encode each 64 bit chunk with a string of length 11. Doing so is even possible with just 57 different characters. So, you could eliminate 5 of the 62 characters to avoid any "visual ambiguities". For example, remove l,1,B,8. That leaves 58 different characters and 11*log2(58)=64.438 which is just enough to encode 64 bits.

Getting the two 64 bit chunks is not that difficult:

#include <climits>

#if CHAR_BIT != 8
#error "platform not supported, CHAR_BIT==8 expected"
#endif

// 'long long' is not yet part of C++
// But it's usually a supported extension
typedef unsigned long long uint64;

uint64 bits2uint64_bigendian(unsigned char const buff[]) {
   return (static_cast<uint64>(buff[0]) << 56)
        | (static_cast<uint64>(buff[1]) << 48)
        | (static_cast<uint64>(buff[2]) << 40)
        | (static_cast<uint64>(buff[3]) << 32)
        | (static_cast<uint64>(buff[4]) << 24)
        | (static_cast<uint64>(buff[5]) << 16)
        | (static_cast<uint64>(buff[6]) <<  8)
        |  static_cast<uint64>(buff[7]);
}

int main() {
   unsigned char md5sum[16] = {...};
   uint64 hi = bits2uint64_bigendian(md5sum);
   uint64 lo = bits2uint64_bigendian(md5sum+8);
}
sellibitze
thank you for making me think outside the box. you're right this would be the practical thing to do if all i wanted was a shortened string representation of the hash. i may use your solution in the short term, but i'm really fishing for a more generalized solution for representing any byte array in base N.
rev