I needed to generate a random unique string of no more than 30 characters length for integrating with an external API. Usually for something like this we’d use a UUID/GUID, which are 36 characters long (or 35 in ColdFusion). A colleague came up with the idea of taking that and converting it to Base 32. He was using C# and this class, but then this part of the work got transferred to the ColdFusion team, so here’s the approach I took.
A 128-bit number = 2^128, which is a very big number (340,282,366,920,938,463,463,374,607,431,768,211,456 possible values). ColdFusion can struggle with integers this size in certain functions, e.g. InputBaseN (and its counterpart FormatBaseN), otherwise that would be ideal for what I’m trying to do here
There’s already a function in CFLib which could do this, but it’s compatible with ColdFusion 5, i.e. it’s not got access to any of the underlying Java classes which CFMX does. My colleague Adam also wrote a different function which can convert between any two bases, but it’s more fully-featured than I require as well.
UUID’s also contain hyphens. Typically a UUID format is in a
8-4-4-4-12 pattern, i.e. the string is 36 characters long. ColdFusion does it differently;
8-4-4-16, only 35 characters long. Apparently this difference was a mistake, which they didn’t want to amend later due to backwards-compatibility. However for the purposes of this code, I don’t care either way, I just want to get rid of the hyphen separators.
So, step 1 is remove the hyphens from a UUID:
<cfset strUUID = createUUID()> <cfset strUUID = replace(strUUID, "-", "", "ALL")>
Now I want to turn this hexadecimal string into a BigInteger, which is the Java class which can deal with numbers this size (Integer only goes up to 2^31-1, Long goes up to 2^63-1, so BigInteger it has to be).
<cfset bigInt = createObject("java", "java.math.BigInteger").init(strUUID, 16)>
So we use the BigInteger constructor, specifying our string and the Base 16 radix as the second parameter.
Step 3, we want to then convert that BigInteger to Base 36. This is the characters A-Z0-9, and is ‘the most compact case-insensitive alphanumeric numeral system using ASCII characters’. Which I took to be a good thing; the advantage that Base 32 (that my colleague had chosen) would have given is it eliminates certain characters which might be easily-confused if you were reading the values after encoding in that base. So it’s ideal for generating codes used for humans, but that wasn’t going to be an issue here, so I chose Base 36 instead.
<cfset strBase36 = bigInt.toString(36)>
Putting it all together:
<cfset strUUID = createUUID()> <cfset strUUID = replace(strUUID, "-", "", "ALL")> <cfset bigInt = createObject("java", "java.math.BigInteger").init(strUUID, 16)> <cfset strBase36 = bigInt.toString(36)> <cfoutput> strUUID: #strUUID#, #len(strUUID)#<br> strBase36: #strBase36#, #len(strBase36)# </cfoutput>
Gives me something like:
strUUID: AFA64721B8AC6F8CD055DE2527EB20F9, 32
strBase36: aecxzys9dnwydn6ejgkxwgb95, 25
PS: apologies to Base36 (the company) for borrowing their funky (and let’s be honest, better-executed) Base36 mosaic idea.