using System.Buffers.Text;
using System.Diagnostics;
using System.Runtime.InteropServices;
public static void Main()
Console.WriteLine("Hello World");
Guid guid = new Guid("69190000-08DC-6C24-53B9-08DBBF25527F");
Console.WriteLine(guid.ToString());
ShortGuid shortGuid = new ShortGuid(guid);
Console.WriteLine(shortGuid);
ShortGuid shortGuid2 = new ShortGuid("AAAZadwIJGxTuQjbvyVSfw");
Console.WriteLine(shortGuid2);
Console.WriteLine(shortGuid2.Guid);
Console.WriteLine(new ShortGuid(new Guid("69190000-08dc-6c24-53b9-08dbbf25527f")).ToString());
Console.WriteLine(new ShortGuid(new Guid("69190000-08dc-6c24-5351-08dbbf25527f")).ToString());
Console.WriteLine(new ShortGuid("AAAZadwIJGxTUQjbvyVSfw").Guid.ToString());
for (count = 0; count < testCount; count++)
Guid originalGuid = Guid.NewGuid();
shortGuid = new ShortGuid(originalGuid);
Guid convertedBackGuid = new ShortGuid(shortGuid.ToString()).Guid;
if (!originalGuid.Equals(convertedBackGuid))
Console.WriteLine($"Mismatch found on iteration {count + 1}!");
Console.WriteLine($"Original Guid: {originalGuid}");
Console.WriteLine($"ShortGuid: {shortGuid}");
Console.WriteLine($"Converted Back Guid: {convertedBackGuid}");
Console.WriteLine("Test completed successfully with {0} iterations", count);
[DebuggerDisplay("{Value}")]
public readonly struct ShortGuid : IEquatable<Guid>, IEquatable<ShortGuid>, IEquatable<string>
private const char EqualsChar = '=';
private const char HyphenChar = '-';
private const char UnderscoreChar = '_';
private const char SlashChar = '/';
private const byte SlashByte = (byte)'/';
private const char PlusChar = '+';
private const byte PlusByte = (byte)'+';
#pragma warning disable IDE0032
private readonly Guid underlyingGuid;
private readonly string encodedValue;
#pragma warning restore IDE0032
public ShortGuid(string value)
underlyingGuid = Decode(value);
public ShortGuid(Guid guid)
encodedValue = Encode(underlyingGuid);
public Guid Guid => underlyingGuid;
public override string ToString() => Value;
public string Value => encodedValue;
public static readonly ShortGuid Empty = new(Guid.Empty);
public static string Encode(Guid guid)
Span<byte> guidBytes = stackalloc byte[16];
Span<byte> base64Bytes = stackalloc byte[24];
MemoryMarshal.TryWrite(guidBytes, in guid);
MemoryMarshal.TryWrite(guidBytes, ref guid);
Base64.EncodeToUtf8(guidBytes, base64Bytes, out _, out _);
Span<char> finalChars = stackalloc char[22];
for (int i = 0; i < 22; i++)
finalChars[i] = base64Bytes[i] switch
SlashByte => UnderscoreChar,
_ => (char)base64Bytes[i]
return new string(finalChars);
public static ReadOnlySpan<char> Encode(ReadOnlySpan<char> value)
Guid guid = Guid.Parse(value);
public static Guid Decode(ReadOnlySpan<char> value)
throw new ArgumentException($"A ShortGuid must be exactly 22 characters long. Received a {value.Length} character string.", paramName: nameof(value));
Span<char> base64Chars = stackalloc char[24];
for (int i = 0; i < 22; i++)
base64Chars[i] = value[i] switch
UnderscoreChar => SlashChar,
base64Chars[22] = EqualsChar;
base64Chars[23] = EqualsChar;
Span<byte> idBytes = stackalloc byte[16];
Convert.TryFromBase64Chars(base64Chars, idBytes, out _);
Guid guid = new(idBytes);
string sanityCheck = Encode(guid);
return !value.SequenceEqual(sanityCheck)
? throw new FormatException(
$"Invalid strict ShortGuid encoded string. The string '{new string(value)}' " +
$"but failed a round-trip test expecting '{new string(sanityCheck)}'.")
public static bool TryDecode(ReadOnlySpan<char> value, out Guid guid)
public static bool TryParse(ReadOnlySpan<char> value, out ShortGuid shortGuid)
case { Length: 22 } when TryDecode(value, out Guid sg):
case { Length: 36 } when Guid.TryParse(value, out Guid guid):
public static bool TryParse(ReadOnlySpan<char> value, out Guid guid)
case { Length: 22 } when TryDecode(value, out guid):
case { Length: 36 } when Guid.TryParse(value, out guid):
public bool Equals(Guid other)
return Equals((object)other);
public bool Equals(ShortGuid other)
return Equals((object)other);
public bool Equals(string? other)
return other is not null && Equals((object)other);
public override bool Equals(object? obj)
if (obj is ShortGuid shortGuid)
return underlyingGuid.Equals(shortGuid.underlyingGuid);
return underlyingGuid.Equals(guid);
if (TryDecode(span, out guid))
return underlyingGuid.Equals(guid);
if (Guid.TryParse(span, out guid))
return underlyingGuid.Equals(guid);
if (TryDecode(chars, out guid))
return underlyingGuid.Equals(guid);
if (Guid.TryParse(chars, out guid))
return underlyingGuid.Equals(guid);
public override int GetHashCode() => underlyingGuid.GetHashCode();
public static ShortGuid NewGuid() => new(Guid.NewGuid());
public static bool operator ==(ShortGuid a, ShortGuid b)
=> a.underlyingGuid == b.underlyingGuid;
public static bool operator ==(ShortGuid a, Guid b)
=> a.underlyingGuid == b;
public static bool operator ==(Guid a, ShortGuid b)
public static bool operator !=(ShortGuid a, ShortGuid b)
=> a.underlyingGuid != b.underlyingGuid;
public static bool operator !=(ShortGuid a, Guid b)
=> a.underlyingGuid != b;
public static bool operator !=(Guid a, ShortGuid b)
public static implicit operator ReadOnlySpan<char>(ShortGuid shortGuid) => shortGuid.Value;
public static implicit operator string(ShortGuid shortGuid) => shortGuid.Value;
public static implicit operator Guid(ShortGuid shortGuid) => shortGuid.underlyingGuid;
public static implicit operator ShortGuid(string? value)
if (!string.IsNullOrWhiteSpace(value) && TryParse(value, out ShortGuid shortGuid))
#pragma warning disable S125
#pragma warning restore S125
public static implicit operator ShortGuid(Guid guid)