+88
-27
@@ -1,28 +1,37 @@
|
||||
namespace Just.Core;
|
||||
|
||||
public enum Base32EncodeOptions
|
||||
{
|
||||
None = 0x00,
|
||||
LowerCase = 0x01,
|
||||
NoPadding = 0x02,
|
||||
LowerCaseNoPadding = 0x03,
|
||||
}
|
||||
|
||||
public static class Base32
|
||||
{
|
||||
public const string Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
public const string AlphabetLower = "abcdefghijklmnopqrstuvwxyz234567";
|
||||
public const char Padding = '=';
|
||||
public const int MaxBytesStack = 250;
|
||||
|
||||
[Pure]
|
||||
public static string Encode(ReadOnlySpan<byte> input)
|
||||
public static string Encode(ReadOnlySpan<byte> input, Base32EncodeOptions options = Base32EncodeOptions.None)
|
||||
{
|
||||
if (input.IsEmpty) return string.Empty;
|
||||
|
||||
int outLength = 8 * ((input.Length + 4) / 5);
|
||||
Span<char> output = input.Length <= MaxBytesStack
|
||||
? stackalloc char[outLength]
|
||||
: new char[outLength];
|
||||
Span<char> output = input.Length > MaxBytesStack
|
||||
? new char[outLength]
|
||||
: stackalloc char[outLength];
|
||||
|
||||
var size = Encode(input, output);
|
||||
var size = Encode(input, output, options);
|
||||
|
||||
return new string(output[..size]);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public static int Encode(ReadOnlySpan<byte> input, Span<char> output)
|
||||
public static int Encode(ReadOnlySpan<byte> input, Span<char> output, Base32EncodeOptions options = Base32EncodeOptions.None)
|
||||
{
|
||||
if (input.IsEmpty) return 0;
|
||||
|
||||
@@ -34,25 +43,63 @@ public static class Base32
|
||||
|
||||
output = output[..outputLength];
|
||||
|
||||
int i = 0;
|
||||
ReadOnlySpan<char> alphabet = Alphabet;
|
||||
ReadOnlySpan<char> alphabet = (options & Base32EncodeOptions.LowerCase) == Base32EncodeOptions.LowerCase
|
||||
? AlphabetLower
|
||||
: Alphabet;
|
||||
Span<byte> alphabetKeys = stackalloc byte[8];
|
||||
|
||||
int i = 0;
|
||||
for (int offset = 0; offset < input.Length;)
|
||||
{
|
||||
alphabetKeys.Clear();
|
||||
int numCharsToOutput = GetNextGroup(input, ref offset, alphabetKeys);
|
||||
|
||||
output[i++] = (numCharsToOutput > 0) ? alphabet[alphabetKeys[0]] : Padding;
|
||||
output[i++] = (numCharsToOutput > 1) ? alphabet[alphabetKeys[1]] : Padding;
|
||||
output[i++] = (numCharsToOutput > 2) ? alphabet[alphabetKeys[2]] : Padding;
|
||||
output[i++] = (numCharsToOutput > 3) ? alphabet[alphabetKeys[3]] : Padding;
|
||||
output[i++] = (numCharsToOutput > 4) ? alphabet[alphabetKeys[4]] : Padding;
|
||||
output[i++] = (numCharsToOutput > 5) ? alphabet[alphabetKeys[5]] : Padding;
|
||||
output[i++] = (numCharsToOutput > 6) ? alphabet[alphabetKeys[6]] : Padding;
|
||||
output[i++] = (numCharsToOutput > 7) ? alphabet[alphabetKeys[7]] : Padding;
|
||||
output[i++] = alphabet[alphabetKeys[0]];
|
||||
output[i++] = alphabet[alphabetKeys[1]];
|
||||
|
||||
if (numCharsToOutput < 3)
|
||||
{
|
||||
i = FillWithPadding(output, i, numCharsToOutput, options);
|
||||
break;
|
||||
}
|
||||
output[i++] = alphabet[alphabetKeys[2]];
|
||||
output[i++] = alphabet[alphabetKeys[3]];
|
||||
|
||||
if (numCharsToOutput < 5)
|
||||
{
|
||||
i = FillWithPadding(output, i, numCharsToOutput, options);
|
||||
break;
|
||||
}
|
||||
output[i++] = alphabet[alphabetKeys[4]];
|
||||
|
||||
if (numCharsToOutput < 6)
|
||||
{
|
||||
i = FillWithPadding(output, i, numCharsToOutput, options);
|
||||
break;
|
||||
}
|
||||
output[i++] = alphabet[alphabetKeys[5]];
|
||||
output[i++] = alphabet[alphabetKeys[6]];
|
||||
|
||||
if (numCharsToOutput < 8)
|
||||
{
|
||||
i = FillWithPadding(output, i, numCharsToOutput, options);
|
||||
break;
|
||||
}
|
||||
output[i++] = alphabet[alphabetKeys[7]];
|
||||
}
|
||||
|
||||
return i;
|
||||
|
||||
static int FillWithPadding(Span<char> output, int i, int numCharsToOutput, Base32EncodeOptions options)
|
||||
{
|
||||
if ((options & Base32EncodeOptions.NoPadding) == Base32EncodeOptions.NoPadding)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
var pad = 8 - numCharsToOutput;
|
||||
output[i..(i+pad)].Fill(Padding);
|
||||
return i + pad;
|
||||
}
|
||||
}
|
||||
|
||||
[Pure]
|
||||
@@ -61,10 +108,10 @@ public static class Base32
|
||||
input = input.TrimEnd(Padding);
|
||||
if (input.IsEmpty) return [];
|
||||
|
||||
var outputLength = 5 * ((input.Length + 7) / 8);
|
||||
Span<byte> output = outputLength <= MaxBytesStack
|
||||
? stackalloc byte[outputLength]
|
||||
: new byte[outputLength];
|
||||
var outputLength = 5 * input.Length / 8;
|
||||
Span<byte> output = outputLength > MaxBytesStack
|
||||
? new byte[outputLength]
|
||||
: stackalloc byte[outputLength];
|
||||
|
||||
var size = Decode(input, output);
|
||||
|
||||
@@ -76,7 +123,13 @@ public static class Base32
|
||||
{
|
||||
input = input.TrimEnd(Padding);
|
||||
|
||||
var outputLength = 5 * ((input.Length + 7) / 8);
|
||||
var rem = input.Length % 8;
|
||||
if (rem == 1 || rem == 3 || rem == 6)
|
||||
{
|
||||
throw new FormatException("Invalid Base32 string length.");
|
||||
}
|
||||
|
||||
var outputLength = 5 * input.Length / 8;
|
||||
if (output.Length < outputLength)
|
||||
{
|
||||
throw new ArgumentException("Decoded input can not fit in output span.", nameof(output));
|
||||
@@ -85,9 +138,9 @@ public static class Base32
|
||||
output = output[..outputLength];
|
||||
output.Clear();
|
||||
|
||||
Span<char> inputspan = outputLength <= MaxBytesStack
|
||||
? stackalloc char[input.Length]
|
||||
: new char[input.Length];
|
||||
Span<char> inputspan = outputLength > MaxBytesStack
|
||||
? new char[input.Length]
|
||||
: stackalloc char[input.Length];
|
||||
input.ToUpperInvariant(inputspan);
|
||||
|
||||
int bitIndex = 0;
|
||||
@@ -127,10 +180,17 @@ public static class Base32
|
||||
inputIndex++;
|
||||
bitIndex = 0;
|
||||
}
|
||||
else if (inputIndex == input.Length -1) break;
|
||||
else if (inputIndex == input.Length -1)
|
||||
{
|
||||
if ((byteIndex & ~(0x1f << (bitPos - bits))) > 0)
|
||||
{
|
||||
throw new FormatException("Invalid Base32 string. Inconsistent tail bits.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return outputIndex + (outputBits + 7) / 8;
|
||||
return outputIndex;
|
||||
}
|
||||
|
||||
// returns the number of bytes that were output
|
||||
@@ -145,7 +205,8 @@ public static class Base32
|
||||
4 => 7,
|
||||
_ => 8,
|
||||
};
|
||||
uint b1 = (offset < input.Length) ? input[offset++] : 0U;
|
||||
|
||||
uint b1 = input[offset++];
|
||||
uint b2 = (offset < input.Length) ? input[offset++] : 0U;
|
||||
uint b3 = (offset < input.Length) ? input[offset++] : 0U;
|
||||
uint b4 = (offset < input.Length) ? input[offset++] : 0U;
|
||||
|
||||
Reference in New Issue
Block a user