diff --git a/Directory.Packages.props b/Directory.Packages.props
index c84a6f7..baba2e6 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,6 +9,5 @@
-
\ No newline at end of file
diff --git a/README.md b/README.md
index d849c0b..815cac3 100644
--- a/README.md
+++ b/README.md
@@ -47,16 +47,6 @@ foreach (ref readonly HeavyStruct s in view)
}
```
-## Changelog
-
-### 1.2.2
-- Optimize `ArrayView.Create` method for empty collection expression
-
-### 1.2.1
-- Add `List` to `ArrayView` extension for NET9.0+
-
-### 1.2.0
-- Add `Trim` overloads to `StringView` class
## Supported versions
diff --git a/Ramstack.Structures.Tests/Collections/ArrayViewExtensionsTests.cs b/Ramstack.Structures.Tests/Collections/ArrayViewExtensionsTests.cs
index 07652b1..76c0501 100644
--- a/Ramstack.Structures.Tests/Collections/ArrayViewExtensionsTests.cs
+++ b/Ramstack.Structures.Tests/Collections/ArrayViewExtensionsTests.cs
@@ -1,10 +1,10 @@
namespace Ramstack.Collections;
+#if NET9_0_OR_GREATER
+
[TestFixture]
public class ArrayViewExtensionsTests
{
- #if NET9_0_OR_GREATER
-
[Test]
public void List_AsView_Empty()
{
@@ -25,5 +25,6 @@ public void List_AsView()
Assert.That(view, Is.EquivalentTo(list));
}
- #endif
}
+
+#endif
diff --git a/Ramstack.Structures/Collections/ArrayViewExtensions.cs b/Ramstack.Structures/Collections/ArrayViewExtensions.cs
index 8b84906..d1f59ac 100644
--- a/Ramstack.Structures/Collections/ArrayViewExtensions.cs
+++ b/Ramstack.Structures/Collections/ArrayViewExtensions.cs
@@ -149,9 +149,13 @@ public static ArrayView AsView(this List? list)
if (list is not null)
{
var array = ListAccessor.GetArray(list);
- var count = Math.Min(list.Count, array.Length);
+ _ = array.Length;
- return new ArrayView(array, 0, count);
+ //
+ // SCG.List maintains internal invariants, so we can safely use the unchecked constructor
+ // to bypass redundant bounds checks for better performance.
+ //
+ return new ArrayView(array, 0, list.Count, unused: 0);
}
return ArrayView.Empty;
diff --git a/Ramstack.Structures/Collections/ArrayView`1.cs b/Ramstack.Structures/Collections/ArrayView`1.cs
index 56e6a04..83414dd 100644
--- a/Ramstack.Structures/Collections/ArrayView`1.cs
+++ b/Ramstack.Structures/Collections/ArrayView`1.cs
@@ -101,17 +101,23 @@ public ArrayView(T[] array, int index, int length)
/// Initializes a new instance of the structure that creates
/// a view for the specified range of the elements in the specified array.
///
+ ///
+ /// This constructor is intentionally minimal and skips all argument validations,
+ /// as the caller is responsible for ensuring correctness (e.g.,
+ /// is non-null, and
+ /// are within bounds).
+ ///
/// The array to wrap.
/// The zero-based index of the first element in the range.
/// The number of elements in the range.
- /// The dummy parameter.
+ /// Unused parameter, exists solely to disambiguate overloads.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private ArrayView(T[] array, int index, int length, int dummy)
+ internal ArrayView(T[] array, int index, int length, int unused)
{
_index = index;
_count = length;
_array = array;
- _ = dummy;
+ _ = unused;
}
///
@@ -132,7 +138,7 @@ public ArrayView Slice(int index)
if ((uint)index > (uint)_count)
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new ArrayView(_array!, _index + index, _count - index, dummy: 0);
+ return new ArrayView(_array!, _index + index, _count - index, unused: 0);
}
///
@@ -157,7 +163,7 @@ public ArrayView Slice(int index, int count)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
- return new ArrayView(_array!, _index + index, count, dummy: 0);
+ return new ArrayView(_array!, _index + index, count, unused: 0);
}
///
@@ -316,7 +322,7 @@ ref array.GetRawArrayData(view._index),
/// A representation of the array segment.
///
public static implicit operator ArrayView(ArraySegment segment) =>
- new(segment.Array!, segment.Offset, segment.Count, dummy: 0);
+ new(segment.Array!, segment.Offset, segment.Count, unused: 0);
///
/// Returns a string representation of the current instance's state,
diff --git a/Ramstack.Structures/Internal/JitHelpers.cs b/Ramstack.Structures/Internal/JitHelpers.cs
index 7fc3c8f..f664a93 100644
--- a/Ramstack.Structures/Internal/JitHelpers.cs
+++ b/Ramstack.Structures/Internal/JitHelpers.cs
@@ -30,7 +30,7 @@ public static ref readonly char GetRawStringData(this string text, int index) =>
public static ref T GetRawArrayData(this T[] array, int index)
{
// It's valid for a ref to point just past the end of an array, and it'll
- // be properly GC-tracked. (Though dereferencing it may result in undefined behavior.)
+ // be properly GC-tracked. (Though dereferencing it may result in undefined behavior)
return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), (nint)(uint)index);
}
}
diff --git a/Ramstack.Structures/Ramstack.Structures.csproj b/Ramstack.Structures/Ramstack.Structures.csproj
index e674105..b74071e 100644
--- a/Ramstack.Structures/Ramstack.Structures.csproj
+++ b/Ramstack.Structures/Ramstack.Structures.csproj
@@ -64,11 +64,6 @@ Ramstack.Collections.ReadOnlyArray<T>
-
-
-
-
-
True
diff --git a/Ramstack.Structures/Text/StringView.cs b/Ramstack.Structures/Text/StringView.cs
index 786c7cb..a2a3682 100644
--- a/Ramstack.Structures/Text/StringView.cs
+++ b/Ramstack.Structures/Text/StringView.cs
@@ -45,7 +45,7 @@ public char this[int index]
///
/// The string to wrap.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public StringView(string value) : this(value, 0, value.Length, dummy: 0)
+ public StringView(string value) : this(value, 0, value.Length, unused: 0)
{
}
@@ -96,17 +96,23 @@ public StringView(string value, int index, int length)
/// Initializes a new instance of the structure that creates
/// a view for the specified range of the characters in the specified string.
///
+ ///
+ /// This constructor is intentionally minimal and skips all argument validations,
+ /// as the caller is responsible for ensuring correctness (e.g.,
+ /// is non-null, and
+ /// are within bounds).
+ ///
/// The string to wrap.
/// The zero-based index of the first character in the range.
/// The number of characters in the range.
- /// The dummy parameter.
+ /// Unused parameter, exists solely to disambiguate overloads.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private StringView(string value, int index, int length, int dummy)
+ private StringView(string value, int index, int length, int unused)
{
_index = index;
_length = length;
_value = value;
- _ = dummy;
+ _ = unused;
}
///
@@ -156,7 +162,7 @@ public StringView Slice(int start)
if ((uint)start > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();
- return new StringView(_value!, _index + start, _length - start, dummy: 0);
+ return new StringView(_value!, _index + start, _length - start, unused: 0);
}
///
@@ -182,7 +188,7 @@ public StringView Slice(int start, int length)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
- return new StringView(_value!, _index + start, length, dummy: 0);
+ return new StringView(_value!, _index + start, length, unused: 0);
}
///
@@ -256,7 +262,7 @@ public StringView TrimStart()
if (!char.IsWhiteSpace(value.GetRawStringData(start)))
break;
- return new StringView(value!, start, final - start, dummy: 0);
+ return new StringView(value!, start, final - start, unused: 0);
}
///
@@ -277,7 +283,7 @@ public StringView TrimStart(char trimChar)
if (value.GetRawStringData(start) != trimChar)
break;
- return new StringView(value!, start, final - start, dummy: 0);
+ return new StringView(value!, start, final - start, unused: 0);
}
///
@@ -306,7 +312,7 @@ public StringView TrimStart(params char[]? trimChars) =>
///
/// The trimmed .
///
- public StringView TrimStart(ReadOnlySpan trimChars)
+ public StringView TrimStart(params ReadOnlySpan trimChars)
{
if (trimChars.Length == 0)
return TrimStart();
@@ -319,8 +325,10 @@ public StringView TrimStart(ReadOnlySpan trimChars)
{
for (; start < final; start++)
{
- for (var i = 0; i < trimChars.Length; i++)
- if (value.GetRawStringData(start) == trimChars[i])
+ var ch = value.GetRawStringData(start);
+
+ foreach (var trimChar in trimChars)
+ if (ch == trimChar)
goto MATCHED;
break;
@@ -328,7 +336,7 @@ public StringView TrimStart(ReadOnlySpan trimChars)
}
}
- return new StringView(value!, start, final - start, dummy: 0);
+ return new StringView(value!, start, final - start, unused: 0);
}
///
@@ -348,7 +356,7 @@ public StringView TrimEnd()
if (!char.IsWhiteSpace(value.GetRawStringData(final)))
break;
- return new StringView(value!, start, final + 1 - start, dummy: 0);
+ return new StringView(value!, start, final + 1 - start, unused: 0);
}
///
@@ -369,7 +377,7 @@ public StringView TrimEnd(char trimChar)
if (value.GetRawStringData(final) != trimChar)
break;
- return new StringView(value!, start, final + 1 - start, dummy: 0);
+ return new StringView(value!, start, final + 1 - start, unused: 0);
}
///
@@ -398,7 +406,7 @@ public StringView TrimEnd(params char[]? trimChars) =>
///
/// The trimmed .
///
- public StringView TrimEnd(ReadOnlySpan trimChars)
+ public StringView TrimEnd(params ReadOnlySpan trimChars)
{
if (trimChars.Length == 0)
return TrimEnd();
@@ -411,8 +419,10 @@ public StringView TrimEnd(ReadOnlySpan trimChars)
{
for (; final >= start; final--)
{
- for (var i = 0; i < trimChars.Length; i++)
- if (value.GetRawStringData(final) == trimChars[i])
+ var ch = value.GetRawStringData(final);
+
+ foreach (var trimChar in trimChars)
+ if (ch == trimChar)
goto MATCHED;
break;
@@ -420,7 +430,7 @@ public StringView TrimEnd(ReadOnlySpan trimChars)
}
}
- return new StringView(value!, start, final + 1 - start, dummy: 0);
+ return new StringView(value!, start, final + 1 - start, unused: 0);
}
///
@@ -446,7 +456,7 @@ public StringView Trim()
break;
}
- return new StringView(value!, start, final + 1 - start, dummy: 0);
+ return new StringView(value!, start, final + 1 - start, unused: 0);
}
///
@@ -473,7 +483,7 @@ public StringView Trim(char trimChar)
break;
}
- return new StringView(value!, start, final + 1 - start, dummy: 0);
+ return new StringView(value!, start, final + 1 - start, unused: 0);
}
///
@@ -502,7 +512,7 @@ public StringView Trim(params char[]? trimChars) =>
///
/// The trimmed .
///
- public StringView Trim(ReadOnlySpan trimChars)
+ public StringView Trim(params ReadOnlySpan trimChars)
{
if (trimChars.Length == 0)
return Trim();
@@ -515,8 +525,10 @@ public StringView Trim(ReadOnlySpan trimChars)
{
for (; start <= final; start++)
{
- for (var i = 0; i < trimChars.Length; i++)
- if (value.GetRawStringData(start) == trimChars[i])
+ var ch = value.GetRawStringData(start);
+
+ foreach (var trimChar in trimChars)
+ if (ch == trimChar)
goto MATCHED;
break;
@@ -525,8 +537,10 @@ public StringView Trim(ReadOnlySpan trimChars)
for (; final > start; final--)
{
- for (var i = 0; i < trimChars.Length; i++)
- if (value.GetRawStringData(final) == trimChars[i])
+ var ch = value.GetRawStringData(final);
+
+ foreach (var trimChar in trimChars)
+ if (ch == trimChar)
goto MATCHED;
break;
@@ -534,7 +548,7 @@ public StringView Trim(ReadOnlySpan trimChars)
}
}
- return new StringView(value!, start, final + 1 - start, dummy: 0);
+ return new StringView(value!, start, final + 1 - start, unused: 0);
}
///
@@ -723,8 +737,8 @@ public override bool Equals([NotNullWhen(true)] object? obj)
if (obj is null)
return _length == 0;
- if (obj is StringView)
- return Equals(this, Unsafe.Unbox(obj));
+ if (obj is StringView view)
+ return Equals(this, view);
return obj is string s && Equals(s);
}