Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@kspeakman
Last active April 24, 2018 16:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kspeakman/ec704d972eec53e03b9684272d135b68 to your computer and use it in GitHub Desktop.
Save kspeakman/ec704d972eec53e03b9684272d135b68 to your computer and use it in GitHub Desktop.
Utf8Json Short GUID parser
// differences from Utf8Json GuidBits
// * parses Base64 encoded Guids
// * writes Guids without '-' character
[StructLayout(LayoutKind.Explicit, Pack = 1)]
public struct GuidBits
{
[FieldOffset(0)]
public readonly Guid Value;
[FieldOffset(0)]
public readonly byte Byte0;
[FieldOffset(1)]
public readonly byte Byte1;
[FieldOffset(2)]
public readonly byte Byte2;
[FieldOffset(3)]
public readonly byte Byte3;
[FieldOffset(4)]
public readonly byte Byte4;
[FieldOffset(5)]
public readonly byte Byte5;
[FieldOffset(6)]
public readonly byte Byte6;
[FieldOffset(7)]
public readonly byte Byte7;
[FieldOffset(8)]
public readonly byte Byte8;
[FieldOffset(9)]
public readonly byte Byte9;
[FieldOffset(10)]
public readonly byte Byte10;
[FieldOffset(11)]
public readonly byte Byte11;
[FieldOffset(12)]
public readonly byte Byte12;
[FieldOffset(13)]
public readonly byte Byte13;
[FieldOffset(14)]
public readonly byte Byte14;
[FieldOffset(15)]
public readonly byte Byte15;
// string.Join(", ", Enumerable.Range(0, 256).Select(x => (int)BitConverter.ToString(new byte[] { (byte)x }).ToLower()[0]))
static byte[] byteToHexStringHigh = new byte[256] { 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102 };
// string.Join(", ", Enumerable.Range(0, 256).Select(x => (int)BitConverter.ToString(new byte[] { (byte)x }).ToLower()[1]))
static byte[] byteToHexStringLow = new byte[256] { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 97, 98, 99, 100, 101, 102 };
public GuidBits(ref Guid value)
{
this = default(GuidBits);
this.Value = value;
}
// 4-pattern, lower/upper and '-' or no
public GuidBits(ref ArraySegment<byte> utf8string)
{
this = default(GuidBits);
var array = utf8string.Array;
var offset = utf8string.Offset;
// utf8 + base64, base64 is 6 bits per char
// I think Base64 does not account for endianness
if (utf8string.Count == 22)
{
this.Byte0 = (byte)(ParseUtf8Base64(array, offset + 0, Shift.Left2)
| ParseUtf8Base64(array, offset + 1, Shift.Right4));
this.Byte1 = (byte)(ParseUtf8Base64(array, offset + 1, Shift.Left4)
| ParseUtf8Base64(array, offset + 2, Shift.Right2));
this.Byte2 = (byte)(ParseUtf8Base64(array, offset + 2, Shift.Left6)
| ParseUtf8Base64(array, offset + 3, Shift.NoShift));
this.Byte3 = (byte)(ParseUtf8Base64(array, offset + 4, Shift.Left2)
| ParseUtf8Base64(array, offset + 5, Shift.Right4));
this.Byte4 = (byte)(ParseUtf8Base64(array, offset + 5, Shift.Left4)
| ParseUtf8Base64(array, offset + 6, Shift.Right2));
this.Byte5 = (byte)(ParseUtf8Base64(array, offset + 6, Shift.Left6)
| ParseUtf8Base64(array, offset + 7, Shift.NoShift));
this.Byte6 = (byte)(ParseUtf8Base64(array, offset + 8, Shift.Left2)
| ParseUtf8Base64(array, offset + 9, Shift.Right4));
this.Byte7 = (byte)(ParseUtf8Base64(array, offset + 9, Shift.Left4)
| ParseUtf8Base64(array, offset + 10, Shift.Right2));
this.Byte8 = (byte)(ParseUtf8Base64(array, offset + 10, Shift.Left6)
| ParseUtf8Base64(array, offset + 11, Shift.NoShift));
this.Byte9 = (byte)(ParseUtf8Base64(array, offset + 12, Shift.Left2)
| ParseUtf8Base64(array, offset + 13, Shift.Right4));
this.Byte10 = (byte)(ParseUtf8Base64(array, offset + 13, Shift.Left4)
| ParseUtf8Base64(array, offset + 14, Shift.Right2));
this.Byte11 = (byte)(ParseUtf8Base64(array, offset + 14, Shift.Left6)
| ParseUtf8Base64(array, offset + 15, Shift.NoShift));
this.Byte12 = (byte)(ParseUtf8Base64(array, offset + 16, Shift.Left2)
| ParseUtf8Base64(array, offset + 17, Shift.Right4));
this.Byte13 = (byte)(ParseUtf8Base64(array, offset + 17, Shift.Left4)
| ParseUtf8Base64(array, offset + 18, Shift.Right2));
this.Byte14 = (byte)(ParseUtf8Base64(array, offset + 18, Shift.Left6)
| ParseUtf8Base64(array, offset + 19, Shift.NoShift));
this.Byte15 = (byte)(ParseUtf8Base64(array, offset + 20, Shift.Left2)
| ParseUtf8Base64(array, offset + 21, Shift.Right4));
return;
}
else if (utf8string.Count == 32)
{
if (BitConverter.IsLittleEndian)
{
this.Byte0 = Parse(array, offset + 6);
this.Byte1 = Parse(array, offset + 4);
this.Byte2 = Parse(array, offset + 2);
this.Byte3 = Parse(array, offset + 0);
this.Byte4 = Parse(array, offset + 10);
this.Byte5 = Parse(array, offset + 8);
this.Byte6 = Parse(array, offset + 14);
this.Byte7 = Parse(array, offset + 12);
}
else
{
this.Byte0 = Parse(array, offset + 0);
this.Byte1 = Parse(array, offset + 2);
this.Byte2 = Parse(array, offset + 4);
this.Byte3 = Parse(array, offset + 6);
this.Byte4 = Parse(array, offset + 8);
this.Byte5 = Parse(array, offset + 10);
this.Byte6 = Parse(array, offset + 12);
this.Byte7 = Parse(array, offset + 14);
}
this.Byte8 = Parse(array, offset + 16);
this.Byte9 = Parse(array, offset + 18);
this.Byte10 = Parse(array, offset + 20);
this.Byte11 = Parse(array, offset + 22);
this.Byte12 = Parse(array, offset + 24);
this.Byte13 = Parse(array, offset + 26);
this.Byte14 = Parse(array, offset + 28);
this.Byte15 = Parse(array, offset + 30);
return;
}
else if (utf8string.Count == 36)
{
// '-' => 45
if (BitConverter.IsLittleEndian)
{
this.Byte0 = Parse(array, offset + 6);
this.Byte1 = Parse(array, offset + 4);
this.Byte2 = Parse(array, offset + 2);
this.Byte3 = Parse(array, offset + 0);
if (array[offset + 8] != '-') goto ERROR;
this.Byte4 = Parse(array, offset + 11);
this.Byte5 = Parse(array, offset + 9);
if (array[offset + 13] != '-') goto ERROR;
this.Byte6 = Parse(array, offset + 16);
this.Byte7 = Parse(array, offset + 14);
}
else
{
this.Byte0 = Parse(array, offset + 0);
this.Byte1 = Parse(array, offset + 2);
this.Byte2 = Parse(array, offset + 4);
this.Byte3 = Parse(array, offset + 6);
if (array[offset + 8] != '-') goto ERROR;
this.Byte4 = Parse(array, offset + 9);
this.Byte5 = Parse(array, offset + 11);
if (array[offset + 13] != '-') goto ERROR;
this.Byte6 = Parse(array, offset + 14);
this.Byte7 = Parse(array, offset + 16);
}
if (array[offset + 18] != '-') goto ERROR;
this.Byte8 = Parse(array, offset + 19);
this.Byte9 = Parse(array, offset + 21);
if (array[offset + 23] != '-') goto ERROR;
this.Byte10 = Parse(array, offset + 24);
this.Byte11 = Parse(array, offset + 26);
this.Byte12 = Parse(array, offset + 28);
this.Byte13 = Parse(array, offset + 30);
this.Byte14 = Parse(array, offset + 32);
this.Byte15 = Parse(array, offset + 34);
return;
}
ERROR:
throw new ArgumentException("Invalid Guid Pattern.");
}
#if NETSTANDARD
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
#endif
static byte Parse(byte[] bytes, int highOffset)
{
return unchecked((byte)(SwitchParse(bytes[highOffset]) * 16 + SwitchParse(bytes[highOffset + 1])));
}
public enum Shift
{ // given --00 0000
Left2, // << 2 0000 00--
Right4, // >> 4 ---- --00
Left4, // << 4 0000 ----
Right2, // >> 2 0000
Left6, // << 6 00-- ----
NoShift // no shift --00 0000
}
#if NETSTANDARD
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
#endif
static int ParseUtf8Base64(byte[] bytes, int highOffset, Shift shift)
{
var b1 = bytes[highOffset];
int ret;
// https://tools.ietf.org/html/rfc4648#section-4
switch (b1)
{
case 65: // A
case 66:
case 67:
case 68:
case 69:
case 70:
case 71:
case 72:
case 73:
case 74:
case 75:
case 76:
case 77:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 84:
case 85:
case 86:
case 87:
case 88:
case 89:
case 90: // Z
ret = b1 - 65; // 0 - 25 output
break;
case 97: // a
case 98:
case 99:
case 100:
case 101:
case 102:
case 103:
case 104:
case 105:
case 106:
case 107:
case 108:
case 109:
case 110:
case 111:
case 112:
case 113:
case 114:
case 115:
case 116:
case 117:
case 118:
case 119:
case 120:
case 121:
case 122: // z
ret = b1 - 71; // 26 - 51 output
break;
case 48: // 0
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57: // 9
ret = b1 + 4; // 52 - 61 output range
break;
case (byte)'-':
ret = 62;
break;
case (byte)'_':
ret = 63;
break;
default:
throw new ArgumentException("Invalid Short Guid Pattern.");
}
switch (shift)
{
case Shift.Left2:
return ret << 2;
case Shift.Right4:
return ret >> 4;
case Shift.Left4:
return ret << 4;
case Shift.Right2:
return ret >> 2;
case Shift.Left6:
return ret << 6;
case Shift.NoShift:
return ret;
default:
throw new ArgumentException("Shenanigans!");
}
}
#if NETSTANDARD
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
#endif
static byte SwitchParse(byte b)
{
// '0'(48) ~ '9'(57) => -48
// 'A'(65) ~ 'F'(70) => -55
// 'a'(97) ~ 'f'(102) => -87
switch (b)
{
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
return unchecked((byte)((b - 48)));
case 65:
case 66:
case 67:
case 68:
case 69:
case 70:
return unchecked((byte)((b - 55)));
case 97:
case 98:
case 99:
case 100:
case 101:
case 102:
return unchecked((byte)((b - 87)));
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
case 24:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
case 38:
case 39:
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
case 58:
case 59:
case 60:
case 61:
case 62:
case 63:
case 64:
case 71:
case 72:
case 73:
case 74:
case 75:
case 76:
case 77:
case 78:
case 79:
case 80:
case 81:
case 82:
case 83:
case 84:
case 85:
case 86:
case 87:
case 88:
case 89:
case 90:
case 91:
case 92:
case 93:
case 94:
case 95:
case 96:
default:
throw new ArgumentException("Invalid Guid Pattern.");
}
}
// 4(x2) - 2(x2) - 2(x2) - 2(x2) - 6(x2)
public void Write(byte[] buffer, int offset)
{
if (BitConverter.IsLittleEndian)
{
// int(_a)
buffer[offset + 6] = byteToHexStringHigh[Byte0];
buffer[offset + 7] = byteToHexStringLow[Byte0];
buffer[offset + 4] = byteToHexStringHigh[Byte1];
buffer[offset + 5] = byteToHexStringLow[Byte1];
buffer[offset + 2] = byteToHexStringHigh[Byte2];
buffer[offset + 3] = byteToHexStringLow[Byte2];
buffer[offset + 0] = byteToHexStringHigh[Byte3];
buffer[offset + 1] = byteToHexStringLow[Byte3];
// short(_b)
buffer[offset + 10] = byteToHexStringHigh[Byte4];
buffer[offset + 11] = byteToHexStringLow[Byte4];
buffer[offset + 8] = byteToHexStringHigh[Byte5];
buffer[offset + 9] = byteToHexStringLow[Byte5];
// short(_c)
buffer[offset + 14] = byteToHexStringHigh[Byte6];
buffer[offset + 15] = byteToHexStringLow[Byte6];
buffer[offset + 12] = byteToHexStringHigh[Byte7];
buffer[offset + 13] = byteToHexStringLow[Byte7];
}
else
{
buffer[offset + 0] = byteToHexStringHigh[Byte0];
buffer[offset + 1] = byteToHexStringLow[Byte0];
buffer[offset + 2] = byteToHexStringHigh[Byte1];
buffer[offset + 3] = byteToHexStringLow[Byte1];
buffer[offset + 4] = byteToHexStringHigh[Byte2];
buffer[offset + 5] = byteToHexStringLow[Byte2];
buffer[offset + 6] = byteToHexStringHigh[Byte3];
buffer[offset + 7] = byteToHexStringLow[Byte3];
buffer[offset + 8] = byteToHexStringHigh[Byte4];
buffer[offset + 9] = byteToHexStringLow[Byte4];
buffer[offset + 10] = byteToHexStringHigh[Byte5];
buffer[offset + 11] = byteToHexStringLow[Byte5];
buffer[offset + 12] = byteToHexStringHigh[Byte6];
buffer[offset + 13] = byteToHexStringLow[Byte6];
buffer[offset + 14] = byteToHexStringHigh[Byte7];
buffer[offset + 15] = byteToHexStringLow[Byte7];
}
buffer[offset + 16] = byteToHexStringHigh[Byte8];
buffer[offset + 17] = byteToHexStringLow[Byte8];
buffer[offset + 18] = byteToHexStringHigh[Byte9];
buffer[offset + 19] = byteToHexStringLow[Byte9];
buffer[offset + 20] = byteToHexStringHigh[Byte10];
buffer[offset + 21] = byteToHexStringLow[Byte10];
buffer[offset + 22] = byteToHexStringHigh[Byte11];
buffer[offset + 23] = byteToHexStringLow[Byte11];
buffer[offset + 24] = byteToHexStringHigh[Byte12];
buffer[offset + 25] = byteToHexStringLow[Byte12];
buffer[offset + 26] = byteToHexStringHigh[Byte13];
buffer[offset + 27] = byteToHexStringLow[Byte13];
buffer[offset + 28] = byteToHexStringHigh[Byte14];
buffer[offset + 29] = byteToHexStringLow[Byte14];
buffer[offset + 30] = byteToHexStringHigh[Byte15];
buffer[offset + 31] = byteToHexStringLow[Byte15];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment