-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix append string methods with invalid encoding implementations
- Loading branch information
Showing
6 changed files
with
212 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
namespace Mikodev.Binary.Tests.Contexts; | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using Xunit; | ||
|
||
public class AllocatorStringTests | ||
{ | ||
private delegate int GetByteCountDelegate(ReadOnlySpan<char> chars); | ||
|
||
private delegate int GetBytesDelegate(ReadOnlySpan<char> chars, Span<byte> bytes); | ||
|
||
private class FakeEncoding : Encoding | ||
{ | ||
public required Func<int, int> GetMaxByteCountCallback { get; init; } | ||
|
||
public required GetByteCountDelegate GetByteCountCallback { get; init; } | ||
|
||
public required GetBytesDelegate GetBytesCallback { get; init; } | ||
|
||
public override int GetByteCount(char[] chars, int index, int count) => throw new NotSupportedException(); | ||
|
||
public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) => throw new NotSupportedException(); | ||
|
||
public override int GetCharCount(byte[] bytes, int index, int count) => throw new NotSupportedException(); | ||
|
||
public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) => throw new NotSupportedException(); | ||
|
||
public override int GetMaxCharCount(int byteCount) => throw new NotSupportedException(); | ||
|
||
public override int GetMaxByteCount(int charCount) => GetMaxByteCountCallback.Invoke(charCount); | ||
|
||
public override int GetByteCount(ReadOnlySpan<char> chars) => GetByteCountCallback.Invoke(chars); | ||
|
||
public override int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes) => GetBytesCallback.Invoke(chars, bytes); | ||
} | ||
|
||
public static IEnumerable<object[]> StringData() | ||
{ | ||
yield return new object[] { string.Empty }; | ||
yield return new object[] { "Alpha" }; | ||
yield return new object[] { "一二三四" }; | ||
} | ||
|
||
[Theory(DisplayName = "Append String UTF8 Encoding")] | ||
[MemberData(nameof(StringData))] | ||
public void AppendStringUTF8Encoding(string text) | ||
{ | ||
var encoding = Encoding.UTF8; | ||
var allocator = new Allocator(); | ||
Assert.Equal(0, allocator.Length); | ||
Assert.Equal(0, allocator.Capacity); | ||
Allocator.Append(ref allocator, text, encoding); | ||
var expected = encoding.GetBytes(text); | ||
Assert.Equal(expected, allocator.ToArray()); | ||
} | ||
|
||
[Theory(DisplayName = "Append String With Length Prefix UTF8 Encoding")] | ||
[MemberData(nameof(StringData))] | ||
public void AppendStringWithLengthPrefixUTF8Encoding(string text) | ||
{ | ||
var encoding = Encoding.UTF8; | ||
var allocator = new Allocator(); | ||
Assert.Equal(0, allocator.Length); | ||
Assert.Equal(0, allocator.Capacity); | ||
Allocator.AppendWithLengthPrefix(ref allocator, text, encoding); | ||
var expected = encoding.GetBytes(text); | ||
var buffer = allocator.AsSpan(); | ||
var result = Converter.DecodeWithLengthPrefix(ref buffer); | ||
Assert.Equal(0, buffer.Length); | ||
Assert.Equal(expected, result.ToArray()); | ||
} | ||
|
||
[Theory(DisplayName = "Append String UTF8 Encoding Medium Length Test")] | ||
[InlineData(72, 96, 96)] | ||
[InlineData(72, 0, 256)] | ||
[InlineData(72, 1024, 1024)] | ||
public void AppendStringUTF8EncodingMediumLengthTest(int stringLength, int allocatorInitialCapacity, int allocatorFinalCapacity) | ||
{ | ||
var encoding = Encoding.UTF8; | ||
var text = new string('a', stringLength); | ||
var allocator = new Allocator(new Span<byte>(new byte[allocatorInitialCapacity])); | ||
Assert.True(encoding.GetByteCount(text) < 128); | ||
Assert.True(encoding.GetMaxByteCount(text.Length) > 128); | ||
Allocator.Append(ref allocator, text, encoding); | ||
Assert.Equal(stringLength, allocator.Length); | ||
Assert.Equal(allocatorFinalCapacity, allocator.Capacity); | ||
} | ||
|
||
[Theory(DisplayName = "Append String With Length Prefix UTF8 Encoding Medium Length Test")] | ||
[InlineData(48, 80, 80, 1)] | ||
[InlineData(48, 0, 256, 1)] | ||
[InlineData(48, 1024, 1024, 4)] | ||
public void AppendStringWithLengthPrefixUTF8EncodingMediumLengthTest(int stringLength, int allocatorInitialCapacity, int allocatorFinalCapacity, int prefixLength) | ||
{ | ||
var encoding = Encoding.UTF8; | ||
var text = new string('a', stringLength); | ||
var allocator = new Allocator(new Span<byte>(new byte[allocatorInitialCapacity])); | ||
Assert.True(encoding.GetByteCount(text) < 128); | ||
Assert.True(encoding.GetMaxByteCount(text.Length) > 128); | ||
Allocator.AppendWithLengthPrefix(ref allocator, text, encoding); | ||
var buffer = allocator.AsSpan(); | ||
var actualIntentLength = Converter.Decode(buffer, out var actualPrefixLength); | ||
Assert.Equal(stringLength, actualIntentLength); | ||
Assert.Equal(prefixLength, actualPrefixLength); | ||
Assert.Equal(stringLength + prefixLength, allocator.Length); | ||
Assert.Equal(allocatorFinalCapacity, allocator.Capacity); | ||
} | ||
|
||
[Theory(DisplayName = "Append String Fake Encoding Invalid 'GetBytes()' Return Test")] | ||
[InlineData(1024, 256, -1, -1)] | ||
[InlineData(1024, 256, -1, 257)] | ||
[InlineData(256, 256, -1, -1)] | ||
[InlineData(256, 256, -1, 257)] | ||
[InlineData(0, 128, 0, 1)] | ||
[InlineData(0, 128, 0, -1)] | ||
public void AppendStringFakeEncodingInvalidGetBytesReturnTest(int allocatorInitialCapacity, int getMaxByteCountReturn, int getByteCountReturn, int getBytesReturn) | ||
{ | ||
var encoding = new FakeEncoding | ||
{ | ||
GetMaxByteCountCallback = _ => getMaxByteCountReturn, | ||
GetByteCountCallback = getByteCountReturn is -1 ? (_ => throw new NotSupportedException()) : (_ => getByteCountReturn), | ||
GetBytesCallback = (_, _) => getBytesReturn, | ||
}; | ||
var error = Assert.Throws<InvalidOperationException>(() => | ||
{ | ||
var allocator = new Allocator(new Span<byte>(new byte[allocatorInitialCapacity])); | ||
Assert.Equal(0, allocator.Length); | ||
Assert.Equal(allocatorInitialCapacity, allocator.Capacity); | ||
Allocator.Append(ref allocator, string.Empty, encoding); | ||
}); | ||
Assert.Equal("Invalid return value.", error.Message); | ||
} | ||
|
||
[Theory(DisplayName = "Append String With Length Prefix Fake Encoding Invalid 'GetBytes()' Return Test")] | ||
[InlineData(196, 192, -1, -1)] | ||
[InlineData(196, 192, -1, 193)] | ||
[InlineData(192, 192, 192, -1)] | ||
[InlineData(192, 192, 192, 193)] | ||
[InlineData(0, 1, 0, 1)] | ||
[InlineData(0, 1, 0, -1)] | ||
public void AppendStringWithLengthPrefixInvalidGetBytesReturnTest(int allocatorInitialCapacity, int getMaxByteCountReturn, int getByteCountReturn, int getBytesReturn) | ||
{ | ||
var encoding = new FakeEncoding | ||
{ | ||
GetMaxByteCountCallback = _ => getMaxByteCountReturn, | ||
GetByteCountCallback = getByteCountReturn is -1 ? (_ => throw new NotSupportedException()) : (_ => getByteCountReturn), | ||
GetBytesCallback = (_, _) => getBytesReturn, | ||
}; | ||
var error = Assert.Throws<InvalidOperationException>(() => | ||
{ | ||
var allocator = new Allocator(new Span<byte>(new byte[allocatorInitialCapacity])); | ||
Assert.Equal(0, allocator.Length); | ||
Assert.Equal(allocatorInitialCapacity, allocator.Capacity); | ||
Allocator.AppendWithLengthPrefix(ref allocator, string.Empty, encoding); | ||
}); | ||
Assert.Equal("Invalid return value.", error.Message); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,19 @@ | ||
namespace Mikodev.Binary.Creators.Isolated.Primitive; | ||
|
||
using Mikodev.Binary.Internal; | ||
using System; | ||
using System.Text; | ||
|
||
internal sealed class StringConverter : Converter<string> | ||
{ | ||
public override void Encode(ref Allocator allocator, string? item) => Allocator.Append(ref allocator, item.AsSpan(), SharedModule.Encoding); | ||
public override void Encode(ref Allocator allocator, string? item) => Allocator.Append(ref allocator, item.AsSpan(), Encoding.UTF8); | ||
|
||
public override void EncodeAuto(ref Allocator allocator, string? item) => Allocator.AppendWithLengthPrefix(ref allocator, item.AsSpan(), SharedModule.Encoding); | ||
public override void EncodeAuto(ref Allocator allocator, string? item) => Allocator.AppendWithLengthPrefix(ref allocator, item.AsSpan(), Encoding.UTF8); | ||
|
||
public override void EncodeWithLengthPrefix(ref Allocator allocator, string? item) => Allocator.AppendWithLengthPrefix(ref allocator, item.AsSpan(), SharedModule.Encoding); | ||
public override void EncodeWithLengthPrefix(ref Allocator allocator, string? item) => Allocator.AppendWithLengthPrefix(ref allocator, item.AsSpan(), Encoding.UTF8); | ||
|
||
public override string Decode(in ReadOnlySpan<byte> span) => SharedModule.Encoding.GetString(span); | ||
public override string Decode(in ReadOnlySpan<byte> span) => Encoding.UTF8.GetString(span); | ||
|
||
public override string DecodeAuto(ref ReadOnlySpan<byte> span) => SharedModule.Encoding.GetString(Converter.DecodeWithLengthPrefix(ref span)); | ||
public override string DecodeAuto(ref ReadOnlySpan<byte> span) => Encoding.UTF8.GetString(Converter.DecodeWithLengthPrefix(ref span)); | ||
|
||
public override string DecodeWithLengthPrefix(ref ReadOnlySpan<byte> span) => SharedModule.Encoding.GetString(Converter.DecodeWithLengthPrefix(ref span)); | ||
public override string DecodeWithLengthPrefix(ref ReadOnlySpan<byte> span) => Encoding.UTF8.GetString(Converter.DecodeWithLengthPrefix(ref span)); | ||
} |
This file was deleted.
Oops, something went wrong.