Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="NUnit" Version="4.3.2" />
<PackageVersion Include="NUnit3TestAdapter" Version="5.0.0" />
<PackageVersion Include="System.Collections.Immutable" Version="6.0.0" />
</ItemGroup>
</Project>
10 changes: 0 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` to `ArrayView<T>` extension for NET9.0+

### 1.2.0
- Add `Trim` overloads to `StringView` class

## Supported versions

Expand Down
Original file line number Diff line number Diff line change
@@ -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()
{
Expand All @@ -25,5 +25,6 @@ public void List_AsView()
Assert.That(view, Is.EquivalentTo(list));
}

#endif
}

#endif
8 changes: 6 additions & 2 deletions Ramstack.Structures/Collections/ArrayViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,13 @@ public static ArrayView<T> AsView<T>(this List<T>? list)
if (list is not null)
{
var array = ListAccessor<T>.GetArray(list);
var count = Math.Min(list.Count, array.Length);
_ = array.Length;

return new ArrayView<T>(array, 0, count);
//
// SCG.List<T> maintains internal invariants, so we can safely use the unchecked constructor
// to bypass redundant bounds checks for better performance.
//
return new ArrayView<T>(array, 0, list.Count, unused: 0);
}

return ArrayView<T>.Empty;
Expand Down
18 changes: 12 additions & 6 deletions Ramstack.Structures/Collections/ArrayView`1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,17 +101,23 @@ public ArrayView(T[] array, int index, int length)
/// Initializes a new instance of the <see cref="ArrayView{T}"/> structure that creates
/// a view for the specified range of the elements in the specified array.
/// </summary>
/// <remarks>
/// This constructor is intentionally minimal and skips all argument validations,
/// as the caller is responsible for ensuring correctness (e.g.,
/// <paramref name="array"/> is non-null, <paramref name="index"/> and
/// <paramref name="length"/> are within bounds).
/// </remarks>
/// <param name="array">The array to wrap.</param>
/// <param name="index">The zero-based index of the first element in the range.</param>
/// <param name="length">The number of elements in the range.</param>
/// <param name="dummy">The dummy parameter.</param>
/// <param name="unused">Unused parameter, exists solely to disambiguate overloads.</param>
[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;
}

/// <inheritdoc cref="IEnumerable{T}.GetEnumerator"/>
Expand All @@ -132,7 +138,7 @@ public ArrayView<T> Slice(int index)
if ((uint)index > (uint)_count)
ThrowHelper.ThrowArgumentOutOfRangeException();

return new ArrayView<T>(_array!, _index + index, _count - index, dummy: 0);
return new ArrayView<T>(_array!, _index + index, _count - index, unused: 0);
}

/// <summary>
Expand All @@ -157,7 +163,7 @@ public ArrayView<T> Slice(int index, int count)
ThrowHelper.ThrowArgumentOutOfRangeException();
}

return new ArrayView<T>(_array!, _index + index, count, dummy: 0);
return new ArrayView<T>(_array!, _index + index, count, unused: 0);
}

/// <summary>
Expand Down Expand Up @@ -316,7 +322,7 @@ ref array.GetRawArrayData(view._index),
/// A <see cref="ArrayView{T}"/> representation of the array segment.
/// </returns>
public static implicit operator ArrayView<T>(ArraySegment<T> segment) =>
new(segment.Array!, segment.Offset, segment.Count, dummy: 0);
new(segment.Array!, segment.Offset, segment.Count, unused: 0);

/// <summary>
/// Returns a string representation of the current instance's state,
Expand Down
2 changes: 1 addition & 1 deletion Ramstack.Structures/Internal/JitHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public static ref readonly char GetRawStringData(this string text, int index) =>
public static ref T GetRawArrayData<T>(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);
}
}
5 changes: 0 additions & 5 deletions Ramstack.Structures/Ramstack.Structures.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,6 @@ Ramstack.Collections.ReadOnlyArray&lt;T&gt;</Description>
</PackageReference>
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="System.Collections.Immutable" />
</ItemGroup>


<ItemGroup>
<None Include="..\README.md" Link="Properties\README.md">
<Pack>True</Pack>
Expand Down
70 changes: 42 additions & 28 deletions Ramstack.Structures/Text/StringView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public char this[int index]
/// </summary>
/// <param name="value">The string to wrap.</param>
[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)
{
}

Expand Down Expand Up @@ -96,17 +96,23 @@ public StringView(string value, int index, int length)
/// Initializes a new instance of the <see cref="StringView"/> structure that creates
/// a view for the specified range of the characters in the specified string.
/// </summary>
/// <remarks>
/// This constructor is intentionally minimal and skips all argument validations,
/// as the caller is responsible for ensuring correctness (e.g.,
/// <paramref name="value"/> is non-null, <paramref name="index"/> and
/// <paramref name="length"/> are within bounds).
/// </remarks>
/// <param name="value">The string to wrap.</param>
/// <param name="index">The zero-based index of the first character in the range.</param>
/// <param name="length">The number of characters in the range.</param>
/// <param name="dummy">The dummy parameter.</param>
/// <param name="unused">Unused parameter, exists solely to disambiguate overloads.</param>
[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;
}

/// <inheritdoc cref="IEnumerable{T}.GetEnumerator"/>
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand All @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -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);
}

/// <summary>
Expand All @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -306,7 +312,7 @@ public StringView TrimStart(params char[]? trimChars) =>
/// <returns>
/// The trimmed <see cref="StringView"/>.
/// </returns>
public StringView TrimStart(ReadOnlySpan<char> trimChars)
public StringView TrimStart(params ReadOnlySpan<char> trimChars)
{
if (trimChars.Length == 0)
return TrimStart();
Expand All @@ -319,16 +325,18 @@ public StringView TrimStart(ReadOnlySpan<char> 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;
MATCHED: ;
}
}

return new StringView(value!, start, final - start, dummy: 0);
return new StringView(value!, start, final - start, unused: 0);
}

/// <summary>
Expand All @@ -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);
}

/// <summary>
Expand All @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -398,7 +406,7 @@ public StringView TrimEnd(params char[]? trimChars) =>
/// <returns>
/// The trimmed <see cref="StringView"/>.
/// </returns>
public StringView TrimEnd(ReadOnlySpan<char> trimChars)
public StringView TrimEnd(params ReadOnlySpan<char> trimChars)
{
if (trimChars.Length == 0)
return TrimEnd();
Expand All @@ -411,16 +419,18 @@ public StringView TrimEnd(ReadOnlySpan<char> 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;
MATCHED: ;
}
}

return new StringView(value!, start, final + 1 - start, dummy: 0);
return new StringView(value!, start, final + 1 - start, unused: 0);
}

/// <summary>
Expand All @@ -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);
}

/// <summary>
Expand All @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -502,7 +512,7 @@ public StringView Trim(params char[]? trimChars) =>
/// <returns>
/// The trimmed <see cref="StringView"/>.
/// </returns>
public StringView Trim(ReadOnlySpan<char> trimChars)
public StringView Trim(params ReadOnlySpan<char> trimChars)
{
if (trimChars.Length == 0)
return Trim();
Expand All @@ -515,8 +525,10 @@ public StringView Trim(ReadOnlySpan<char> 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;
Expand All @@ -525,16 +537,18 @@ public StringView Trim(ReadOnlySpan<char> 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;
MATCHED: ;
}
}

return new StringView(value!, start, final + 1 - start, dummy: 0);
return new StringView(value!, start, final + 1 - start, unused: 0);
}

/// <summary>
Expand Down Expand Up @@ -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<StringView>(obj));
if (obj is StringView view)
return Equals(this, view);

return obj is string s && Equals(s);
}
Expand Down