Skip to content

Commit

Permalink
Merge branch 'refs/heads/main' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
MrMatthewLayton committed Jul 2, 2024
2 parents e450a17 + 658325f commit 07cb34b
Show file tree
Hide file tree
Showing 69 changed files with 956 additions and 158 deletions.
42 changes: 42 additions & 0 deletions OnixLabs.Core.UnitTests.Data/BufferSegment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2020 ONIXLabs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Buffers;

namespace OnixLabs.Core.UnitTests.Data;

public sealed class BufferSegment<T> : ReadOnlySequenceSegment<T>
{
private BufferSegment(ReadOnlyMemory<T> memory) => Memory = memory;

private void SetNext(BufferSegment<T> next)
{
Next = next;
next.RunningIndex = RunningIndex + Memory.Length;
}

public static ReadOnlySequence<T> FromMemory(ReadOnlyMemory<T>[] segments)
{
if (segments.Length == 1)
return new ReadOnlySequence<T>(segments[0]);

BufferSegment<T>[] bufferSegments = segments
.Select(segment => new BufferSegment<T>(segment)).ToArray();

for (int index = 0; index < bufferSegments.Length - 1; index++)
bufferSegments[index].SetNext(bufferSegments[index + 1]);

return new ReadOnlySequence<T>(bufferSegments[0], 0, bufferSegments[^1], bufferSegments[^1].Memory.Length);
}
}
56 changes: 56 additions & 0 deletions OnixLabs.Core.UnitTests/PreconditionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,34 @@ public void CheckNotNullShouldNotThrowInvalidOperationExceptionWhenConditionIsNo
CheckNotNull(new object());
}

[Fact(DisplayName = "CheckNotNull of ValueType should throw an InvalidOperationException when the condition is null")]
public void CheckNotNullOfValueTypeShouldThrowInvalidOperationExceptionWhenConditionIsNull()
{
// Given
int? expected = null;
int actual = default;

// When
Exception exception = Assert.Throws<InvalidOperationException>(() => actual = CheckNotNull(expected));

// Then
Assert.Equal(default, actual);
Assert.Equal("Argument must not be null.", exception.Message);
}

[Fact(DisplayName = "CheckNotNull of ValueType should not throw an InvalidOperationException when the condition is not null")]
public void CheckNotNullOfValueTypeShouldNotThrowInvalidOperationExceptionWhenConditionIsNotNull()
{
// Given
int? expected = 123;

// When
int actual = CheckNotNull(expected);

// Then
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "CheckNotNullOrEmpty should throw an InvalidOperationException when the value is null")]
public void CheckNotNullOrEmptyShouldThrowInvalidOperationExceptionWhenValueIsNull()
{
Expand Down Expand Up @@ -240,6 +268,34 @@ public void RequireNotNullShouldNotThrowInvalidOperationExceptionWhenConditionIs
RequireNotNull(new object());
}

[Fact(DisplayName = "RequireNotNull of ValueType should throw an InvalidOperationException when the condition is null")]
public void RequireNotNullOfValueTypeShouldThrowInvalidOperationExceptionWhenConditionIsNull()
{
// Given
int? expected = null;
int actual = default;

// When
Exception exception = Assert.Throws<ArgumentNullException>(() => actual = RequireNotNull(expected));

// Then
Assert.Equal(default, actual);
Assert.Equal("Argument must not be null.", exception.Message);
}

[Fact(DisplayName = "RequireNotNull of ValueType should not throw an InvalidOperationException when the condition is not null")]
public void RequireNotNullOfValueTypeShouldNotThrowInvalidOperationExceptionWhenConditionIsNotNull()
{
// Given
int? expected = 123;

// When
int actual = RequireNotNull(expected);

// Then
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "RequireNotNullOrEmpty should throw an ArgumentException when the value is null")]
public void RequireNotNullOrEmptyShouldThrowArgumentExceptionWhenValueIsNull()
{
Expand Down
54 changes: 54 additions & 0 deletions OnixLabs.Core.UnitTests/ReadOnlySequenceExtensionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2020 ONIXLabs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Buffers;
using OnixLabs.Core.UnitTests.Data;
using Xunit;

namespace OnixLabs.Core.UnitTests;

public sealed class ReadOnlySequenceExtensionTests
{
[Fact(DisplayName = "ReadOnlySequence.CopyTo should copy into a new array with a single element")]
public void ReadOnlySequenceCopyToShouldCopyIntoNewArrayWithSingleElement()
{
// Given
string[] expected = ["ABC", "XYZ", "123", "789"];
ReadOnlySequence<string> value = new(expected);

// When
value.CopyTo(out string[] actual);

// Then
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "ReadOnlySequence.CopyTo should copy into a new array with multiple elements")]
public void ReadOnlySequenceCopyToShouldCopyIntoNewArrayWithMultipleElements()
{
// Given
string[] expected = ["ABC", "XYZ", "123", "789"];
ReadOnlySequence<string> value = BufferSegment<string>.FromMemory([
new ReadOnlyMemory<string>(["ABC", "XYZ"]),
new ReadOnlyMemory<string>(["123", "789"])
]);

// When
value.CopyTo(out string[] actual);

// Then
Assert.Equal(expected, actual);
}
}
15 changes: 15 additions & 0 deletions OnixLabs.Core.UnitTests/Text/Base16Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ public void Base16ShouldBeImplicitlyConstructableFromReadOnlySequenceOfChar()
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base16 should be implicitly constructable from ReadOnlySequence<byte>")]
public void Base16ShouldBeImplicitlyConstructableFromReadOnlySequenceOfByte()
{
// Given
byte[] expected = [1, 2, 3, 4];
ReadOnlySequence<byte> value = new(expected);

// When
Base16 candidate = value;
byte[] actual = candidate.ToByteArray();

// Then
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base16 should not change when modifying the original byte array")]
public void Base16ShouldNotChangeWhenModifyingOriginalByteArray()
{
Expand Down
15 changes: 15 additions & 0 deletions OnixLabs.Core.UnitTests/Text/Base32Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ public void Base32ShouldBeImplicitlyConstructableFromReadOnlySequenceOfChar()
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base32 should be implicitly constructable from ReadOnlySequence<byte>")]
public void Base32ShouldBeImplicitlyConstructableFromReadOnlySequenceOfByte()
{
// Given
byte[] expected = [1, 2, 3, 4];
ReadOnlySequence<byte> value = new(expected);

// When
Base32 candidate = value;
byte[] actual = candidate.ToByteArray();

// Then
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base32 should not change when modifying the original byte array")]
public void Base32ShouldNotChangeWhenModifyingOriginalByteArray()
{
Expand Down
15 changes: 15 additions & 0 deletions OnixLabs.Core.UnitTests/Text/Base58Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ public void Base58ShouldBeImplicitlyConstructableFromReadOnlySpanOfByte()
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base58 should be implicitly constructable from ReadOnlySequence<byte>")]
public void Base58ShouldBeImplicitlyConstructableFromReadOnlySequenceOfByte()
{
// Given
byte[] expected = [1, 2, 3, 4];
ReadOnlySequence<byte> value = new(expected);

// When
Base58 candidate = value;
byte[] actual = candidate.ToByteArray();

// Then
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base58 should be implicitly constructable from string")]
public void Base58ShouldBeImplicitlyConstructableFromString()
{
Expand Down
15 changes: 15 additions & 0 deletions OnixLabs.Core.UnitTests/Text/Base64Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,21 @@ public void Base64ShouldBeImplicitlyConstructableFromReadOnlySequenceOfChar()
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base64 should be implicitly constructable from ReadOnlySequence<byte>")]
public void Base64ShouldBeImplicitlyConstructableFromReadOnlySequenceOfByte()
{
// Given
byte[] expected = [1, 2, 3, 4];
ReadOnlySequence<byte> value = new(expected);

// When
Base64 candidate = value;
byte[] actual = candidate.ToByteArray();

// Then
Assert.Equal(expected, actual);
}

[Fact(DisplayName = "Base64 should not change when modifying the original byte array")]
public void Base64ShouldNotChangeWhenModifyingOriginalByteArray()
{
Expand Down
44 changes: 44 additions & 0 deletions OnixLabs.Core/Extensions.ReadOnlySequence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2020 ONIXLabs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Buffers;
using System.ComponentModel;

namespace OnixLabs.Core;

/// <summary>
/// Provides extension methods for read-only sequences.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
public static class ReadOnlySequenceExtensions
{
/// <summary>
/// Copies the current <see cref="ReadOnlySequence{T}"/> to the specified <see cref="T:[]"/> by reference.
/// </summary>
/// <param name="sequence">The current <see cref="ReadOnlySequence{T}"/> to copy.</param>
/// <param name="array">The <see cref="T:[]"/> to copy to.</param>
/// <typeparam name="T">The underlying type of the specified <see cref="ReadOnlySequence{T}"/> and <see cref="T:[]"/>.</typeparam>
public static void CopyTo<T>(this ReadOnlySequence<T> sequence, out T[] array)
{
if (sequence.IsSingleSegment)
{
array = sequence.First.Span.ToArray();
}
else
{
array = new T[sequence.Length];
sequence.CopyTo(array);
}
}
}
7 changes: 1 addition & 6 deletions OnixLabs.Core/Extensions.String.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@ public static class StringExtensions
/// </summary>
private const DateTimeStyles DefaultStyles = DateTimeStyles.None;

/// <summary>
/// The default encoding.
/// </summary>
private static readonly Encoding DefaultEncoding = Encoding.UTF8;

/// <summary>
/// Repeats the current <see cref="String"/> by the specified number of repetitions.
/// </summary>
Expand Down Expand Up @@ -246,7 +241,7 @@ public static string SubstringAfterLast(this string value, string delimiter, str
/// <param name="value">The current <see cref="String"/> from which to obtain a <see cref="T:Byte[]"/>.</param>
/// <param name="encoding">The encoding that will be used to convert the current <see cref="String"/> into a <see cref="T:Byte[]"/>.</param>
/// <returns>Returns a new <see cref="T:Byte[]"/> representation of the current <see cref="String"/> instance.</returns>
public static byte[] ToByteArray(this string value, Encoding? encoding = null) => (encoding ?? DefaultEncoding).GetBytes(value);
public static byte[] ToByteArray(this string value, Encoding? encoding = null) => encoding.GetOrDefault().GetBytes(value);

/// <summary>
/// Converts the current <see cref="String"/> instance into a new <see cref="DateTime"/> instance.
Expand Down
9 changes: 7 additions & 2 deletions OnixLabs.Core/OnixLabs.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
<Title>OnixLabs.Core</Title>
<Authors>ONIXLabs</Authors>
<Description>ONIXLabs Core API for .NET</Description>
<AssemblyVersion>8.15.0</AssemblyVersion>
<AssemblyVersion>8.16.0</AssemblyVersion>
<NeutralLanguage>en</NeutralLanguage>
<Copyright>Copyright © ONIXLabs 2020</Copyright>
<RepositoryUrl>https://github.com/onix-labs/onixlabs-dotnet</RepositoryUrl>
<PackageVersion>8.15.0</PackageVersion>
<PackageVersion>8.16.0</PackageVersion>
</PropertyGroup>
<PropertyGroup>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
Expand Down Expand Up @@ -45,4 +45,9 @@
<ItemGroup>
<Using Include="OnixLabs.Core.Preconditions" Static="True"/>
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="OnixLabs.Numerics"/>
<InternalsVisibleTo Include="OnixLabs.Security"/>
<InternalsVisibleTo Include="OnixLabs.Security.Cryptography"/>
</ItemGroup>
</Project>
28 changes: 28 additions & 0 deletions OnixLabs.Core/Preconditions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ public static T CheckNotNull<T>(T? value, string message = ArgumentNull)
return value ?? throw new InvalidOperationException(message);
}

/// <summary>
/// Performs a general pre-condition check that fails if the specified value is <see langword="null"/>.
/// </summary>
/// <param name="value">The nullable value to check.</param>
/// <param name="message">The exception message to throw in the event that the specified value is <see langword="null"/>.</param>
/// <typeparam name="T">The underlying type of the value.</typeparam>
/// <returns>Returns a non-null value of the specified type.</returns>
/// <exception cref="InvalidOperationException">If the specified value is <see langword="null"/>.</exception>
public static T CheckNotNull<T>(T? value, string message = ArgumentNull) where T : struct
{
return value ?? throw new InvalidOperationException(message);
}

/// <summary>
/// Performs a general pre-condition check that fails if the specified value is <see langword="null"/> or an empty string.
/// </summary>
Expand Down Expand Up @@ -99,6 +112,7 @@ public static void Require(bool condition, string message = ArgumentFailed, stri
/// <param name="parameterName">The name of the invalid parameter.</param>
/// <exception cref="ArgumentOutOfRangeException">If the specified condition is <see langword="false"/>.</exception>
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("This method is obsolete and will be removed in a future version. Use RequireWithinRangeInclusive or RequireWithinRangeExclusive methods instead.")]
public static void RequireWithinRange(bool condition, string message = ArgumentOutOfRange, string? parameterName = null)
{
if (!condition) throw new ArgumentOutOfRangeException(parameterName, message);
Expand Down Expand Up @@ -148,6 +162,20 @@ public static T RequireNotNull<T>(T? value, string message = ArgumentNull, strin
return value ?? throw new ArgumentNullException(parameterName, message);
}

/// <summary>
/// Performs a general pre-condition requirement that fails if the specified value is <see langword="null"/>.
/// </summary>
/// <param name="value">The nullable value to check.</param>
/// <param name="message">The exception message to throw in the event that the specified value is <see langword="null"/>.</param>
/// <param name="parameterName">The name of the invalid parameter.</param>
/// <typeparam name="T">The underlying type of the value.</typeparam>
/// <returns>Returns a non-null value of the specified type.</returns>
/// <exception cref="ArgumentNullException">If the specified value is <see langword="null"/>.</exception>
public static T RequireNotNull<T>(T? value, string message = ArgumentNull, string? parameterName = null) where T : struct
{
return value ?? throw new ArgumentNullException(parameterName, message);
}

/// <summary>
/// Performs a general pre-condition requirement that fails if the specified value is <see langword="null"/> or an empty string.
/// </summary>
Expand Down
Loading

0 comments on commit 07cb34b

Please sign in to comment.