minor refactoring and tests
This commit is contained in:
@@ -7,7 +7,7 @@ public class Decode
|
|||||||
[InlineData(554121)]
|
[InlineData(554121)]
|
||||||
[InlineData(100454567)]
|
[InlineData(100454567)]
|
||||||
[InlineData(3210589)]
|
[InlineData(3210589)]
|
||||||
public void WhenEncodedToString_ShouldBeDecodedToTheSameByteArray(int seed)
|
public void WhenBytesEncodedToString_ShouldBeDecodedToTheSameByteArray(int seed)
|
||||||
{
|
{
|
||||||
var rng = new Random(seed);
|
var rng = new Random(seed);
|
||||||
|
|
||||||
@@ -23,6 +23,43 @@ public class Decode
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(72121)]
|
||||||
|
[InlineData(554121)]
|
||||||
|
[InlineData(100454567)]
|
||||||
|
[InlineData(3210589)]
|
||||||
|
public void WhenLongEncodedToString_ShouldBeDecodedToTheSameLongArray(int seed)
|
||||||
|
{
|
||||||
|
var rng = new Random(seed);
|
||||||
|
|
||||||
|
for (int i = 1; i <= 512; i++)
|
||||||
|
{
|
||||||
|
var testLong = rng.NextInt64();
|
||||||
|
|
||||||
|
var resultString = Base64Url.Encode(testLong);
|
||||||
|
var resultLong = Base64Url.DecodeLong(resultString);
|
||||||
|
|
||||||
|
resultLong.Should().Be(testLong);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("RgGxr0_n1ZI", -7866126844696657594L)]
|
||||||
|
[InlineData("sxAPfpKB5kY", 5108913293478531251L)]
|
||||||
|
[InlineData("lO4_uitvLCg", 2894810894091415188L)]
|
||||||
|
[InlineData("awxjIqZWz10", 6759716837247880299L)]
|
||||||
|
[InlineData("VjNe72vug4U", -8825948697371200682L)]
|
||||||
|
[InlineData("AAAAAAAAAAA", 0L)]
|
||||||
|
[InlineData("__________8", -1L)]
|
||||||
|
[InlineData("AQAAAAAAAAA", 1L)]
|
||||||
|
[InlineData("CgAAAAAAAAA", 10L)]
|
||||||
|
[InlineData("6AMAAAAAAAA", 1000L)]
|
||||||
|
public void WhenCalled_ShouldReturnValidLong(string testString, long expected)
|
||||||
|
{
|
||||||
|
var result = Base64Url.DecodeLong(testString);
|
||||||
|
result.Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("5QrdUxDUVkCAEGw8pvLsEw", "53dd0ae5-d410-4056-8010-6c3ca6f2ec13")]
|
[InlineData("5QrdUxDUVkCAEGw8pvLsEw", "53dd0ae5-d410-4056-8010-6c3ca6f2ec13")]
|
||||||
[InlineData("6nE2uKQ4_0ar9kpmybgkdw", "b83671ea-38a4-46ff-abf6-4a66c9b82477")]
|
[InlineData("6nE2uKQ4_0ar9kpmybgkdw", "b83671ea-38a4-46ff-abf6-4a66c9b82477")]
|
||||||
@@ -75,10 +112,23 @@ public class Decode
|
|||||||
action.Should().Throw<FormatException>();
|
action.Should().Throw<FormatException>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("RgG&r0_n1ZI")]
|
||||||
|
[InlineData("sxA fpKB5kY")]
|
||||||
|
[InlineData("lO4_uitvL)g")]
|
||||||
|
[InlineData("awxjIqZ^z10")]
|
||||||
|
[InlineData("VjNe7!vug4U")]
|
||||||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1806:Do not ignore method results", Justification = "Test case")]
|
||||||
|
public void WhenCalledWithInvalidLongString_ShouldThrowFormatException(string testString)
|
||||||
|
{
|
||||||
|
Action action = () => Base64Url.DecodeLong(testString);
|
||||||
|
action.Should().Throw<FormatException>();
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(null)]
|
[InlineData(null)]
|
||||||
[InlineData("")]
|
[InlineData("")]
|
||||||
public void WhenCalledWithNullString_ShouldReturnEmptyArray(string testString)
|
public void WhenCalledWithNullString_ShouldReturnEmptyArray(string? testString)
|
||||||
{
|
{
|
||||||
Base64Url.Decode(testString).Should().BeEmpty();
|
Base64Url.Decode(testString).Should().BeEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,23 @@ public class Encode
|
|||||||
result.Should().Be(expected);
|
result.Should().Be(expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("RgGxr0_n1ZI", -7866126844696657594L)]
|
||||||
|
[InlineData("sxAPfpKB5kY", 5108913293478531251L)]
|
||||||
|
[InlineData("lO4_uitvLCg", 2894810894091415188L)]
|
||||||
|
[InlineData("awxjIqZWz10", 6759716837247880299L)]
|
||||||
|
[InlineData("VjNe72vug4U", -8825948697371200682L)]
|
||||||
|
[InlineData("AAAAAAAAAAA", 0L)]
|
||||||
|
[InlineData("__________8", -1L)]
|
||||||
|
[InlineData("AQAAAAAAAAA", 1L)]
|
||||||
|
[InlineData("CgAAAAAAAAA", 10L)]
|
||||||
|
[InlineData("6AMAAAAAAAA", 1000L)]
|
||||||
|
public void WhenCalledWithLong_ShouldReturnValidString(string expected, long testLong)
|
||||||
|
{
|
||||||
|
var result = Base64Url.Encode(testLong);
|
||||||
|
result.Should().Be(expected);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("IA", new byte[]{ 0x20, })]
|
[InlineData("IA", new byte[]{ 0x20, })]
|
||||||
[InlineData("Ag", new byte[]{ 0x02, })]
|
[InlineData("Ag", new byte[]{ 0x02, })]
|
||||||
@@ -33,7 +50,7 @@ public class Encode
|
|||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(new byte[] { })]
|
[InlineData(new byte[] { })]
|
||||||
[InlineData(null)]
|
[InlineData(null)]
|
||||||
public void WhenCalledWithEmptyByteArray_ShouldReturnEmptyString(byte[] testArray)
|
public void WhenCalledWithEmptyByteArray_ShouldReturnEmptyString(byte[]? testArray)
|
||||||
{
|
{
|
||||||
var actualBase32 = Base64Url.Encode(testArray);
|
var actualBase32 = Base64Url.Encode(testArray);
|
||||||
actualBase32.Should().Be(string.Empty);
|
actualBase32.Should().Be(string.Empty);
|
||||||
@@ -42,7 +59,7 @@ public class Encode
|
|||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(new byte[] { })]
|
[InlineData(new byte[] { })]
|
||||||
[InlineData(null)]
|
[InlineData(null)]
|
||||||
public void WhenCalledWithEmptyByteArray_ShouldReturnZeroAndNotChangeOutput(byte[] testArray)
|
public void WhenCalledWithEmptyByteArray_ShouldReturnZeroAndNotChangeOutput(byte[]? testArray)
|
||||||
{
|
{
|
||||||
char[] output = ['1', '2', '3', '4'];
|
char[] output = ['1', '2', '3', '4'];
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ public static class Base32
|
|||||||
? stackalloc char[outLength]
|
? stackalloc char[outLength]
|
||||||
: new char[outLength];
|
: new char[outLength];
|
||||||
|
|
||||||
_ = Encode(input, output);
|
var size = Encode(input, output);
|
||||||
|
|
||||||
return new string(output);
|
return new string(output[..size]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Pure]
|
[Pure]
|
||||||
@@ -26,20 +26,31 @@ public static class Base32
|
|||||||
{
|
{
|
||||||
if (input.IsEmpty) return 0;
|
if (input.IsEmpty) return 0;
|
||||||
|
|
||||||
|
int outputLength = 8 * ((input.Length + 4) / 5);
|
||||||
|
if (output.Length < outputLength)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Encoded input can not fit in output span.", nameof(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
output = output[..outputLength];
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
ReadOnlySpan<char> alphabet = Alphabet;
|
ReadOnlySpan<char> alphabet = Alphabet;
|
||||||
|
Span<byte> alphabetKeys = stackalloc byte[8];
|
||||||
|
|
||||||
for (int offset = 0; offset < input.Length;)
|
for (int offset = 0; offset < input.Length;)
|
||||||
{
|
{
|
||||||
int numCharsToOutput = GetNextGroup(input, ref offset, out byte a, out byte b, out byte c, out byte d, out byte e, out byte f, out byte g, out byte h);
|
alphabetKeys.Clear();
|
||||||
|
int numCharsToOutput = GetNextGroup(input, ref offset, alphabetKeys);
|
||||||
|
|
||||||
output[i++] = (numCharsToOutput > 0) ? alphabet[a] : Padding;
|
output[i++] = (numCharsToOutput > 0) ? alphabet[alphabetKeys[0]] : Padding;
|
||||||
output[i++] = (numCharsToOutput > 1) ? alphabet[b] : Padding;
|
output[i++] = (numCharsToOutput > 1) ? alphabet[alphabetKeys[1]] : Padding;
|
||||||
output[i++] = (numCharsToOutput > 2) ? alphabet[c] : Padding;
|
output[i++] = (numCharsToOutput > 2) ? alphabet[alphabetKeys[2]] : Padding;
|
||||||
output[i++] = (numCharsToOutput > 3) ? alphabet[d] : Padding;
|
output[i++] = (numCharsToOutput > 3) ? alphabet[alphabetKeys[3]] : Padding;
|
||||||
output[i++] = (numCharsToOutput > 4) ? alphabet[e] : Padding;
|
output[i++] = (numCharsToOutput > 4) ? alphabet[alphabetKeys[4]] : Padding;
|
||||||
output[i++] = (numCharsToOutput > 5) ? alphabet[f] : Padding;
|
output[i++] = (numCharsToOutput > 5) ? alphabet[alphabetKeys[5]] : Padding;
|
||||||
output[i++] = (numCharsToOutput > 6) ? alphabet[g] : Padding;
|
output[i++] = (numCharsToOutput > 6) ? alphabet[alphabetKeys[6]] : Padding;
|
||||||
output[i++] = (numCharsToOutput > 7) ? alphabet[h] : Padding;
|
output[i++] = (numCharsToOutput > 7) ? alphabet[alphabetKeys[7]] : Padding;
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -66,6 +77,11 @@ public static class Base32
|
|||||||
input = input.TrimEnd(Padding);
|
input = input.TrimEnd(Padding);
|
||||||
|
|
||||||
var outputLength = 5 * ((input.Length + 7) / 8);
|
var outputLength = 5 * ((input.Length + 7) / 8);
|
||||||
|
if (output.Length < outputLength)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Decoded input can not fit in output span.", nameof(output));
|
||||||
|
}
|
||||||
|
|
||||||
output = output[..outputLength];
|
output = output[..outputLength];
|
||||||
output.Clear();
|
output.Clear();
|
||||||
|
|
||||||
@@ -119,7 +135,7 @@ public static class Base32
|
|||||||
|
|
||||||
// returns the number of bytes that were output
|
// returns the number of bytes that were output
|
||||||
[Pure]
|
[Pure]
|
||||||
private static int GetNextGroup(ReadOnlySpan<byte> input, ref int offset, out byte a, out byte b, out byte c, out byte d, out byte e, out byte f, out byte g, out byte h)
|
private static int GetNextGroup(ReadOnlySpan<byte> input, ref int offset, Span<byte> alphabetKeys)
|
||||||
{
|
{
|
||||||
var retVal = (input.Length - offset) switch
|
var retVal = (input.Length - offset) switch
|
||||||
{
|
{
|
||||||
@@ -135,14 +151,14 @@ public static class Base32
|
|||||||
uint b4 = (offset < input.Length) ? input[offset++] : 0U;
|
uint b4 = (offset < input.Length) ? input[offset++] : 0U;
|
||||||
uint b5 = (offset < input.Length) ? input[offset++] : 0U;
|
uint b5 = (offset < input.Length) ? input[offset++] : 0U;
|
||||||
|
|
||||||
a = (byte)(b1 >> 3);
|
alphabetKeys[0] = (byte)(b1 >> 3);
|
||||||
b = (byte)(((b1 & 0x07) << 2) | (b2 >> 6));
|
alphabetKeys[1] = (byte)(((b1 & 0x07) << 2) | (b2 >> 6));
|
||||||
c = (byte)((b2 >> 1) & 0x1f);
|
alphabetKeys[2] = (byte)((b2 >> 1) & 0x1f);
|
||||||
d = (byte)(((b2 & 0x01) << 4) | (b3 >> 4));
|
alphabetKeys[3] = (byte)(((b2 & 0x01) << 4) | (b3 >> 4));
|
||||||
e = (byte)(((b3 & 0x0f) << 1) | (b4 >> 7));
|
alphabetKeys[4] = (byte)(((b3 & 0x0f) << 1) | (b4 >> 7));
|
||||||
f = (byte)((b4 >> 2) & 0x1f);
|
alphabetKeys[5] = (byte)((b4 >> 2) & 0x1f);
|
||||||
g = (byte)(((b4 & 0x3) << 3) | (b5 >> 5));
|
alphabetKeys[6] = (byte)(((b4 & 0x3) << 3) | (b5 >> 5));
|
||||||
h = (byte)(b5 & 0x1f);
|
alphabetKeys[7] = (byte)(b5 & 0x1f);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,41 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Just.Core;
|
namespace Just.Core;
|
||||||
|
|
||||||
public static class Base64Url
|
public static class Base64Url
|
||||||
{
|
{
|
||||||
|
private const char Padding = '=';
|
||||||
|
|
||||||
|
[Pure] public static long DecodeLong(ReadOnlySpan<char> value)
|
||||||
|
{
|
||||||
|
ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, 11);
|
||||||
|
|
||||||
|
Span<byte> longBytes = stackalloc byte[8];
|
||||||
|
Span<char> chars = stackalloc char[12];
|
||||||
|
|
||||||
|
value.CopyTo(chars);
|
||||||
|
chars[^1] = Padding;
|
||||||
|
|
||||||
|
ReplaceNonUrlChars(chars);
|
||||||
|
|
||||||
|
if (!Convert.TryFromBase64Chars(chars, longBytes, out int _))
|
||||||
|
throw new FormatException("Invalid Base64 string.");
|
||||||
|
|
||||||
|
return MemoryMarshal.Read<long>(longBytes);
|
||||||
|
}
|
||||||
|
|
||||||
[Pure] public static Guid DecodeGuid(ReadOnlySpan<char> value)
|
[Pure] public static Guid DecodeGuid(ReadOnlySpan<char> value)
|
||||||
{
|
{
|
||||||
ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, 22);
|
ArgumentOutOfRangeException.ThrowIfNotEqual(value.Length, 22);
|
||||||
|
|
||||||
Span<byte> guidBytes = stackalloc byte[16];
|
Span<byte> guidBytes = stackalloc byte[16];
|
||||||
Span<char> chars = stackalloc char[24];
|
Span<char> chars = stackalloc char[24];
|
||||||
|
|
||||||
value.CopyTo(chars);
|
value.CopyTo(chars);
|
||||||
for (int i = 0; i < value.Length; i++)
|
chars[^2..].Fill(Padding);
|
||||||
{
|
|
||||||
switch (value[i])
|
ReplaceNonUrlChars(chars);
|
||||||
{
|
|
||||||
case '-': chars[i] = '+'; continue;
|
|
||||||
case '_': chars[i] = '/'; continue;
|
|
||||||
default: continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chars[^2..].Fill('=');
|
|
||||||
if (!Convert.TryFromBase64Chars(chars, guidBytes, out int _))
|
if (!Convert.TryFromBase64Chars(chars, guidBytes, out int _))
|
||||||
throw new FormatException("Invalid Base64 string.");
|
throw new FormatException("Invalid Base64 string.");
|
||||||
|
|
||||||
@@ -40,21 +57,14 @@ public static class Base64Url
|
|||||||
{
|
{
|
||||||
var padding = (4 - (value.Length & 3)) & 3;
|
var padding = (4 - (value.Length & 3)) & 3;
|
||||||
var charlen = value.Length + padding;
|
var charlen = value.Length + padding;
|
||||||
var outputBytes = charlen / 4;
|
var outputBytes = 3 * (charlen / 4);
|
||||||
ArgumentOutOfRangeException.ThrowIfLessThan(output.Length, outputBytes);
|
ArgumentOutOfRangeException.ThrowIfLessThan(output.Length, outputBytes);
|
||||||
Span<char> chars = stackalloc char[charlen];
|
Span<char> chars = stackalloc char[charlen];
|
||||||
|
|
||||||
value.CopyTo(chars);
|
value.CopyTo(chars);
|
||||||
for (int i = 0; i < value.Length; i++)
|
chars[^padding..].Fill(Padding);
|
||||||
{
|
|
||||||
switch (value[i])
|
ReplaceNonUrlChars(chars);
|
||||||
{
|
|
||||||
case '-': chars[i] = '+'; continue;
|
|
||||||
case '_': chars[i] = '/'; continue;
|
|
||||||
default: continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chars[^padding..].Fill('=');
|
|
||||||
|
|
||||||
if (!Convert.TryFromBase64Chars(chars, output, out outputBytes))
|
if (!Convert.TryFromBase64Chars(chars, output, out outputBytes))
|
||||||
throw new FormatException("Invalid Base64 string.");
|
throw new FormatException("Invalid Base64 string.");
|
||||||
@@ -62,6 +72,18 @@ public static class Base64Url
|
|||||||
return outputBytes;
|
return outputBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Pure] public static string Encode(in long id)
|
||||||
|
{
|
||||||
|
Span<byte> longBytes = stackalloc byte[8];
|
||||||
|
MemoryMarshal.Write(longBytes, id);
|
||||||
|
|
||||||
|
Span<char> chars = stackalloc char[12];
|
||||||
|
Convert.TryToBase64Chars(longBytes, chars, out int _);
|
||||||
|
ReplaceUrlChars(chars[..^1]);
|
||||||
|
|
||||||
|
return new string(chars[..^1]);
|
||||||
|
}
|
||||||
|
|
||||||
[Pure] public static string Encode(in Guid id)
|
[Pure] public static string Encode(in Guid id)
|
||||||
{
|
{
|
||||||
Span<byte> guidBytes = stackalloc byte[16];
|
Span<byte> guidBytes = stackalloc byte[16];
|
||||||
@@ -69,15 +91,7 @@ public static class Base64Url
|
|||||||
Span<char> chars = stackalloc char[24];
|
Span<char> chars = stackalloc char[24];
|
||||||
Convert.TryToBase64Chars(guidBytes, chars, out int _);
|
Convert.TryToBase64Chars(guidBytes, chars, out int _);
|
||||||
|
|
||||||
for (int i = 0; i < chars.Length - 2; i++)
|
ReplaceUrlChars(chars[..^2]);
|
||||||
{
|
|
||||||
switch (chars[i])
|
|
||||||
{
|
|
||||||
case '+': chars[i] = '-'; continue;
|
|
||||||
case '/': chars[i] = '_'; continue;
|
|
||||||
default: continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new string(chars[..^2]);
|
return new string(chars[..^2]);
|
||||||
}
|
}
|
||||||
@@ -86,7 +100,7 @@ public static class Base64Url
|
|||||||
{
|
{
|
||||||
if (input.IsEmpty) return string.Empty;
|
if (input.IsEmpty) return string.Empty;
|
||||||
|
|
||||||
int outLength = 8 * ((input.Length + 5) / 6);
|
int outLength = 4 * ((input.Length + 2) / 3);
|
||||||
Span<char> output = stackalloc char[outLength];
|
Span<char> output = stackalloc char[outLength];
|
||||||
|
|
||||||
int strlen = Encode(input, output);
|
int strlen = Encode(input, output);
|
||||||
@@ -97,24 +111,47 @@ public static class Base64Url
|
|||||||
{
|
{
|
||||||
if (input.IsEmpty) return 0;
|
if (input.IsEmpty) return 0;
|
||||||
|
|
||||||
var charlen = 8 * ((input.Length + 5) / 6);
|
var charlen = 4 * ((input.Length + 2) / 3);
|
||||||
Span<char> chars = stackalloc char[charlen];
|
Span<char> chars = stackalloc char[charlen];
|
||||||
Convert.TryToBase64Chars(input, chars, out int charsWritten);
|
Convert.TryToBase64Chars(input, chars, out int charsWritten);
|
||||||
|
|
||||||
int i;
|
int i = ReplaceUrlChars(chars[..charsWritten]);
|
||||||
for (i = 0; i < charsWritten; i++)
|
chars[..i].CopyTo(output);
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ReplaceUrlChars(Span<char> chars)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (; i < chars.Length; i++)
|
||||||
{
|
{
|
||||||
switch (chars[i])
|
switch (chars[i])
|
||||||
{
|
{
|
||||||
case '+': chars[i] = '-'; continue;
|
case '+': chars[i] = '-'; continue;
|
||||||
case '/': chars[i] = '_'; continue;
|
case '/': chars[i] = '_'; continue;
|
||||||
case '=': goto exitLoop;
|
case Padding: goto break_loop;
|
||||||
default: continue;
|
default: continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exitLoop:
|
break_loop:
|
||||||
chars[..i].CopyTo(output);
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int ReplaceNonUrlChars(Span<char> chars)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (; i < chars.Length; i++)
|
||||||
|
{
|
||||||
|
switch (chars[i])
|
||||||
|
{
|
||||||
|
case '-': chars[i] = '+'; continue;
|
||||||
|
case '_': chars[i] = '/'; continue;
|
||||||
|
case Padding: goto break_loop;
|
||||||
|
default: continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break_loop:
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,12 @@ public class ImmutableSequence<T> :
|
|||||||
IReadOnlyList<T>,
|
IReadOnlyList<T>,
|
||||||
IEquatable<ImmutableSequence<T>>
|
IEquatable<ImmutableSequence<T>>
|
||||||
{
|
{
|
||||||
private static readonly int InitialHash = typeof(ImmutableSequence<T>).GetHashCode();
|
|
||||||
private static readonly Func<T?, T?, bool> CompareItem = EqualityComparer<T>.Default.Equals;
|
private static readonly Func<T?, T?, bool> CompareItem = EqualityComparer<T>.Default.Equals;
|
||||||
private readonly ImmutableList<T> _values;
|
private readonly ImmutableList<T> _values;
|
||||||
|
|
||||||
|
public ImmutableSequence() => _values = [];
|
||||||
public ImmutableSequence(ImmutableList<T> values) => _values = values;
|
public ImmutableSequence(ImmutableList<T> values) => _values = values;
|
||||||
public ImmutableSequence() : this(ImmutableArray<T>.Empty)
|
public ImmutableSequence(IEnumerable<T> values) => _values = [..values];
|
||||||
{
|
|
||||||
}
|
|
||||||
public ImmutableSequence(IEnumerable<T> values)
|
|
||||||
{
|
|
||||||
_values = [..values];
|
|
||||||
}
|
|
||||||
public ImmutableSequence(ReadOnlySpan<T> values) : this(ImmutableList.Create(values))
|
public ImmutableSequence(ReadOnlySpan<T> values) : this(ImmutableList.Create(values))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -37,10 +31,10 @@ public class ImmutableSequence<T> :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual ImmutableSequence<T> ConstructNew(ImmutableList<T> values) => new(values);
|
protected virtual ImmutableSequence<T> ConstructNew(ImmutableList<T> values) => [..values];
|
||||||
|
|
||||||
public ImmutableSequence<T> Add(T value) => ConstructNew([.._values, value]);
|
public ImmutableSequence<T> Add(T value) => ConstructNew(_values.Add(value));
|
||||||
public ImmutableSequence<T> AddFront(T value) => ConstructNew([value, .._values]);
|
public ImmutableSequence<T> AddFront(T value) => ConstructNew(_values.Insert(0, value));
|
||||||
|
|
||||||
public ImmutableList<T>.Enumerator GetEnumerator() => _values.GetEnumerator();
|
public ImmutableList<T>.Enumerator GetEnumerator() => _values.GetEnumerator();
|
||||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => ((IEnumerable<T>)_values).GetEnumerator();
|
IEnumerator<T> IEnumerable<T>.GetEnumerator() => ((IEnumerable<T>)_values).GetEnumerator();
|
||||||
@@ -75,7 +69,6 @@ public class ImmutableSequence<T> :
|
|||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
HashCode hash = new();
|
HashCode hash = new();
|
||||||
hash.Add(InitialHash);
|
|
||||||
|
|
||||||
foreach (var value in _values)
|
foreach (var value in _values)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user