-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Performance improvements. * Safe fixed blocks. * CString range. * CStringSequence creation function. * CStringSequence as span fixed. * Span/ReadOnlySpan transforms. * CString.Clone allways returns a UTF-8 text with null-termination character. * CStringSequence allways ends with null-termination character.
- Loading branch information
1 parent
0ce5963
commit afd609e
Showing
32 changed files
with
3,080 additions
and
1,492 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
232 changes: 116 additions & 116 deletions
232
src/Rxmxnx.PInvoke.Extensions.Tests/CStringSequenceTest/AsSpanTest.cs
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,125 +1,125 @@ | ||
using System; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
||
using AutoFixture; | ||
|
||
using Xunit; | ||
|
||
namespace Rxmxnx.PInvoke.Extensions.Tests.CStringSequenceTest | ||
{ | ||
[ExcludeFromCodeCoverage] | ||
public sealed class AsSpanTest | ||
{ | ||
[Fact] | ||
internal void NormalTest() | ||
{ | ||
Random random = new Random(); | ||
using System; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Runtime.InteropServices; | ||
using System.Text; | ||
|
||
using AutoFixture; | ||
|
||
using Xunit; | ||
|
||
namespace Rxmxnx.PInvoke.Extensions.Tests.CStringSequenceTest | ||
{ | ||
[ExcludeFromCodeCoverage] | ||
public sealed class AsSpanTest | ||
{ | ||
[Fact] | ||
internal void NormalTest() | ||
{ | ||
Random random = new Random(); | ||
CString[] localValues = TestUtilities.SharedFixture.CreateMany<String>(100).Select(x => | ||
{ | ||
Int32 start = random.Next(0, x.Length); | ||
Int32 end = random.Next(start, x.Length); | ||
return (CString)x[start..end]; | ||
}).ToArray(); | ||
String[] strValue = TestUtilities.SharedFixture.CreateMany<String>(100).Select(x => | ||
}).ToArray(); | ||
String[] strValue = TestUtilities.SharedFixture.CreateMany<String>(100).Select(x => | ||
{ | ||
Int32 start = random.Next(0, x.Length); | ||
Int32 end = random.Next(start, x.Length); | ||
return x[start..end]; | ||
}).ToArray(); | ||
Byte[][] bytes = strValue.Select(x => Encoding.UTF8.GetBytes(x)).ToArray(); | ||
GCHandle[] handles = TestUtilities.Alloc(bytes); | ||
try | ||
{ | ||
ExecuteTest(localValues, TestUtilities.CreateCStrings(bytes, handles)); | ||
} | ||
finally | ||
{ | ||
TestUtilities.Free(handles); | ||
} | ||
} | ||
|
||
private static void ExecuteTest(CString[] localValues, CString[] externalValues) | ||
{ | ||
CString[] allValues = MixValues(localValues, externalValues); | ||
Int32 expectedTextLength = GetExpectedTextLength(allValues); | ||
Int32 expectedStringLength = GetExpectedStringLength(expectedTextLength); | ||
String[] expectedValue = allValues.Select(x => x?.ToString()).ToArray(); | ||
|
||
CStringSequence sequence = new(allValues); | ||
ReadOnlySpan<Char> buffer = sequence.AsSpan(out CString[] output); | ||
|
||
Assert.Equal(expectedStringLength, buffer.Length); | ||
Assert.Equal(allValues.Length, output.Length); | ||
TextTest(allValues, expectedValue, output); | ||
ConcatTest(sequence); | ||
} | ||
|
||
private static void TextTest(CString[] allValues, String[] expectedValue, CString[] output) | ||
{ | ||
for (Int32 i = 0; i < allValues.Length; i++) | ||
{ | ||
Boolean isNullOrEmpty = CString.IsNullOrEmpty(allValues[i]); | ||
|
||
Assert.Equal(isNullOrEmpty, CString.IsNullOrEmpty(output[i])); | ||
Assert.NotNull(output[i]); | ||
Assert.True(output[i].IsNullTerminated); | ||
if (!isNullOrEmpty) | ||
{ | ||
Assert.True(output[i].IsReference); | ||
Assert.Equal(expectedValue[i], output[i].ToString()); | ||
} | ||
else | ||
Assert.False(output[i].IsReference); | ||
} | ||
} | ||
|
||
private static void ConcatTest(CStringSequence sequence) | ||
{ | ||
ReadOnlySpan<Char> buffer = sequence.AsSpan(out CString[] output); | ||
Int32[] lengths = output.Select(x => x.Length).ToArray(); | ||
CString join = output.Concat(); | ||
ReadOnlySpan<Byte> span = join; | ||
CString[] output2 = GetCString(span, lengths); | ||
CStringSequence sequence2 = new(output2); | ||
ReadOnlySpan<Char> buffer2 = sequence2.AsSpan(out CString[] output3); | ||
|
||
Assert.Equal(sequence, sequence2); | ||
for (Int32 i = 0; i < lengths.Length; i++) | ||
{ | ||
Assert.Equal(output[i], output2[i]); | ||
Assert.Equal(output[i], output3[i]); | ||
Assert.True(output[i].IsNullTerminated); | ||
Assert.True(output3[i].IsNullTerminated); | ||
} | ||
} | ||
|
||
private static CString[] GetCString(ReadOnlySpan<Byte> span, Int32[] lengths) | ||
{ | ||
IntPtr ptr = span.AsIntPtr(); | ||
CString[] result = new CString[lengths.Length]; | ||
Int32 offset = 0; | ||
for (Int32 i = 0; i < lengths.Length; i++) | ||
{ | ||
result[i] = new(ptr + offset, lengths[i]); | ||
offset += lengths[i]; | ||
} | ||
return result; | ||
} | ||
|
||
private static CString[] MixValues(CString[] localValues, CString[] externalValues) | ||
=> localValues.Concat(externalValues) | ||
.Concat(Enumerable.Repeat<CString>(default, sizeof(Char))) | ||
.Concat(Enumerable.Repeat<CString>("", sizeof(Char))) | ||
.OrderBy(x => Guid.NewGuid()) | ||
.ToArray(); | ||
|
||
private static Int32 GetExpectedTextLength(CString[] allValues) | ||
=> allValues.Where(x => !CString.IsNullOrEmpty(x)).Select(x => x.Length + 1).Sum() - 1; | ||
|
||
private static Int32 GetExpectedStringLength(Int32 expectedTextLength) | ||
=> expectedTextLength / sizeof(Char) + expectedTextLength % sizeof(Char); | ||
} | ||
} | ||
}).ToArray(); | ||
Byte[][] bytes = strValue.Select(x => Encoding.UTF8.GetBytes(x)).ToArray(); | ||
GCHandle[] handles = TestUtilities.Alloc(bytes); | ||
try | ||
{ | ||
ExecuteTest(localValues, TestUtilities.CreateCStrings(bytes, handles)); | ||
} | ||
finally | ||
{ | ||
TestUtilities.Free(handles); | ||
} | ||
} | ||
|
||
private static void ExecuteTest(CString[] localValues, CString[] externalValues) | ||
{ | ||
CString[] allValues = MixValues(localValues, externalValues); | ||
Int32 expectedTextLength = GetExpectedTextLength(allValues); | ||
Int32 expectedStringLength = GetExpectedStringLength(expectedTextLength); | ||
String[] expectedValue = allValues.Select(x => x?.ToString()).ToArray(); | ||
|
||
CStringSequence sequence = new(allValues); | ||
ReadOnlySpan<Char> buffer = sequence.AsSpan(out CString[] output); | ||
|
||
Assert.Equal(expectedStringLength, buffer.Length); | ||
Assert.Equal(allValues.Length, output.Length); | ||
TextTest(allValues, expectedValue, output); | ||
ConcatTest(sequence); | ||
} | ||
|
||
private static void TextTest(CString[] allValues, String[] expectedValue, CString[] output) | ||
{ | ||
for (Int32 i = 0; i < allValues.Length; i++) | ||
{ | ||
Boolean isNullOrEmpty = CString.IsNullOrEmpty(allValues[i]); | ||
|
||
Assert.Equal(isNullOrEmpty, CString.IsNullOrEmpty(output[i])); | ||
Assert.NotNull(output[i]); | ||
Assert.True(output[i].IsNullTerminated); | ||
if (!isNullOrEmpty) | ||
{ | ||
Assert.True(output[i].IsReference); | ||
Assert.Equal(expectedValue[i], output[i].ToString()); | ||
} | ||
else | ||
Assert.False(output[i].IsReference); | ||
} | ||
} | ||
|
||
private static void ConcatTest(CStringSequence sequence) | ||
{ | ||
ReadOnlySpan<Char> buffer = sequence.AsSpan(out CString[] output); | ||
Int32[] lengths = output.Select(x => x.Length).ToArray(); | ||
CString join = output.Concat(); | ||
ReadOnlySpan<Byte> span = join; | ||
CString[] output2 = GetCString(span, lengths); | ||
CStringSequence sequence2 = new(output2); | ||
ReadOnlySpan<Char> buffer2 = sequence2.AsSpan(out CString[] output3); | ||
|
||
Assert.Equal(sequence, sequence2); | ||
for (Int32 i = 0; i < lengths.Length; i++) | ||
{ | ||
Assert.Equal(output[i], output2[i]); | ||
Assert.Equal(output[i], output3[i]); | ||
Assert.True(output[i].IsNullTerminated); | ||
Assert.True(output3[i].IsNullTerminated); | ||
} | ||
} | ||
|
||
private static CString[] GetCString(ReadOnlySpan<Byte> span, Int32[] lengths) | ||
{ | ||
IntPtr ptr = span.AsIntPtr(); | ||
CString[] result = new CString[lengths.Length]; | ||
Int32 offset = 0; | ||
for (Int32 i = 0; i < lengths.Length; i++) | ||
{ | ||
result[i] = new(ptr + offset, lengths[i]); | ||
offset += lengths[i]; | ||
} | ||
return result; | ||
} | ||
|
||
private static CString[] MixValues(CString[] localValues, CString[] externalValues) | ||
=> localValues.Concat(externalValues) | ||
.Concat(Enumerable.Repeat<CString>(default, sizeof(Char))) | ||
.Concat(Enumerable.Repeat<CString>("", sizeof(Char))) | ||
.OrderBy(x => Guid.NewGuid()) | ||
.ToArray(); | ||
|
||
private static Int32 GetExpectedTextLength(CString[] allValues) | ||
=> allValues.Where(x => !CString.IsNullOrEmpty(x)).Select(x => x.Length + 1).Sum(); | ||
|
||
private static Int32 GetExpectedStringLength(Int32 expectedTextLength) | ||
=> (expectedTextLength / sizeof(Char)) + (expectedTextLength % sizeof(Char)); | ||
} | ||
} |
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
Oops, something went wrong.