Created
August 21, 2019 19:54
-
-
Save vanbukin/fd958e039bb666e8348df2bd5510ffa4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
namespace Uuid1 | |
{ | |
public struct Uuidv1 : IFormattable | |
{ | |
static Uuidv1() | |
{ | |
for (var i = 0; i < 256; i++) | |
{ | |
var chars = Convert.ToString(i, 16).PadLeft(2, '0'); | |
TableToHex[i] = ((uint) chars[1] << 16) | chars[0]; | |
} | |
} | |
private static readonly uint[] TableToHex = new uint[256]; | |
public Uuidv1( | |
byte byte0, | |
byte byte1, | |
byte byte2, | |
byte byte3, | |
byte byte4, | |
byte byte5, | |
byte byte6, | |
byte byte7, | |
byte byte8, | |
byte byte9, | |
byte byte10, | |
byte byte11, | |
byte byte12, | |
byte byte13, | |
byte byte14, | |
byte byte15) | |
{ | |
Byte0 = byte0; | |
Byte1 = byte1; | |
Byte2 = byte2; | |
Byte3 = byte3; | |
Byte4 = byte4; | |
Byte5 = byte5; | |
Byte6 = byte6; | |
Byte7 = byte7; | |
Byte8 = byte8; | |
Byte9 = byte9; | |
Byte10 = byte10; | |
Byte11 = byte11; | |
Byte12 = byte12; | |
Byte13 = byte13; | |
Byte14 = byte14; | |
Byte15 = byte15; | |
} | |
private readonly byte Byte0; | |
private readonly byte Byte1; | |
private readonly byte Byte2; | |
private readonly byte Byte3; | |
private readonly byte Byte4; | |
private readonly byte Byte5; | |
private readonly byte Byte6; | |
private readonly byte Byte7; | |
private readonly byte Byte8; | |
private readonly byte Byte9; | |
private readonly byte Byte10; | |
private readonly byte Byte11; | |
private readonly byte Byte12; | |
private readonly byte Byte13; | |
private readonly byte Byte14; | |
private readonly byte Byte15; | |
public Uuidv1(byte[] bytes) | |
{ | |
if (bytes == null) | |
throw new ArgumentNullException(nameof(bytes)); | |
if (bytes.Length != 16) | |
throw new ArgumentOutOfRangeException(nameof(bytes)); | |
Byte15 = bytes[15]; | |
Byte0 = bytes[0]; | |
Byte1 = bytes[1]; | |
Byte2 = bytes[2]; | |
Byte3 = bytes[3]; | |
Byte4 = bytes[4]; | |
Byte5 = bytes[5]; | |
Byte6 = bytes[6]; | |
Byte7 = bytes[7]; | |
Byte8 = bytes[8]; | |
Byte9 = bytes[9]; | |
Byte10 = bytes[10]; | |
Byte11 = bytes[11]; | |
Byte12 = bytes[12]; | |
Byte13 = bytes[13]; | |
Byte14 = bytes[14]; | |
} | |
public unsafe Uuidv1(byte* bytes) | |
{ | |
if (bytes == null) | |
throw new NullReferenceException(nameof(bytes)); | |
Byte15 = bytes[15]; | |
Byte0 = bytes[0]; | |
Byte1 = bytes[1]; | |
Byte2 = bytes[2]; | |
Byte3 = bytes[3]; | |
Byte4 = bytes[4]; | |
Byte5 = bytes[5]; | |
Byte6 = bytes[6]; | |
Byte7 = bytes[7]; | |
Byte8 = bytes[8]; | |
Byte9 = bytes[9]; | |
Byte10 = bytes[10]; | |
Byte11 = bytes[11]; | |
Byte12 = bytes[12]; | |
Byte13 = bytes[13]; | |
Byte14 = bytes[14]; | |
} | |
public Uuidv1(Span<byte> bytes) | |
{ | |
if (bytes == null) | |
throw new NullReferenceException(nameof(bytes)); | |
Byte15 = bytes[15]; | |
Byte0 = bytes[0]; | |
Byte1 = bytes[1]; | |
Byte2 = bytes[2]; | |
Byte3 = bytes[3]; | |
Byte4 = bytes[4]; | |
Byte5 = bytes[5]; | |
Byte6 = bytes[6]; | |
Byte7 = bytes[7]; | |
Byte8 = bytes[8]; | |
Byte9 = bytes[9]; | |
Byte10 = bytes[10]; | |
Byte11 = bytes[11]; | |
Byte12 = bytes[12]; | |
Byte13 = bytes[13]; | |
Byte14 = bytes[14]; | |
} | |
public byte[] ToByteArray() | |
{ | |
return new[] | |
{ | |
Byte0, | |
Byte1, | |
Byte2, | |
Byte3, | |
Byte4, | |
Byte5, | |
Byte6, | |
Byte7, | |
Byte8, | |
Byte9, | |
Byte10, | |
Byte11, | |
Byte12, | |
Byte13, | |
Byte14, | |
Byte15 | |
}; | |
} | |
public bool TryWriteBytes(Span<byte> destination) | |
{ | |
if (BitConverter.IsLittleEndian) return MemoryMarshal.TryWrite(destination, ref this); | |
// slower path for BigEndian | |
if (destination.Length < 16) | |
return false; | |
destination[0] = Byte0; | |
destination[1] = Byte1; | |
destination[2] = Byte2; | |
destination[3] = Byte3; | |
destination[4] = Byte4; | |
destination[5] = Byte5; | |
destination[6] = Byte6; | |
destination[7] = Byte7; | |
destination[8] = Byte8; | |
destination[9] = Byte9; | |
destination[10] = Byte10; | |
destination[11] = Byte11; | |
destination[12] = Byte12; | |
destination[13] = Byte13; | |
destination[14] = Byte14; | |
destination[15] = Byte15; | |
return true; | |
} | |
public override string ToString() => ToString("D", null); | |
public string ToString(string? format) | |
{ | |
return ToString(format, null); | |
} | |
public string ToString(string format, IFormatProvider? provider) | |
{ | |
if (string.IsNullOrEmpty(format)) format = "D"; | |
// all acceptable format strings are of length 1 | |
if (format.Length != 1) | |
throw new FormatException( | |
"Format string can be only \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\"."); | |
int guidSize; | |
switch (format[0]) | |
{ | |
case 'D': | |
case 'd': | |
guidSize = 36; | |
break; | |
case 'N': | |
case 'n': | |
guidSize = 32; | |
break; | |
case 'B': | |
case 'b': | |
case 'P': | |
case 'p': | |
guidSize = 38; | |
break; | |
case 'X': | |
case 'x': | |
guidSize = 68; | |
break; | |
default: | |
throw new FormatException( | |
"Format string can be only \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\"."); | |
} | |
Span<char> guidString = stackalloc char[guidSize]; | |
var result = TryFormat(guidString, out var bytesWritten, format); | |
Debug.Assert(result && bytesWritten == guidString.Length, "Formatting Uuidv1 should have succeeded."); | |
return new string(guidString); | |
} | |
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default) | |
{ | |
if (format.Length == 0) format = "D"; | |
// all acceptable format strings are of length 1 | |
if (format.Length != 1) | |
throw new FormatException( | |
"Format string can be only \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\"."); | |
var dash = true; | |
var hex = false; | |
var braces = 0; | |
int guidSize; | |
switch (format[0]) | |
{ | |
case 'D': | |
case 'd': | |
guidSize = 36; | |
break; | |
case 'N': | |
case 'n': | |
dash = false; | |
guidSize = 32; | |
break; | |
case 'B': | |
case 'b': | |
braces = '{' + ('}' << 16); | |
guidSize = 38; | |
break; | |
case 'P': | |
case 'p': | |
braces = '(' + (')' << 16); | |
guidSize = 38; | |
break; | |
case 'X': | |
case 'x': | |
braces = '{' + ('}' << 16); | |
dash = false; | |
hex = true; | |
guidSize = 68; | |
break; | |
default: | |
throw new FormatException( | |
"Format string can be only \"D\", \"d\", \"N\", \"n\", \"P\", \"p\", \"B\", \"b\", \"X\" or \"x\"."); | |
} | |
if (destination.Length < guidSize) | |
{ | |
charsWritten = 0; | |
return false; | |
} | |
unsafe | |
{ | |
fixed (char* guidCharsReference = &MemoryMarshal.GetReference(destination)) | |
{ | |
var chars = guidCharsReference; | |
var uints = (uint*) chars; | |
var bytes = (byte**) &uints; | |
if (braces != 0) | |
{ | |
*chars++ = (char) braces; | |
*bytes += 2; | |
} | |
if (hex) | |
{ | |
// {0xdddddddd,0xdddd,0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte0]; | |
uints[1] = TableToHex[Byte1]; | |
uints[2] = TableToHex[Byte2]; | |
uints[3] = TableToHex[Byte3]; | |
chars += 8; | |
*bytes += 16; | |
*chars++ = ','; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 6; | |
uints[0] = TableToHex[Byte4]; | |
uints[1] = TableToHex[Byte5]; | |
chars += 4; | |
*bytes += 8; | |
*chars++ = ','; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 6; | |
uints[0] = TableToHex[Byte6]; | |
uints[1] = TableToHex[Byte7]; | |
chars += 4; | |
*bytes += 8; | |
*chars++ = ','; | |
*chars++ = '{'; | |
*bytes += 4; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte8]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = ','; | |
*bytes += 2; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte9]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = ','; | |
*bytes += 2; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte10]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = ','; | |
*bytes += 2; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte11]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = ','; | |
*bytes += 2; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte12]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = ','; | |
*bytes += 2; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte13]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = ','; | |
*bytes += 2; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte14]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = ','; | |
*bytes += 2; | |
*chars++ = '0'; | |
*chars++ = 'x'; | |
*bytes += 4; | |
uints[0] = TableToHex[Byte15]; | |
chars += 2; | |
*bytes += 4; | |
*chars++ = '}'; | |
*bytes += 2; | |
} | |
else | |
{ | |
// [{|(]dddddddd[-]dddd[-]dddd[-]dddd[-]dddddddddddd[}|)] | |
uints[0] = TableToHex[Byte0]; | |
uints[1] = TableToHex[Byte1]; | |
uints[2] = TableToHex[Byte2]; | |
uints[3] = TableToHex[Byte3]; | |
chars += 8; | |
*bytes += 16; | |
if (dash) | |
{ | |
*chars++ = '-'; | |
*bytes += 2; | |
} | |
uints[0] = TableToHex[Byte4]; | |
uints[1] = TableToHex[Byte5]; | |
chars += 4; | |
*bytes += 8; | |
if (dash) | |
{ | |
*chars++ = '-'; | |
*bytes += 2; | |
} | |
uints[0] = TableToHex[Byte6]; | |
uints[1] = TableToHex[Byte7]; | |
chars += 4; | |
*bytes += 8; | |
if (dash) | |
{ | |
*chars++ = '-'; | |
*bytes += 2; | |
} | |
uints[0] = TableToHex[Byte8]; | |
uints[1] = TableToHex[Byte9]; | |
chars += 4; | |
*bytes += 8; | |
if (dash) | |
{ | |
*chars++ = '-'; | |
*bytes += 2; | |
} | |
uints[0] = TableToHex[Byte10]; | |
uints[1] = TableToHex[Byte11]; | |
uints[2] = TableToHex[Byte12]; | |
uints[3] = TableToHex[Byte13]; | |
uints[4] = TableToHex[Byte14]; | |
uints[5] = TableToHex[Byte15]; | |
chars += 12; | |
*bytes += 24; | |
} | |
if (braces != 0) | |
{ | |
*chars++ = (char) (braces >> 16); | |
*bytes += 2; | |
} | |
Debug.Assert(chars - guidCharsReference == guidSize); | |
} | |
} | |
charsWritten = guidSize; | |
return true; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
dich