Skip to content

Instantly share code, notes, and snippets.

@goelze
Last active July 14, 2017 20:42
Show Gist options
  • Save goelze/b2c3e58631abfbabaa1d5f2480b96376 to your computer and use it in GitHub Desktop.
Save goelze/b2c3e58631abfbabaa1d5f2480b96376 to your computer and use it in GitHub Desktop.
A struct containing a string in z-base-32 encoding.
/*
* ZBase32String.cs by Garrett Oelze <garrett@oelze.com>
*
* This work is licensed under a Creative Commons Attribution 4.0 International License.
* http://creativecommons.org/licenses/by/4.0/
*
* z-base-32 encoding is derived from the specification at http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace Oelze.Garrett
{
public struct ZBase32String
{
public ZBase32String(byte[] bytes)
{
_bytes = bytes;
_string = ConvertFromBytes(bytes);
}
public ZBase32String(string text, Encoding encoding) : this(encoding.GetBytes(text)) { }
public ZBase32String(string zBase32)
{
_string = zBase32.ToLower();
_bytes = ConvertToBytes(zBase32);
}
readonly byte[] _bytes;
readonly string _string;
const string ZBase32Chars = "ybndrfg8ejkmcpqxot1uwisza345h769";
static string ConvertFromBytes(byte[] bytes)
{
if (bytes == null)
return null;
if (bytes.Length == 0)
return string.Empty;
var sb = new StringBuilder((int)Math.Ceiling(bytes.Length * 8.0 / 5));
for (int i = 0; i < bytes.Length; i += 5)
{
int bytesLeft = Math.Min(5, bytes.Length - i);
ulong buffer = 0;
for (int j = 0; j < bytesLeft; j++)
{
buffer = (buffer << 8) | bytes[i + j];
}
int bitsLeft = bytesLeft * 8;
for (; bitsLeft > 0; bitsLeft -= 5)
{
int zBaseValue;
if (bitsLeft >= 5)
zBaseValue = (int)(buffer >> (bitsLeft - 5)) & 0x1f;
else
zBaseValue = (int)(buffer & (ulong)(0x1f >> (5 - bitsLeft))) << (5 - bitsLeft);
sb.Append(ZBase32Chars[zBaseValue]);
}
}
return sb.ToString();
}
static byte[] ConvertToBytes(string s)
{
if (s == null)
return null;
if (s == string.Empty)
return new byte[0];
var result = new List<byte>((int)Math.Ceiling(s.Length * 5.0 / 8));
for (int i = 0; i < s.Length; i += 8)
{
int subLength = Math.Min(8, s.Length - i);
ulong buffer = 0;
for (int j = 0; j < subLength; j++)
{
int index = ZBase32Chars.IndexOf(s[i + j]);
if (index < 0)
throw new ArgumentException("String is not a valid z-base-32 string.");
buffer = (buffer << 5) | (byte)index;
}
int bitsLeft = subLength * 5;
for (; bitsLeft >= 8; bitsLeft -= 8)
{
result.Add((byte)((buffer >> (bitsLeft - 8)) & 0xff));
}
}
return result.ToArray();
}
/// <summary>
/// Returns the value encoded in zbase32.
/// </summary>
public override string ToString()
{
return _string;
}
/// <summary>
/// Returns the value with the specified encoding.
/// </summary>
public string ToString(Encoding enc)
{
return enc.GetString(_bytes);
}
public byte[] ToByteArray()
{
return _bytes;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment