This is my own take for exotic base encodings like Base32 and Base58. I started to write it in 2013 as coding practice and kept it as a small pet project. I suggest anyone who wants to brush up their coding skills to give those encoding problems a shot. They turned out to be more challenging than I expected. To grasp the algorithms I had to get a pen and paper to see how the math worked.
- Base32: RFC 4648, Crockford and Extended Hex (BASE32-HEX) alphabets with Crockford character substitution (or any other custom alphabets you might want to use)
- Base58: Bitcoin, Ripple and Flickr alphabets (and any custom alphabet you might have)
- Base16: An experimental hexadecimal encoder/decoder just to see how far I can take
the optimizations compared to .NET's implementations. It's quite fast now. It can also be used as a replacement for
SoapHexBinary.Parsemethod since it's missing from .NET Core. - Thread-safe
- Simple to use
To install it from NuGet:
Install-Package SimpleBase
Encode a byte array:
using SimpleBase;
byte[] myBuffer;
string result = Base32.Crockford.Encode(myBuffer, padding: true);
// you can also use "ExtendedHex" or "Rfc4648" as encoder flavorsDecode a Base32-encoded string:
using SimpleBase;
string myText;
byte[] result = Base32.Crockford.Decode(myText);
// you can also use "ExtendedHex" or "Rfc4648" as decoder flavorsEncode a byte array:
using SimpleBase;
byte[] myBuffer;
string result = Base58.Bitcoin.Encode(myBuffer);
// you can also use "Ripple" or "Flickr" as encoder flavorsDecode a Base58-encoded string:
using SimpleBase;
string myText;
byte[] result = Base58.Bitcoin.Decode(myText);
// you can also use "Ripple" or "Flickr" as decoder flavorsEncode a byte array to hex string:
using SimpleBase;
string result = Base16.EncodeUpper(myBuffer); // encode to uppercase
// or
string result = Base16.EncodeLower(myBuffer); // encode to lowercaseTo decode a valid hex string:
using SimpleBase;
byte[] result = Base16.Decode(text); // decodes both upper and lowercaseSmall buffer sizes are used (64 characters). They are closer to real life applications. Base58 performs really bad in decoding of larger buffer sizes, due to polynomial complexity of numeric base conversions.
CPU: Intel Core i7-7700 @ 3.60Ghz 1,000,000 iterations on 64 byte buffer (encode) / 64 character string (decode)
| Implementation | Growth | Encode | Decode |
|---|---|---|---|
| .NET Framework Base64 | 1.33x | 0.14 | 0.19 |
| SimpleBase Base16 | 2x | 0.14 (about the same) | 0.13 (1.5x faster! YAY!) |
| SimpleBase Base32 Crockford | 1.6x | 0.27 (2x slower) | 0.15 (1.2x faster! YAY!) |
| SimpleBase Base58 | 1.38x | 8.90 (65.4x slower) | 5.52 (29x slower) |
I'm sure there are areas for improvement. I didn't want to go further in optimizations which would hurt readability and extensibility. I might experiment on them in the future.
Test suite for Base32 isn't complete, I took most of it from RFC4648. Base58 really lacks a good spec or test vectors needed. I had to resort to using online converters to generate preliminary test vectors.
It's interesting that I wasn't able to reach .NET Base64's performance with Base16 with a straightforward managed code despite that it's much simpler. I was only able to match it after I converted Base16 to unsafe code with good independent interleaving so CPU pipeline optimizations could take place. Still not satisfied though. Is .NET's Base64 implementation native? Perhaps.
Chatting about this pet project with my friends @detaybey, @vhallac, @alkimake and @Utopians at one of our friend's birthday encouraged me to finish this. Thanks guys. Special thanks to my wife for unlimited tea and love.