using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Umbraco.Core
{
///
/// Utility methods for the struct.
///
internal static class GuidUtils
{
///
/// Combines two guid instances utilizing an exclusive disjunction.
/// The resultant guid is not guaranteed to be unique since the number of unique bits is halved.
///
/// The first guid.
/// The seconds guid.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Guid Combine(Guid a, Guid b)
{
var ad = new DecomposedGuid(a);
var bd = new DecomposedGuid(b);
ad.Hi ^= bd.Hi;
ad.Lo ^= bd.Lo;
return ad.Value;
}
///
/// A decomposed guid. Allows access to the high and low bits without unsafe code.
///
[StructLayout(LayoutKind.Explicit)]
private struct DecomposedGuid
{
[FieldOffset(00)] public Guid Value;
[FieldOffset(00)] public long Hi;
[FieldOffset(08)] public long Lo;
public DecomposedGuid(Guid value) : this() => this.Value = value;
}
private static readonly char[] Base32Table =
{
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5'
};
///
/// Converts a Guid into a base-32 string.
///
/// A Guid.
/// The string length.
/// A base-32 encoded string.
///
/// A base-32 string representation of a Guid is the shortest, efficient, representation
/// that is case insensitive (base-64 is case sensitive).
/// Length must be 1-26, anything else becomes 26.
///
public static string ToBase32String(Guid guid, int length = 26)
{
if (length <= 0 || length > 26)
length = 26;
var bytes = guid.ToByteArray(); // a Guid is 128 bits ie 16 bytes
// this could be optimized by making it unsafe,
// and fixing the table + bytes + chars (see Convert.ToBase64CharArray)
// each block of 5 bytes = 5*8 = 40 bits
// becomes 40 bits = 8*5 = 8 byte-32 chars
// a Guid is 3 blocks + 8 bits
// so it turns into a 3*8+2 = 26 chars string
var chars = new char[length];
var i = 0;
var j = 0;
while (i < 15)
{
if (j == length) break;
chars[j++] = Base32Table[(bytes[i] & 0b1111_1000) >> 3];
if (j == length) break;
chars[j++] = Base32Table[((bytes[i] & 0b0000_0111) << 2) | ((bytes[i + 1] & 0b1100_0000) >> 6)];
if (j == length) break;
chars[j++] = Base32Table[(bytes[i + 1] & 0b0011_1110) >> 1];
if (j == length) break;
chars[j++] = Base32Table[(bytes[i + 1] & 0b0000_0001) | ((bytes[i + 2] & 0b1111_0000) >> 4)];
if (j == length) break;
chars[j++] = Base32Table[((bytes[i + 2] & 0b0000_1111) << 1) | ((bytes[i + 3] & 0b1000_0000) >> 7)];
if (j == length) break;
chars[j++] = Base32Table[(bytes[i + 3] & 0b0111_1100) >> 2];
if (j == length) break;
chars[j++] = Base32Table[((bytes[i + 3] & 0b0000_0011) << 3) | ((bytes[i + 4] & 0b1110_0000) >> 5)];
if (j == length) break;
chars[j++] = Base32Table[bytes[i + 4] & 0b0001_1111];
i += 5;
}
if (j < length)
chars[j++] = Base32Table[(bytes[i] & 0b1111_1000) >> 3];
if (j < length)
chars[j] = Base32Table[(bytes[i] & 0b0000_0111) << 2];
return new string(chars);
}
}
}