namespace Just.Core; public static class Base64Url { [Pure] public static Guid DecodeGuid(ReadOnlySpan value) { ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, 22); Span guidBytes = stackalloc byte[16]; Span chars = stackalloc char[24]; value.CopyTo(chars); for (int i = 0; i < value.Length; i++) { switch (value[i]) { case '-': chars[i] = '+'; continue; case '_': chars[i] = '/'; continue; default: continue; } } chars[^2..].Fill('='); if (!Convert.TryFromBase64Chars(chars, guidBytes, out int _)) throw new FormatException("Invalid Base64 string."); return new Guid(guidBytes); } [Pure] public static byte[] Decode(ReadOnlySpan input) { if (input.IsEmpty) return []; Span output = stackalloc byte[3 * ((input.Length + 3) / 4)]; var size = Decode(input, output); return output[..size].ToArray(); } [Pure] public static int Decode(ReadOnlySpan value, Span output) { var padding = (4 - (value.Length & 3)) & 3; var charlen = value.Length + padding; var outputBytes = charlen / 4; ArgumentOutOfRangeException.ThrowIfLessThan(output.Length, outputBytes); Span chars = stackalloc char[charlen]; value.CopyTo(chars); for (int i = 0; i < value.Length; i++) { switch (value[i]) { case '-': chars[i] = '+'; continue; case '_': chars[i] = '/'; continue; default: continue; } } chars[^padding..].Fill('='); if (!Convert.TryFromBase64Chars(chars, output, out outputBytes)) throw new FormatException("Invalid Base64 string."); return outputBytes; } [Pure] public static string Encode(in Guid id) { Span guidBytes = stackalloc byte[16]; id.TryWriteBytes(guidBytes); Span chars = stackalloc char[24]; Convert.TryToBase64Chars(guidBytes, chars, out int _); for (int i = 0; i < chars.Length - 2; i++) { switch (chars[i]) { case '+': chars[i] = '-'; continue; case '/': chars[i] = '_'; continue; default: continue; } } return new string(chars[..^2]); } [Pure] public static string Encode(ReadOnlySpan input) { if (input.IsEmpty) return string.Empty; int outLength = 8 * ((input.Length + 5) / 6); Span output = stackalloc char[outLength]; int strlen = Encode(input, output); return new string(output[..strlen]); } [Pure] public static int Encode(ReadOnlySpan input, Span output) { if (input.IsEmpty) return 0; var charlen = 8 * ((input.Length + 5) / 6); Span chars = stackalloc char[charlen]; Convert.TryToBase64Chars(input, chars, out int charsWritten); int i; for (i = 0; i < charsWritten; i++) { switch (chars[i]) { case '+': chars[i] = '-'; continue; case '/': chars[i] = '_'; continue; case '=': goto exitLoop; default: continue; } } exitLoop: chars[..i].CopyTo(output); return i; } }