mirror of
https://github.com/oliverbooth/X10D
synced 2024-11-09 22:55:42 +00:00
Compare commits
11 Commits
74f957f0c2
...
964d105f92
Author | SHA1 | Date | |
---|---|---|---|
964d105f92 | |||
ff6b8d5465 | |||
56c3f117c7 | |||
5220f386aa | |||
dfa0bd4e11 | |||
20f81c5c97 | |||
0d02c88ef6 | |||
e115a8fa0e | |||
2fb91d5175 | |||
43d2e8feea | |||
2445e36cfd |
5
.github/workflows/sonarcloud.yml
vendored
5
.github/workflows/sonarcloud.yml
vendored
@ -44,6 +44,11 @@ jobs:
|
|||||||
New-Item -Path .\.sonar\scanner -ItemType Directory
|
New-Item -Path .\.sonar\scanner -ItemType Directory
|
||||||
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
|
dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner
|
||||||
|
|
||||||
|
- name: Install dotCover
|
||||||
|
shell: powershell
|
||||||
|
run: |
|
||||||
|
dotnet tool install --global JetBrains.dotCover.GlobalTool
|
||||||
|
|
||||||
- name: Build and analyze
|
- name: Build and analyze
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
|
||||||
|
@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- X10D: Added `string.ConcatIf`.
|
- X10D: Added `string.ConcatIf`.
|
||||||
- X10D: Added `string.MDBold`, `string.MDCode`, `string.MDCodeBlock([string])`, `string.MDHeading(int)`,
|
- X10D: Added `string.MDBold`, `string.MDCode`, `string.MDCodeBlock([string])`, `string.MDHeading(int)`,
|
||||||
`string.MDItalic`, `string.MDLink`, `string.MDStrikeOut`, and `string.MDUnderline` for Markdown formatting.
|
`string.MDItalic`, `string.MDLink`, `string.MDStrikeOut`, and `string.MDUnderline` for Markdown formatting.
|
||||||
|
- X10D: Added Span overloads which complement `char.Repeat` and `string.Repeat`.
|
||||||
- X10D.Unity: Added `RaycastHit.GetComponent` and `RaycastHit.TryGetComponent`.
|
- X10D.Unity: Added `RaycastHit.GetComponent` and `RaycastHit.TryGetComponent`.
|
||||||
- X10D.Unity: Added `DebugUtility.DrawFunction`, and `DebugUtility.DrawUnjoinedPolyhedron` on which it relies.
|
- X10D.Unity: Added `DebugUtility.DrawFunction`, and `DebugUtility.DrawUnjoinedPolyhedron` on which it relies.
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ BigEndian/LittleEndian methods.
|
|||||||
- X10D: `Stream.GetHash<>` and `Stream.TryWriteHash<>` now throw ArgumentException in lieu of
|
- X10D: `Stream.GetHash<>` and `Stream.TryWriteHash<>` now throw ArgumentException in lieu of
|
||||||
TypeInitializationException.
|
TypeInitializationException.
|
||||||
- X10D: `char.IsEmoji` no longer allocates for .NET 7.
|
- X10D: `char.IsEmoji` no longer allocates for .NET 7.
|
||||||
|
- X10D: `string.Repeat` is now more efficient.
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
<PackageIconUrl/>
|
<PackageIconUrl/>
|
||||||
<PackageTags>dotnet extension-methods</PackageTags>
|
<PackageTags>dotnet extension-methods</PackageTags>
|
||||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(SolutionDir)/CHANGELOG.md"))</PackageReleaseNotes>
|
<PackageReleaseNotes>See CHANGELOG.md for a full list of changes.</PackageReleaseNotes>
|
||||||
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
@ -60,4 +60,60 @@ internal class CharSpanTests
|
|||||||
Assert.That(string.Empty.AsSpan().CountSubstring('\0'), Is.Zero);
|
Assert.That(string.Empty.AsSpan().CountSubstring('\0'), Is.Zero);
|
||||||
Assert.That(string.Empty.AsSpan().CountSubstring(string.Empty.AsSpan(), StringComparison.OrdinalIgnoreCase), Is.Zero);
|
Assert.That(string.Empty.AsSpan().CountSubstring(string.Empty.AsSpan(), StringComparison.OrdinalIgnoreCase), Is.Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldNotManipulateSpan_GivenCount0()
|
||||||
|
{
|
||||||
|
Span<char> destination = new char[11];
|
||||||
|
"Hello world".AsSpan().CopyTo(destination);
|
||||||
|
|
||||||
|
"a".AsSpan().Repeat(0, destination);
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo("Hello world"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldReturnItself_GivenCount1()
|
||||||
|
{
|
||||||
|
string repeated = "a".AsSpan().Repeat(1);
|
||||||
|
Assert.That(repeated, Has.Length.EqualTo(1));
|
||||||
|
Assert.That(repeated, Is.EqualTo("a"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldPopulateSpan_GivenValidSpan()
|
||||||
|
{
|
||||||
|
const string expected = "aaaaaaaaaa";
|
||||||
|
Span<char> destination = new char[10];
|
||||||
|
"a".AsSpan().Repeat(10, destination);
|
||||||
|
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldReturnEmptyString_GivenCount0()
|
||||||
|
{
|
||||||
|
Assert.That("a".AsSpan().Repeat(0), Is.EqualTo(string.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldReturnRepeatedString_GivenSpan()
|
||||||
|
{
|
||||||
|
const string expected = "aaaaaaaaaa";
|
||||||
|
string actual = "a".AsSpan().Repeat(10);
|
||||||
|
|
||||||
|
Assert.That(actual, Is.EqualTo(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldThrowArgumentException_GivenSmallSpan()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() => "a".AsSpan().Repeat(10, Span<char>.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldThrowArgumentOutOfRangeException_GivenNegativeCount()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = "a".AsSpan().Repeat(-1));
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => "a".AsSpan().Repeat(-1, Span<char>.Empty));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,25 @@ internal class CharTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldPopulateSpanWithRepeatedCharacter_GivenValidCount()
|
||||||
|
{
|
||||||
|
const string expected = "aaaaaaaaaa";
|
||||||
|
Span<char> destination = new char[10];
|
||||||
|
'a'.Repeat(10, destination);
|
||||||
|
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldOnlyWriteOneCharToSpan_GivenCount1()
|
||||||
|
{
|
||||||
|
Span<char> destination = new char[10];
|
||||||
|
'a'.Repeat(1, destination);
|
||||||
|
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo("a\0\0\0\0\0\0\0\0\0"));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Repeat_ShouldReturnRepeatedCharacter_GivenValidCount()
|
public void Repeat_ShouldReturnRepeatedCharacter_GivenValidCount()
|
||||||
{
|
{
|
||||||
@ -51,9 +70,34 @@ internal class CharTests
|
|||||||
Assert.That('a'.Repeat(0), Is.EqualTo(string.Empty));
|
Assert.That('a'.Repeat(0), Is.EqualTo(string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldNotManipulateSpan_GivenCount0()
|
||||||
|
{
|
||||||
|
Span<char> destination = new char[10];
|
||||||
|
destination.Fill(' ');
|
||||||
|
'a'.Repeat(0, destination);
|
||||||
|
|
||||||
|
const string expected = " ";
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo(expected));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Repeat_ShouldThrowArgumentOutOfRangeException_GivenNegativeCount()
|
public void Repeat_ShouldThrowArgumentOutOfRangeException_GivenNegativeCount()
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() => _ = 'a'.Repeat(-1));
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = 'a'.Repeat(-1));
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => 'a'.Repeat(-1, Span<char>.Empty));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldThrowArgumentException_GivenSmallSpan()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentException>(() =>
|
||||||
|
{
|
||||||
|
var destination = Span<char>.Empty;
|
||||||
|
'a'.Repeat(1, destination);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,46 @@ internal class MarkdownTests
|
|||||||
Assert.That("Hello, world!".MDItalic(true), Is.EqualTo("_Hello, world!_"));
|
Assert.That("Hello, world!".MDItalic(true), Is.EqualTo("_Hello, world!_"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void MDLink_ShouldThrowArgumentNullException_GivenNullUrl()
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentNullException>(() => "".MDLink((string)null!));
|
||||||
|
Assert.Throws<ArgumentNullException>(() => "".MDLink((Uri)null!));
|
||||||
|
Assert.Throws<ArgumentNullException>(() => ((Uri)null!).MDLink("Hello, world!"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void MDLink_ShouldReturnUrlOnly_GivenNullOrEmptyLabel()
|
||||||
|
{
|
||||||
|
const string url = "https://example.com/";
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(((string)null!).MDLink(url), Is.EqualTo(url));
|
||||||
|
Assert.That(string.Empty.MDLink(url), Is.EqualTo(url));
|
||||||
|
|
||||||
|
Assert.That(new Uri(url).MDLink(null), Is.EqualTo(url));
|
||||||
|
Assert.That(new Uri(url).MDLink(string.Empty), Is.EqualTo(url));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void MDLink_ShouldReturnFormattedLink_GivenValidLabelAndUrl()
|
||||||
|
{
|
||||||
|
const string url = "https://example.com/";
|
||||||
|
const string label = "Hello, world!";
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(label.MDLink(url), Is.EqualTo($"[{label}]({url})"));
|
||||||
|
Assert.That(label.MDLink(new Uri(url)), Is.EqualTo($"[{label}]({url})"));
|
||||||
|
|
||||||
|
Assert.That(new Uri(url).MDLink(label), Is.EqualTo($"[{label}]({url})"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void MDStrikeOut_ShouldThrowArgumentNullException_GivenNull()
|
public void MDStrikeOut_ShouldThrowArgumentNullException_GivenNull()
|
||||||
{
|
{
|
||||||
|
@ -164,7 +164,9 @@ internal class StringTests
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That("Hello World".CountSubstring('E'), Is.Zero);
|
Assert.That("Hello World".CountSubstring('E'), Is.Zero);
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That("Hello World".CountSubstring("E"), Is.Zero);
|
Assert.That("Hello World".CountSubstring("E"), Is.Zero);
|
||||||
|
#pragma warning restore CA1307
|
||||||
Assert.That("Hello World".CountSubstring("E", StringComparison.OrdinalIgnoreCase), Is.EqualTo(1));
|
Assert.That("Hello World".CountSubstring("E", StringComparison.OrdinalIgnoreCase), Is.EqualTo(1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -175,7 +177,9 @@ internal class StringTests
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That("Hello World".CountSubstring('z'), Is.Zero);
|
Assert.That("Hello World".CountSubstring('z'), Is.Zero);
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That("Hello World".CountSubstring("z"), Is.Zero);
|
Assert.That("Hello World".CountSubstring("z"), Is.Zero);
|
||||||
|
#pragma warning restore CA1307
|
||||||
Assert.That("Hello World".CountSubstring("z", StringComparison.OrdinalIgnoreCase), Is.Zero);
|
Assert.That("Hello World".CountSubstring("z", StringComparison.OrdinalIgnoreCase), Is.Zero);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -186,7 +190,9 @@ internal class StringTests
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That("Hello World".CountSubstring('e'), Is.EqualTo(1));
|
Assert.That("Hello World".CountSubstring('e'), Is.EqualTo(1));
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That("Hello World".CountSubstring("e"), Is.EqualTo(1));
|
Assert.That("Hello World".CountSubstring("e"), Is.EqualTo(1));
|
||||||
|
#pragma warning restore CA1307
|
||||||
Assert.That("Hello World".CountSubstring("e", StringComparison.OrdinalIgnoreCase), Is.EqualTo(1));
|
Assert.That("Hello World".CountSubstring("e", StringComparison.OrdinalIgnoreCase), Is.EqualTo(1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -197,7 +203,9 @@ internal class StringTests
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(string.Empty.CountSubstring('\0'), Is.Zero);
|
Assert.That(string.Empty.CountSubstring('\0'), Is.Zero);
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(string.Empty.CountSubstring(string.Empty), Is.Zero);
|
Assert.That(string.Empty.CountSubstring(string.Empty), Is.Zero);
|
||||||
|
#pragma warning restore CA1307
|
||||||
Assert.That(string.Empty.CountSubstring(string.Empty, StringComparison.OrdinalIgnoreCase), Is.Zero);
|
Assert.That(string.Empty.CountSubstring(string.Empty, StringComparison.OrdinalIgnoreCase), Is.Zero);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -207,7 +215,9 @@ internal class StringTests
|
|||||||
{
|
{
|
||||||
string value = null!;
|
string value = null!;
|
||||||
Assert.Throws<ArgumentNullException>(() => value.CountSubstring('\0'));
|
Assert.Throws<ArgumentNullException>(() => value.CountSubstring('\0'));
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.Throws<ArgumentNullException>(() => value.CountSubstring(string.Empty));
|
Assert.Throws<ArgumentNullException>(() => value.CountSubstring(string.Empty));
|
||||||
|
#pragma warning restore CA1307
|
||||||
Assert.Throws<ArgumentNullException>(() => value.CountSubstring(string.Empty, StringComparison.OrdinalIgnoreCase));
|
Assert.Throws<ArgumentNullException>(() => value.CountSubstring(string.Empty, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +227,10 @@ internal class StringTests
|
|||||||
const string value = "Hello Worl";
|
const string value = "Hello Worl";
|
||||||
const char substring = 'd';
|
const char substring = 'd';
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(value.EnsureEndsWith(substring), Is.EqualTo("Hello World"));
|
Assert.That(value.EnsureEndsWith(substring), Is.EqualTo("Hello World"));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(value.EnsureEndsWith(substring, StringComparison.Ordinal), Is.EqualTo("Hello World"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -226,7 +239,10 @@ internal class StringTests
|
|||||||
const string value = "A";
|
const string value = "A";
|
||||||
const char substring = 'A';
|
const char substring = 'A';
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(value.EnsureEndsWith(substring), Is.EqualTo(value));
|
Assert.That(value.EnsureEndsWith(substring), Is.EqualTo(value));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(value.EnsureEndsWith(substring, StringComparison.Ordinal), Is.EqualTo(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -235,7 +251,10 @@ internal class StringTests
|
|||||||
const string value = "B";
|
const string value = "B";
|
||||||
const char substring = 'A';
|
const char substring = 'A';
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(value.EnsureStartsWith(substring), Is.EqualTo("AB"));
|
Assert.That(value.EnsureStartsWith(substring), Is.EqualTo("AB"));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(value.EnsureStartsWith(substring, StringComparison.Ordinal), Is.EqualTo("AB"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -244,7 +263,10 @@ internal class StringTests
|
|||||||
const string value = "A";
|
const string value = "A";
|
||||||
const char substring = 'A';
|
const char substring = 'A';
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(value.EnsureStartsWith(substring), Is.EqualTo(value));
|
Assert.That(value.EnsureStartsWith(substring), Is.EqualTo(value));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(value.EnsureStartsWith(substring, StringComparison.Ordinal), Is.EqualTo(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -253,7 +275,10 @@ internal class StringTests
|
|||||||
const string value = "Hello ";
|
const string value = "Hello ";
|
||||||
const string substring = "World";
|
const string substring = "World";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(value.EnsureEndsWith(substring), Is.EqualTo("Hello World"));
|
Assert.That(value.EnsureEndsWith(substring), Is.EqualTo("Hello World"));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(value.EnsureEndsWith(substring, StringComparison.Ordinal), Is.EqualTo("Hello World"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -261,7 +286,10 @@ internal class StringTests
|
|||||||
{
|
{
|
||||||
const string substring = "World";
|
const string substring = "World";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(substring.EnsureEndsWith(substring), Is.EqualTo(substring));
|
Assert.That(substring.EnsureEndsWith(substring), Is.EqualTo(substring));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(substring.EnsureEndsWith(substring, StringComparison.Ordinal), Is.EqualTo(substring));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -269,7 +297,10 @@ internal class StringTests
|
|||||||
{
|
{
|
||||||
const string substring = "World";
|
const string substring = "World";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(string.Empty.EnsureEndsWith(substring), Is.EqualTo(substring));
|
Assert.That(string.Empty.EnsureEndsWith(substring), Is.EqualTo(substring));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(string.Empty.EnsureEndsWith(substring, StringComparison.Ordinal), Is.EqualTo(substring));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -277,7 +308,10 @@ internal class StringTests
|
|||||||
{
|
{
|
||||||
const string substring = "World";
|
const string substring = "World";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(substring.EnsureEndsWith(string.Empty), Is.EqualTo(substring));
|
Assert.That(substring.EnsureEndsWith(string.Empty), Is.EqualTo(substring));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(substring.EnsureEndsWith(string.Empty, StringComparison.Ordinal), Is.EqualTo(substring));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -286,7 +320,10 @@ internal class StringTests
|
|||||||
const string value = "World";
|
const string value = "World";
|
||||||
const string substring = "Hello ";
|
const string substring = "Hello ";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(value.EnsureStartsWith(substring), Is.EqualTo("Hello World"));
|
Assert.That(value.EnsureStartsWith(substring), Is.EqualTo("Hello World"));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(value.EnsureStartsWith(substring, StringComparison.Ordinal), Is.EqualTo("Hello World"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -294,7 +331,10 @@ internal class StringTests
|
|||||||
{
|
{
|
||||||
const string substring = "World";
|
const string substring = "World";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(substring.EnsureStartsWith(substring), Is.EqualTo(substring));
|
Assert.That(substring.EnsureStartsWith(substring), Is.EqualTo(substring));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(substring.EnsureStartsWith(substring, StringComparison.Ordinal), Is.EqualTo(substring));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -302,7 +342,10 @@ internal class StringTests
|
|||||||
{
|
{
|
||||||
const string substring = "World";
|
const string substring = "World";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(string.Empty.EnsureStartsWith(substring), Is.EqualTo(substring));
|
Assert.That(string.Empty.EnsureStartsWith(substring), Is.EqualTo(substring));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(string.Empty.EnsureStartsWith(substring, StringComparison.Ordinal), Is.EqualTo(substring));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -310,7 +353,10 @@ internal class StringTests
|
|||||||
{
|
{
|
||||||
const string substring = "World";
|
const string substring = "World";
|
||||||
|
|
||||||
|
#pragma warning disable CA1307
|
||||||
Assert.That(substring.EnsureStartsWith(string.Empty), Is.EqualTo(substring));
|
Assert.That(substring.EnsureStartsWith(string.Empty), Is.EqualTo(substring));
|
||||||
|
#pragma warning restore CA1307
|
||||||
|
Assert.That(substring.EnsureStartsWith(string.Empty, StringComparison.Ordinal), Is.EqualTo(substring));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -393,7 +439,7 @@ internal class StringTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void GetBytes_ShouldReturnUtf8Bytes_GivenHelloWorld()
|
public void GetBytes_ShouldReturnUtf8Bytes_GivenHelloWorld()
|
||||||
{
|
{
|
||||||
var expected = new byte[] {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64};
|
var expected = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64 };
|
||||||
byte[] actual = "Hello World".GetBytes();
|
byte[] actual = "Hello World".GetBytes();
|
||||||
|
|
||||||
CollectionAssert.AreEqual(expected, actual);
|
CollectionAssert.AreEqual(expected, actual);
|
||||||
@ -402,7 +448,7 @@ internal class StringTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void GetBytes_ShouldReturnAsciiBytes_GivenHelloWorld()
|
public void GetBytes_ShouldReturnAsciiBytes_GivenHelloWorld()
|
||||||
{
|
{
|
||||||
var expected = new byte[] {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64};
|
var expected = new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64 };
|
||||||
byte[] actual = "Hello World".GetBytes(Encoding.ASCII);
|
byte[] actual = "Hello World".GetBytes(Encoding.ASCII);
|
||||||
|
|
||||||
CollectionAssert.AreEqual(expected, actual);
|
CollectionAssert.AreEqual(expected, actual);
|
||||||
@ -444,14 +490,16 @@ internal class StringTests
|
|||||||
public void IsEmoji_ShouldReturnTrue_GivenMultiByteEmoji()
|
public void IsEmoji_ShouldReturnTrue_GivenMultiByteEmoji()
|
||||||
{
|
{
|
||||||
string[] regionalIndicatorCodes = Enumerable.Range(0, 26)
|
string[] regionalIndicatorCodes = Enumerable.Range(0, 26)
|
||||||
.Select(i => Encoding.Unicode.GetString(new byte[] {0x3C, 0xD8, (byte)(0xE6 + i), 0xDD}))
|
.Select(i => Encoding.Unicode.GetString(new byte[] { 0x3C, 0xD8, (byte)(0xE6 + i), 0xDD }))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
for (var i = 0; i < 26; i++)
|
for (var i = 0; i < 26; i++)
|
||||||
for (var j = 0; j < 26; j++)
|
|
||||||
{
|
{
|
||||||
string flag = (regionalIndicatorCodes[i] + regionalIndicatorCodes[j]);
|
for (var j = 0; j < 26; j++)
|
||||||
Assert.That(flag.IsEmoji());
|
{
|
||||||
|
string flag = (regionalIndicatorCodes[i] + regionalIndicatorCodes[j]);
|
||||||
|
Assert.That(flag.IsEmoji());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,7 +744,7 @@ internal class StringTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void Randomize_ShouldThrow_GivenNegativeLength()
|
public void Randomize_ShouldThrow_GivenNegativeLength()
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() => string.Empty.Randomize(-1));
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = string.Empty.Randomize(-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -714,25 +762,62 @@ internal class StringTests
|
|||||||
Assert.That("a".Repeat(0), Is.EqualTo(string.Empty));
|
Assert.That("a".Repeat(0), Is.EqualTo(string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldNotManipulateSpan_GivenCount0()
|
||||||
|
{
|
||||||
|
Span<char> destination = new char[11];
|
||||||
|
"Hello world".AsSpan().CopyTo(destination);
|
||||||
|
|
||||||
|
"a".Repeat(0, destination);
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo("Hello world"));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Repeat_ShouldReturnItself_GivenCount1()
|
public void Repeat_ShouldReturnItself_GivenCount1()
|
||||||
{
|
{
|
||||||
string repeated = "a".Repeat(1);
|
string repeated = "a".Repeat(1);
|
||||||
Assert.That(repeated.Length, Is.EqualTo(1));
|
Assert.That(repeated, Has.Length.EqualTo(1));
|
||||||
Assert.That(repeated, Is.EqualTo("a"));
|
Assert.That(repeated, Is.EqualTo("a"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Repeat_ShouldThrow_GivenNegativeCount()
|
public void Repeat_ShouldThrowArgumentException_GivenSmallSpan()
|
||||||
{
|
{
|
||||||
Assert.Throws<ArgumentOutOfRangeException>(() => _ = "a".Repeat(-1));
|
Assert.Throws<ArgumentException>(() => "a".Repeat(10, Span<char>.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Repeat_ShouldThrow_GivenNull()
|
public void Repeat_ShouldThrowArgumentOutOfRangeException_GivenNegativeCount()
|
||||||
|
{
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => _ = "a".Repeat(-1));
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => "a".Repeat(-1, Span<char>.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldThrowArgumentNullException_GivenNull()
|
||||||
{
|
{
|
||||||
string value = null!;
|
string value = null!;
|
||||||
Assert.Throws<ArgumentNullException>(() => _ = value.Repeat(0));
|
Assert.Throws<ArgumentNullException>(() => _ = value.Repeat(0));
|
||||||
|
Assert.Throws<ArgumentNullException>(() => value.Repeat(0, Span<char>.Empty));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldPopulateSpanWithRepeatedCharacter_GivenValidCount()
|
||||||
|
{
|
||||||
|
const string expected = "aaaaaaaaaa";
|
||||||
|
Span<char> destination = new char[10];
|
||||||
|
"a".Repeat(10, destination);
|
||||||
|
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo(expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Repeat_ShouldOnlyWriteOneCharToSpan_GivenCount1()
|
||||||
|
{
|
||||||
|
Span<char> destination = new char[10];
|
||||||
|
"a".Repeat(1, destination);
|
||||||
|
|
||||||
|
Assert.That(destination.ToString(), Is.EqualTo("a\0\0\0\0\0\0\0\0\0"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -751,7 +836,7 @@ internal class StringTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Reverse_ShouldThrow_GivenNull()
|
public void Reverse_ShouldThrowArgumentNullException_GivenNull()
|
||||||
{
|
{
|
||||||
string value = null!;
|
string value = null!;
|
||||||
Assert.Throws<ArgumentNullException>(() => _ = value.Reverse());
|
Assert.Throws<ArgumentNullException>(() => _ = value.Reverse());
|
||||||
@ -769,7 +854,7 @@ internal class StringTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Shuffled_ShouldThrow_GivenNull()
|
public void Shuffled_ShouldThrowArgumentNullException_GivenNull()
|
||||||
{
|
{
|
||||||
string value = null!;
|
string value = null!;
|
||||||
Assert.Throws<ArgumentNullException>(() => _ = value.Shuffled());
|
Assert.Throws<ArgumentNullException>(() => _ = value.Shuffled());
|
||||||
@ -803,7 +888,7 @@ internal class StringTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void Split_ShouldThrow_GivenNullString()
|
public void Split_ShouldThrowArgumentNullException_GivenNullString()
|
||||||
{
|
{
|
||||||
string value = null!;
|
string value = null!;
|
||||||
|
|
||||||
@ -878,7 +963,7 @@ internal class StringTests
|
|||||||
[Test]
|
[Test]
|
||||||
public void StartsWithAny_ShouldThrowArgumentNullException_GivenANullValue()
|
public void StartsWithAny_ShouldThrowArgumentNullException_GivenANullValue()
|
||||||
{
|
{
|
||||||
var values = new[] {"Hello", null!, "World"};
|
var values = new[] { "Hello", null!, "World" };
|
||||||
Assert.Throws<ArgumentNullException>(() => "Foobar".StartsWithAny(values));
|
Assert.Throws<ArgumentNullException>(() => "Foobar".StartsWithAny(values));
|
||||||
Assert.Throws<ArgumentNullException>(() => "Foobar".StartsWithAny(StringComparison.Ordinal, values));
|
Assert.Throws<ArgumentNullException>(() => "Foobar".StartsWithAny(StringComparison.Ordinal, values));
|
||||||
}
|
}
|
||||||
|
@ -47,4 +47,34 @@ public static class CharExtensions
|
|||||||
_ => new string(value, count)
|
_ => new string(value, count)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes a character to a span of characters, repeated a specified number of times.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The character to repeat.</param>
|
||||||
|
/// <param name="count">The number of times to repeat.</param>
|
||||||
|
/// <param name="destination">The span of characters into which the repeated characters will be written.</param>
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static void Repeat(this char value, int count, Span<char> destination)
|
||||||
|
{
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination.Length < count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
destination[i] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
using System.Diagnostics.Contracts;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using X10D.CompilerServices;
|
||||||
|
|
||||||
namespace X10D.Text;
|
namespace X10D.Text;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -67,4 +72,73 @@ public static class CharSpanExtensions
|
|||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Repeats a span of characters a specified number of times.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The string to repeat.</param>
|
||||||
|
/// <param name="count">The repeat count.</param>
|
||||||
|
/// <returns>A string containing <paramref name="value" /> repeated <paramref name="count" /> times.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="count" /> is less than 0.</exception>
|
||||||
|
[Pure]
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static string Repeat(this ReadOnlySpan<char> value, int count)
|
||||||
|
{
|
||||||
|
switch (count)
|
||||||
|
{
|
||||||
|
case < 0:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0);
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = new StringBuilder(value.Length * count);
|
||||||
|
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
builder.Append(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Repeats a span of character a specified number of times, writing the result to another span of characters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The span of characters to repeat.</param>
|
||||||
|
/// <param name="count">The repeat count.</param>
|
||||||
|
/// <param name="destination">The destination span to write to.</param>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="count" /> is less than 0.</exception>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// <paramref name="destination" /> is too short to contain the repeated string.
|
||||||
|
/// </exception>
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static void Repeat(this ReadOnlySpan<char> value, int count, Span<char> destination)
|
||||||
|
{
|
||||||
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination.Length < value.Length * count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var iteration = 0; iteration < count; iteration++)
|
||||||
|
{
|
||||||
|
Span<char> slice = destination.Slice(iteration * value.Length, value.Length);
|
||||||
|
value.CopyTo(slice);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -932,14 +932,50 @@ public static class StringExtensions
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder = new StringBuilder(value.Length * count);
|
Span<char> destination = stackalloc char[value.Length * count];
|
||||||
|
value.Repeat(count, destination);
|
||||||
|
return new string(destination);
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
/// <summary>
|
||||||
|
/// Repeats a string a specified number of times, writing the result to a span of characters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">The string to repeat.</param>
|
||||||
|
/// <param name="count">The repeat count.</param>
|
||||||
|
/// <param name="destination">The destination span to write to.</param>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="value" /> is <see langword="null" />.</exception>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"><paramref name="count" /> is less than 0.</exception>
|
||||||
|
/// <exception cref="ArgumentException">
|
||||||
|
/// <paramref name="destination" /> is too short to contain the repeated string.
|
||||||
|
/// </exception>
|
||||||
|
[MethodImpl(CompilerResources.MethodImplOptions)]
|
||||||
|
public static void Repeat(this string value, int count, Span<char> destination)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
{
|
{
|
||||||
builder.Append(value);
|
throw new ArgumentNullException(nameof(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.ToString();
|
if (count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(count), ExceptionMessages.CountMustBeGreaterThanOrEqualTo0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destination.Length < value.Length * count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException(ExceptionMessages.DestinationSpanLengthTooShort, nameof(destination));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var iteration = 0; iteration < count; iteration++)
|
||||||
|
{
|
||||||
|
Span<char> slice = destination.Slice(iteration * value.Length, value.Length);
|
||||||
|
value.AsSpan().CopyTo(slice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Loading…
Reference in New Issue
Block a user