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); } } }