On one of our e-commerce web sites, we needed a unique transaction ID to pass to a third party reporting tool on the checkout pages. We already had a GUID on the page for internal use. And you know how much we love GUIDs!
22da5537-de54-459d-9b33-f40f2101143b
A GUID is 128 bits, or 16 bytes. And the third party can accept 20 bytes.
This seems workable until you realize that those 20 bytes have to be represented as a plain text string to be transmitted via HTTP in a form post or querystring.
So the question is, how do we represent a 128-bit integer in a plain text string that fits in 20 characters? In other words, we need to equip our ASCII Armor.
There's a Guid.ToByteArray() method which returns an array of 16 bytes (0-255). So we could just use ASCII values 0-255 to represent each byte, right? But wait a minute. ASCII 13 is carriage return! And good luck sending ASCII 0 (aka null) to anyone. Hmm.
We're forced to use only printable ASCII characters. Which means we'll have to use more bytes to represent the same data; it's unavoidable. Let's experiment with a few forms of ASCII armor and see how close we can get.
A Hex encoded GUID..
Dim g As Guid = Guid.NewGuid Dim sb As New Text.StringBuilder For Each b As Byte In g.ToByteArray sb.Append(String.Format("{0:X2}", b)) Next Console.WriteLine(sb.ToString)
.. uses ASCII values 0-9, A-F and results in a 32 byte string:
EBB7EF914C29A6459A34EDCB61EB8C8F
A UUEncoded GUID..
Dim u As New UUEncode Dim g As Guid = Guid.NewGuid Dim s As String s = u.Encode(g.ToByteArray) Console.WriteLine(s)
.. uses ASCII values 32-95 (decimal) and results in a 25 byte string:
0@-_;,9X-@D2BTV!0V$/TP``
A Base64 encoded GUID..
Dim g As Guid = Guid.NewGuid Dim s As String s = Convert.ToBase64String(g.ToByteArray) Console.WriteLine(s)
.. uses ASCII values a-z, A-Z, 0-9 and results in a 22 byte* string:
7v26IM9P2kmVepd7ZxuXyQ==
An ASCII85 encoded GUID...
Dim a As New Ascii85 Dim g As Guid = Guid.NewGuid Dim s As String s = a.Encode(g.ToByteArray) Console.WriteLine(s)
.. uses ASCII values 33-118 (decimal) and results in a 20 byte string:
[Rb*hlkkXVW+q4s(YSF0
So it is possible to fit a complete GUID in 20 printable ASCII characters using the latest and greatest ASCII Armor. But just barely!
In the process of writing this entry, I couldn't find any C# or VB.NET implementions of ASCII85, so I wrote one. I'll have source code up for that shortly.
* The trailing "==" in Base64 is an end of line marker and should not count towards the character total.