diff --git a/Directory.Build.props b/Directory.Build.props index f56123e..62d7b4c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -12,7 +12,7 @@ false false - 0.1.0 + 0.0.1 Kir_Antipov 2023 https://github.com/Kir-Antipov/Spanned diff --git a/src/Spanned/Collections/Generic/InlineArray.cs b/src/Spanned/Collections/Generic/InlineArray.cs deleted file mode 100644 index 6e4e2a1..0000000 --- a/src/Spanned/Collections/Generic/InlineArray.cs +++ /dev/null @@ -1,115 +0,0 @@ -namespace Spanned.Collections.Generic; - -#pragma warning disable CA1823, CS0169, IDE0044, IDE0051 // `_element0` is used by the compiler. - -/// -/// Represents an inline array with one element. -/// -/// The type of the elements in the array. -[StructLayout(LayoutKind.Sequential)] // Like it's gonna help... -internal struct InlineArray1 -{ - /// - /// The reference to the first element. - /// - private T _element0; - - /// - /// Initializes a new instance of the struct with the specified elements. - /// - /// - public InlineArray1(T arg0) - { - _element0 = arg0; - } - - /// - /// Implicitly converts an to a . - /// - /// The to convert. - /// A representing the elements of the . - public static implicit operator ReadOnlySpan(in InlineArray1 inlineArray) - => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in inlineArray._element0), 1); -} - -/// -/// Represents an inline array with two elements. -/// -/// The type of the elements in the array. -[StructLayout(LayoutKind.Sequential)] // Like it's gonna help... -internal struct InlineArray2 -{ - /// - /// The reference to the first element. - /// - private T _element0; - - /// - /// The reference to the second element. - /// - private T _element1; - - /// - /// Initializes a new instance of the struct with the specified elements. - /// - /// - public InlineArray2(T arg0, T arg1) - { - _element0 = arg0; - _element1 = arg1; - } - - /// - /// Implicitly converts an to a . - /// - /// The to convert. - /// A representing the elements of the . - public static implicit operator ReadOnlySpan(in InlineArray2 inlineArray) - => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in inlineArray._element0), 2); -} - -/// -/// Represents an inline array with three elements. -/// -/// The type of the elements in the array. -[StructLayout(LayoutKind.Sequential)] // Like it's gonna help... -internal struct InlineArray3 -{ - /// - /// The reference to the first element. - /// - private T _element0; - - /// - /// The reference to the second element. - /// - private T _element1; - - /// - /// The reference to the third element. - /// - private T _element2; - - /// - /// Initializes a new instance of the struct with the specified elements. - /// - /// The first element. - /// The second element. - /// The third element. - public InlineArray3(T arg0, T arg1, T arg2) - { - _element0 = arg0; - _element1 = arg1; - _element2 = arg2; - } - - /// - /// Implicitly converts an to a . - /// - /// The to convert. - /// A representing the elements of the . - public static implicit operator ReadOnlySpan(in InlineArray3 inlineArray) - => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in inlineArray._element0), 3); -} - -#pragma warning restore CA1823, CS0169, IDE0044, IDE0051 // `_element0` is used by the compiler. diff --git a/src/Spanned/Collections/Generic/ValueDictionary.cs b/src/Spanned/Collections/Generic/ValueDictionary.cs index 1294484..e1a44e2 100644 --- a/src/Spanned/Collections/Generic/ValueDictionary.cs +++ b/src/Spanned/Collections/Generic/ValueDictionary.cs @@ -101,7 +101,6 @@ public ValueDictionary(int capacity) /// The implementation to use when comparing keys in the dictionary, or /// null to use the default implementation for the dictionary type. /// - /// public ValueDictionary(int capacity, IEqualityComparer? comparer) : this(comparer) { @@ -146,7 +145,6 @@ public ValueDictionary(scoped ReadOnlySpan> collectio /// The implementation to use when comparing keys in the dictionary, or /// null to use the default implementation for the dictionary type. /// - /// public ValueDictionary(scoped ReadOnlySpan> collection, int capacity, IEqualityComparer? comparer) : this(comparer) { @@ -194,7 +192,7 @@ public ValueDictionary(IEnumerable> collection, IEqua /// The dictionary to be converted. /// A span covering the content of the . public static implicit operator ReadOnlySpan>(ValueDictionary dictionary) - => MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(dictionary._buffer), dictionary._count); + => dictionary._buffer.Slice(0, dictionary._count); /// /// Returns a span that represents the content of the . @@ -204,7 +202,7 @@ public static implicit operator ReadOnlySpan>(ValueDi /// /// A span covering the content of the . public readonly Span> AsSpan() - => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_buffer), _count); + => _buffer.Slice(0, _count); /// /// Returns a span that represents a segment of the dictionary starting from the specified index. @@ -214,7 +212,6 @@ public readonly Span> AsSpan() /// /// The start index of the segment. /// A span covering the segment of the dictionary. - /// public readonly Span> AsSpan(int start) => AsSpan(start, _count - start); /// @@ -226,13 +223,11 @@ public readonly Span> AsSpan() /// The start index of the segment. /// The length of the segment. /// A span covering the segment of the dictionary. - /// public readonly Span> AsSpan(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - // Skip additional bound checks. - return MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), start), length); + return _buffer.Slice(start, length); } /// @@ -257,7 +252,6 @@ public readonly Span> AsSpan(int start, int length) /// /// The number of key/value pairs contained in the . /// - /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -274,7 +268,6 @@ public int Count /// The total number of key/value pairs the internal data structure can hold /// without resizing. /// - /// public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -358,7 +351,6 @@ internal readonly KeyValuePair[] DebuggerItems /// /// The number of entries. /// The current capacity of the . - /// public int EnsureCapacity(int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity); @@ -507,11 +499,8 @@ public bool Remove(TKey key, [MaybeNullWhen(false)] out TValue value) if (index < 0) return false; - if (RuntimeHelpers.IsReferenceOrContainsReferences>()) - { - // Clear the element so that the GC can reclaim its reference. - _buffer[index] = default!; - } + // Clear the element so that the GC can reclaim its reference. + _buffer[index] = default!; _buffer.Slice(index + 1).CopyTo(_buffer.Slice(index)); _count--; @@ -523,18 +512,14 @@ public bool Remove(TKey key, [MaybeNullWhen(false)] out TValue value) /// Removes the entry at the specified index of the . /// /// The zero-based index of the entry to remove. - /// [EditorBrowsable(EditorBrowsableState.Never)] [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveAt(int index) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidIndex(index, _count); - if (RuntimeHelpers.IsReferenceOrContainsReferences>()) - { - // Clear the element so that the GC can reclaim its reference. - _buffer[index] = default!; - } + // Clear the element so that the GC can reclaim its reference. + _buffer[index] = default!; _buffer.Slice(index + 1).CopyTo(_buffer.Slice(index)); _count--; @@ -545,17 +530,13 @@ public void RemoveAt(int index) /// /// The zero-based starting index of the range of entries to remove. /// The number of entries to remove. - /// [EditorBrowsable(EditorBrowsableState.Never)] public void RemoveRange(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - if (RuntimeHelpers.IsReferenceOrContainsReferences>()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan(start, length).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan(start, length).Clear(); _buffer.Slice(start + length).CopyTo(_buffer.Slice(start)); _count -= length; @@ -591,11 +572,8 @@ public void RemoveRange(int start, int length) /// public void Clear() { - if (RuntimeHelpers.IsReferenceOrContainsReferences>()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _count = 0; } @@ -643,7 +621,7 @@ internal readonly int IndexOfKey(TKey key, out TValue value) for (int i = 0; i < entries.Length; i++) { KeyValuePair pair = entries[i]; - if (comparer.Equals(key, pair.Key)) + if (comparer!.Equals(key, pair.Key)) { value = pair.Value; return i; @@ -694,7 +672,6 @@ internal readonly int IndexOfValue(TValue value, out TKey key) /// /// A shallow copy of a range of elements in the source . /// - /// [EditorBrowsable(EditorBrowsableState.Never)] public readonly ValueDictionary Slice(int start, int length) { @@ -729,7 +706,6 @@ public readonly void CopyTo(scoped Span> destination) /// The destination of the elements copied from . /// /// The zero-based index in the destination at which copying begins. - /// public readonly void CopyTo(KeyValuePair[] destination, int destinationStart) => CopyTo(0, destination, destinationStart, _count); /// @@ -742,7 +718,6 @@ public readonly void CopyTo(scoped Span> destination) /// /// The number of elements to copy. /// - /// public readonly void CopyTo(int start, scoped Span> destination, int length) => AsSpan(start, length).CopyTo(destination); @@ -755,7 +730,6 @@ public readonly void CopyTo(int start, scoped Span> d /// /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. - /// public readonly void CopyTo(int start, KeyValuePair[] destination, int destinationStart, int length) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -798,7 +772,6 @@ public readonly bool TryCopyTo(KeyValuePair[] destination, out int /// The destination of the elements copied from . /// The zero-based index in the destination at which copying begins. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(KeyValuePair[] destination, int destinationStart, out int written) => TryCopyTo(0, destination, destinationStart, _count, out written); @@ -809,7 +782,6 @@ public readonly bool TryCopyTo(KeyValuePair[] destination, int des /// The destination of the elements copied from . /// The number of elements to copy. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(int start, scoped Span> destination, int length, out int written) { if (AsSpan(start, length).TryCopyTo(destination)) @@ -834,7 +806,6 @@ public readonly bool TryCopyTo(int start, scoped Span /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. /// The number of elements copied to the array. - /// public readonly bool TryCopyTo(int start, KeyValuePair[] destination, int destinationStart, int length, out int written) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -923,13 +894,13 @@ public Dictionary ToDictionary(bool dispose) public readonly Span>.Enumerator GetEnumerator() => AsSpan().GetEnumerator(); - /// + /// [Obsolete("Equals(object) on ValueDictionary will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueDictionary will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() @@ -941,11 +912,8 @@ public override readonly int GetHashCode() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - if (RuntimeHelpers.IsReferenceOrContainsReferences>()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); KeyValuePair[]? oldRentedBuffer = _rentedBuffer; @@ -976,7 +944,6 @@ public void TrimExcess() /// without any further expansion of its backing storage. /// /// The new capacity. - /// public void TrimExcess(int capacity) { if (capacity < _buffer.Length) @@ -1014,11 +981,8 @@ private void ResizeBuffer(int capacity) KeyValuePair[]? oldRentedBuffer = _rentedBuffer; AsSpan().CopyTo(newRentedBuffer); - if (RuntimeHelpers.IsReferenceOrContainsReferences>()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _buffer = _rentedBuffer = newRentedBuffer; if (oldRentedBuffer is not null) @@ -1082,7 +1046,6 @@ public KeyCollection(scoped in ValueDictionary dictionary) /// The destination of the elements copied from the . /// /// The zero-based index in array at which copying begins. - /// /// is null. /// /// The number of elements in the source is greater @@ -1115,13 +1078,13 @@ public void CopyTo(scoped Span destination) destination[i] = entries[i].Key; } - /// + /// [Obsolete("Equals(object) on KeyCollection will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on KeyCollection will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() @@ -1247,7 +1210,6 @@ public ValueCollection(scoped in ValueDictionary dictionary) /// The destination of the elements copied from the . /// /// The zero-based index in array at which copying begins. - /// /// is null. /// /// The number of elements in the source is greater @@ -1280,13 +1242,13 @@ public void CopyTo(scoped Span destination) destination[i] = entries[i].Value; } - /// + /// [Obsolete("Equals(object) on ValueCollection will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueCollection will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() diff --git a/src/Spanned/Collections/Generic/ValueEnumerable.cs b/src/Spanned/Collections/Generic/ValueEnumerable.cs index 8c236e3..1dc83ac 100644 --- a/src/Spanned/Collections/Generic/ValueEnumerable.cs +++ b/src/Spanned/Collections/Generic/ValueEnumerable.cs @@ -97,13 +97,13 @@ public ValueEnumerable(IEnumerable? enumerable) /// public Enumerator GetEnumerator() => new(this); - /// + /// [Obsolete("Equals(object) on ValueEnumerable will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueEnumerable will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() diff --git a/src/Spanned/Collections/Generic/ValueList.cs b/src/Spanned/Collections/Generic/ValueList.cs index 4960966..6079dd7 100644 --- a/src/Spanned/Collections/Generic/ValueList.cs +++ b/src/Spanned/Collections/Generic/ValueList.cs @@ -41,7 +41,6 @@ public ValueList(Span buffer) /// is empty and has the specified initial capacity. /// /// The number of elements that the new list can initially store. - /// public ValueList(int capacity) { _rentedBuffer = ArrayPool.Shared.Rent(capacity); @@ -66,7 +65,6 @@ public ValueList(scoped ReadOnlySpan collection) /// /// The collection whose elements are copied to the new list. /// The number of elements that the new list can initially store. - /// public ValueList(scoped ReadOnlySpan collection, int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity, collection.Length); @@ -119,20 +117,19 @@ public ValueList(IEnumerable collection) /// The list to be converted. /// A span covering the content of the . public static implicit operator ReadOnlySpan(ValueList list) - => MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(list._buffer), list._count); + => list._buffer.Slice(0, list._count); /// /// Returns a span that represents the content of the . /// /// A span covering the content of the . - public readonly Span AsSpan() => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_buffer), _count); + public readonly Span AsSpan() => _buffer.Slice(0, _count); /// /// Returns a span that represents a segment of the list starting from the specified index. /// /// The start index of the segment. /// A span covering the segment of the list. - /// public readonly Span AsSpan(int start) => AsSpan(start, _count - start); /// @@ -141,13 +138,11 @@ public static implicit operator ReadOnlySpan(ValueList list) /// The start index of the segment. /// The length of the segment. /// A span covering the segment of the list. - /// public readonly Span AsSpan(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - // Skip additional bound checks. - return MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), start), length); + return _buffer.Slice(start, length); } /// @@ -166,7 +161,6 @@ public readonly Span AsSpan(int start, int length) /// /// The number of elements contained in the . /// - /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -183,7 +177,6 @@ public int Count /// The total number of elements the internal data structure can hold /// without resizing. /// - /// public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -204,7 +197,6 @@ public int Capacity /// /// The zero-based index of the element to get or set. /// The element at the specified index. - /// public readonly ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -242,7 +234,6 @@ internal readonly T[] DebuggerItems /// /// The new capacity of this list. /// The minimum capacity to ensure. - /// public int EnsureCapacity(int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity); @@ -306,7 +297,6 @@ private void GrowAndAdd(T item) /// /// The length of the span to reserve. /// A span that represents the reserved space. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span AddSpan(int length) { @@ -420,7 +410,6 @@ public void AddRange(IEnumerable collection) /// /// The value can be null for reference types. /// - /// public void Insert(int index, T item) { if ((uint)index > (uint)_count) @@ -452,7 +441,6 @@ public void Insert(int index, T item) /// The collection can contain elements that are null, if type /// is a reference type. /// - /// public void InsertRange(int index, scoped ReadOnlySpan collection) { if ((uint)index > (uint)_count) @@ -570,17 +558,13 @@ public int RemoveAll(Predicate predicate) /// Removes the element at the specified index of the . /// /// The zero-based index of the element to remove. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveAt(int index) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidIndex(index, _count); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - _buffer[index] = default!; - } + // Clear the element so that the GC can reclaim its reference. + _buffer[index] = default!; _buffer.Slice(index + 1, _count - index - 1).CopyTo(_buffer.Slice(index)); _count--; @@ -591,16 +575,12 @@ public void RemoveAt(int index) /// /// The zero-based starting index of the range of elements to remove. /// The number of elements to remove. - /// public void RemoveRange(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan(start, length).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan(start, length).Clear(); _buffer.Slice(start + length).CopyTo(_buffer.Slice(start)); _count -= length; @@ -624,11 +604,8 @@ public void RemoveRange(int start, int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _count = 0; } @@ -656,7 +633,6 @@ public void Clear() /// The zero-based starting index of the search. /// 0 (zero) is valid in an empty list. /// - /// public readonly int IndexOf(T item, int index) => IndexOf(item, index, _count - index); /// @@ -669,7 +645,6 @@ public void Clear() /// 0 (zero) is valid in an empty list. /// /// The number of elements in the section to search. - /// public readonly int IndexOf(T item, int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); @@ -698,7 +673,6 @@ public readonly int IndexOf(T item, int start, int length) /// The value can be null for reference types. /// /// The zero-based starting index of the backward search. - /// public readonly int LastIndexOf(T item, int index) { // Don't use (uint) here. @@ -718,7 +692,6 @@ public readonly int LastIndexOf(T item, int index) /// /// The zero-based starting index of the backward search. /// The number of elements in the section to search. - /// public readonly int LastIndexOf(T item, int start, int length) { // Very f-ing funny. @@ -783,7 +756,6 @@ public readonly int BinarySearch(T item, IComparer? comparer) /// -or- /// null to use the default comparer . /// - /// public readonly int BinarySearch(int start, int length, T item, IComparer? comparer) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); @@ -844,7 +816,6 @@ public readonly int BinarySearch(int start, int length, T item, IComparer? co /// /// The delegate that defines the conditions of the elements to search for. /// - /// public readonly int FindIndex(int start, Predicate predicate) => FindIndex(start, _count - start, predicate); @@ -854,7 +825,6 @@ public readonly int FindIndex(int start, Predicate predicate) /// /// The delegate that defines the conditions of the elements to search for. /// - /// public readonly int FindIndex(int start, int length, Predicate predicate) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); @@ -909,7 +879,6 @@ public readonly int FindLastIndex(Predicate predicate) /// /// The delegate that defines the conditions of the elements to search for. /// - /// public readonly int FindLastIndex(int start, Predicate predicate) { // Additional useless null-check to satisfy unit tests. @@ -931,7 +900,6 @@ public readonly int FindLastIndex(int start, Predicate predicate) /// /// The delegate that defines the conditions of the elements to search for. /// - /// public readonly int FindLastIndex(int start, int length, Predicate predicate) { ThrowHelper.ThrowArgumentNullException_IfNull(predicate); @@ -1074,7 +1042,6 @@ public readonly void ForEach(Action action) /// The implementation to use when comparing elements, or null /// to use the default comparer. /// - /// public readonly void Sort(int start, int length, IComparer? comparer) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); @@ -1090,7 +1057,6 @@ public readonly void Sort(int start, int length, IComparer? comparer) /// /// The zero-based starting index of the range to reverse. /// The number of elements in the range to reverse. - /// public readonly void Reverse(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); @@ -1135,7 +1101,6 @@ public readonly ValueList ConvertAll(Converter con /// /// A shallow copy of a range of elements in the source . /// - /// public readonly ValueList GetRange(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); @@ -1176,7 +1141,6 @@ public readonly ValueList Slice(int index, int count) /// The destination of the elements copied from . /// /// The zero-based index in the destination at which copying begins. - /// public readonly void CopyTo(T[] destination, int destinationStart) => CopyTo(0, destination, destinationStart, _count); /// @@ -1189,7 +1153,6 @@ public readonly ValueList Slice(int index, int count) /// /// The number of elements to copy. /// - /// public readonly void CopyTo(int start, scoped Span destination, int length) => AsSpan(start, length).CopyTo(destination); /// @@ -1201,7 +1164,6 @@ public readonly ValueList Slice(int index, int count) /// /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. - /// public readonly void CopyTo(int start, T[] destination, int destinationStart, int length) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1244,7 +1206,6 @@ public readonly bool TryCopyTo(T[] destination, out int written) /// The destination of the elements copied from . /// The zero-based index in the destination at which copying begins. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(T[] destination, int destinationStart, out int written) => TryCopyTo(0, destination, destinationStart, _count, out written); @@ -1255,7 +1216,6 @@ public readonly bool TryCopyTo(T[] destination, int destinationStart, out int wr /// The destination of the elements copied from . /// The number of elements to copy. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(int start, scoped Span destination, int length, out int written) { if (AsSpan(start, length).TryCopyTo(destination)) @@ -1280,9 +1240,7 @@ public readonly bool TryCopyTo(int start, scoped Span destination, int length /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. /// The number of elements copied to the array. - /// - public readonly - bool TryCopyTo(int start, T[] destination, int destinationStart, int length, out int written) + public readonly bool TryCopyTo(int start, T[] destination, int destinationStart, int length, out int written) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1391,13 +1349,13 @@ public readonly ReadOnlyCollection AsReadOnly() [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Span.Enumerator GetEnumerator() => AsSpan().GetEnumerator(); - /// + /// [Obsolete("Equals(object) on ValueList will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueList will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() @@ -1409,11 +1367,8 @@ public override readonly int GetHashCode() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); if (_rentedBuffer is not null) { @@ -1440,7 +1395,6 @@ public void TrimExcess() /// without any further expansion of its backing storage. /// /// The new capacity. - /// public void TrimExcess(int capacity) { if (capacity < _buffer.Length) @@ -1478,11 +1432,8 @@ private void ResizeBuffer(int capacity) T[]? oldRentedBuffer = _rentedBuffer; AsSpan().CopyTo(newRentedBuffer); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _buffer = _rentedBuffer = newRentedBuffer; if (oldRentedBuffer is not null) diff --git a/src/Spanned/Collections/Generic/ValueQueue.cs b/src/Spanned/Collections/Generic/ValueQueue.cs index 173a226..ae5edc9 100644 --- a/src/Spanned/Collections/Generic/ValueQueue.cs +++ b/src/Spanned/Collections/Generic/ValueQueue.cs @@ -46,7 +46,6 @@ public ValueQueue(Span buffer) /// is empty and has the specified initial capacity. /// /// The number of elements that the new queue can initially store. - /// public ValueQueue(int capacity) { _rentedBuffer = ArrayPool.Shared.Rent(capacity); @@ -72,7 +71,6 @@ public ValueQueue(scoped ReadOnlySpan collection) /// /// The collection whose elements are copied to the new queue. /// The number of elements that the new queue can initially store. - /// public ValueQueue(scoped ReadOnlySpan collection, int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity, collection.Length); @@ -129,20 +127,19 @@ public ValueQueue(IEnumerable collection) /// The queue to be converted. /// A span covering the content of the . public static implicit operator ReadOnlySpan(ValueQueue queue) - => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(queue._buffer), queue._head), queue._count); + => queue._buffer.Slice(queue._head, queue._count); /// /// Returns a span that represents the content of the . /// /// A span covering the content of the . - public readonly Span AsSpan() => MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), _head), _count); + public readonly Span AsSpan() => _buffer.Slice(_head, _count); /// /// Returns a span that represents a segment of the queue starting from the specified index. /// /// The start index of the segment. /// A span covering the segment of the queue. - /// public readonly Span AsSpan(int start) => AsSpan(start, _count - start); /// @@ -151,13 +148,11 @@ public static implicit operator ReadOnlySpan(ValueQueue queue) /// The start index of the segment. /// The length of the segment. /// A span covering the segment of the queue. - /// public readonly Span AsSpan(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - // Skip additional bound checks. - return MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), _head + start), length); + return _buffer.Slice(_head + start, length); } /// @@ -188,7 +183,6 @@ public Span AsCapacitySpan() /// /// The number of elements contained in the . /// - /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -208,7 +202,6 @@ public int Count /// The total number of elements the internal data structure can hold /// without resizing. /// - /// public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -229,7 +222,6 @@ public int Capacity /// /// The zero-based index of the element to get or set. /// The element at the specified index. - /// public readonly ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -267,7 +259,6 @@ internal readonly T[] DebuggerItems /// /// The new capacity of this queue. /// The minimum capacity to ensure. - /// public int EnsureCapacity(int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity); @@ -332,7 +323,6 @@ private void GrowOrCompactAndEnqueue(T item) /// /// The length of the span to reserve. /// A span that represents the reserved space. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span EnqueueSpan(int length) { @@ -443,7 +433,6 @@ public void EnqueueRange(IEnumerable collection) /// /// The zero-based index of the queue to insert the element at. /// The object to be added to the . - /// public void Enqueue(int index, T item) { if ((uint)index > (uint)_count) @@ -476,7 +465,6 @@ public void Enqueue(int index, T item) /// The collection can contain elements that are null, if type /// is a reference type. /// - /// public void EnqueueRange(int index, scoped ReadOnlySpan collection) { if ((uint)index > (uint)_count) @@ -554,7 +542,6 @@ public void EnqueueRange(int index, IEnumerable collection) /// /// The object at the beginning of the . /// - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly T Peek() { @@ -603,7 +590,6 @@ public readonly bool TryPeek([MaybeNullWhen(false)] out T result) /// The object at the specified position of the . /// /// The element at the specified index. - /// public readonly T Peek(int index) => this[index]; /// @@ -643,7 +629,6 @@ public readonly bool TryPeek(int index, [MaybeNullWhen(false)] out T result) /// /// The zero-based index of the starting element to copy. /// A span to which the elements are copied. - /// public readonly void Peek(int start, scoped Span result) { if (!TryPeek(start, result)) @@ -680,7 +665,6 @@ public readonly bool TryPeek(int start, scoped Span result) /// /// The object removed from the beginning of the . /// - /// public T Dequeue() { Span buffer = _buffer; @@ -696,11 +680,8 @@ public T Dequeue() _head = head + 1; T item = Unsafe.Add(ref MemoryMarshal.GetReference(buffer), head); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - Unsafe.Add(ref MemoryMarshal.GetReference(buffer), head) = default!; - } + // Clear the element so that the GC can reclaim its reference. + Unsafe.Add(ref MemoryMarshal.GetReference(buffer), head) = default!; return item; } @@ -734,11 +715,8 @@ public bool TryDequeue([MaybeNullWhen(false)] out T result) _head = head + 1; result = Unsafe.Add(ref MemoryMarshal.GetReference(buffer), head); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - Unsafe.Add(ref MemoryMarshal.GetReference(buffer), head) = default!; - } + // Clear the element so that the GC can reclaim its reference. + Unsafe.Add(ref MemoryMarshal.GetReference(buffer), head) = default!; return true; } @@ -750,18 +728,15 @@ public bool TryDequeue([MaybeNullWhen(false)] out T result) /// /// The object removed from the specified position of the . /// - /// public T Dequeue(int index) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidIndex(index, _count); int itemIndex = _head + index; T item = _buffer[itemIndex]; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - _buffer[itemIndex] = default!; - } + + // Clear the element so that the GC can reclaim its reference. + _buffer[itemIndex] = default!; _buffer.Slice(itemIndex + 1, _count - index - 1).CopyTo(_buffer.Slice(itemIndex)); _count--; @@ -795,11 +770,9 @@ public bool TryDequeue(int index, [MaybeNullWhen(false)] out T result) } result = buffer[itemIndex]; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - buffer[itemIndex] = default!; - } + + // Clear the element so that the GC can reclaim its reference. + buffer[itemIndex] = default!; buffer.Slice(itemIndex + 1, _count - index - 1).CopyTo(buffer.Slice(itemIndex)); _count--; @@ -813,7 +786,6 @@ public bool TryDequeue(int index, [MaybeNullWhen(false)] out T result) /// /// The zero-based index of the starting element to dequeue. /// A span to which the elements are copied. - /// public void Dequeue(int start, scoped Span result) { if (!TryDequeue(start, result)) @@ -840,11 +812,9 @@ public bool TryDequeue(int start, scoped Span result) return false; buffer.Slice(segmentStart, result.Length).CopyTo(result); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - _buffer.Slice(segmentStart, result.Length).Clear(); - } + + // Clear the elements so that the GC can reclaim the references. + _buffer.Slice(segmentStart, result.Length).Clear(); buffer.Slice(segmentStart + result.Length, _count - start - result.Length).CopyTo(buffer.Slice(segmentStart)); _count = count - result.Length; @@ -858,7 +828,6 @@ public bool TryDequeue(int start, scoped Span result) /// /// The zero-based index of the starting element to dequeue. /// The number of elements to dequeue. - /// public void Dequeue(int start, int length) { if (!TryDequeue(start, length)) @@ -884,11 +853,8 @@ public bool TryDequeue(int start, int length) if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_count) return false; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - _buffer.Slice(segmentStart, length).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + _buffer.Slice(segmentStart, length).Clear(); buffer.Slice(segmentStart + length, _count - start - length).CopyTo(buffer.Slice(segmentStart)); _count = count - length; @@ -915,11 +881,8 @@ public bool TryDequeue(int start, int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _count = 0; } @@ -932,7 +895,6 @@ public void Clear() /// /// A shallow copy of a range of elements in the source . /// - /// public readonly ValueQueue Slice(int start, int length) { // This method is needed for the slicing syntax ([i..n]) to work. @@ -965,7 +927,6 @@ public readonly ValueQueue Slice(int start, int length) /// The destination of the elements copied from . /// /// The zero-based index in the destination at which copying begins. - /// public readonly void CopyTo(T[] destination, int destinationStart) => CopyTo(0, destination, destinationStart, _count); /// @@ -978,7 +939,6 @@ public readonly ValueQueue Slice(int start, int length) /// /// The number of elements to copy. /// - /// public readonly void CopyTo(int start, scoped Span destination, int length) => AsSpan(start, length).CopyTo(destination); /// @@ -990,7 +950,6 @@ public readonly ValueQueue Slice(int start, int length) /// /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. - /// public readonly void CopyTo(int start, T[] destination, int destinationStart, int length) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1033,7 +992,6 @@ public readonly bool TryCopyTo(T[] destination, out int written) /// The destination of the elements copied from . /// The zero-based index in the destination at which copying begins. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(T[] destination, int destinationStart, out int written) => TryCopyTo(0, destination, destinationStart, _count, out written); @@ -1044,7 +1002,6 @@ public readonly bool TryCopyTo(T[] destination, int destinationStart, out int wr /// The destination of the elements copied from . /// The number of elements to copy. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(int start, scoped Span destination, int length, out int written) { if (AsSpan(start, length).TryCopyTo(destination)) @@ -1069,7 +1026,6 @@ public readonly bool TryCopyTo(int start, scoped Span destination, int length /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. /// The number of elements copied to the array. - /// public readonly bool TryCopyTo(int start, T[] destination, int destinationStart, int length, out int written) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1159,13 +1115,13 @@ public Queue ToQueue(bool dispose) [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Span.Enumerator GetEnumerator() => AsSpan().GetEnumerator(); - /// + /// [Obsolete("Equals(object) on ValueQueue will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueQueue will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() @@ -1177,11 +1133,8 @@ public override readonly int GetHashCode() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); if (_rentedBuffer is not null) { @@ -1208,7 +1161,6 @@ public void TrimExcess() /// without any further expansion of its backing storage. /// /// The new capacity. - /// public void TrimExcess(int capacity) { if (capacity < _buffer.Length) @@ -1264,11 +1216,8 @@ private void ResizeBuffer(int capacity) T[]? oldRentedBuffer = _rentedBuffer; AsSpan().CopyTo(newRentedBuffer); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _buffer = _rentedBuffer = newRentedBuffer; _head = 0; diff --git a/src/Spanned/Collections/Generic/ValueSet.cs b/src/Spanned/Collections/Generic/ValueSet.cs index a92f4a9..95ef704 100644 --- a/src/Spanned/Collections/Generic/ValueSet.cs +++ b/src/Spanned/Collections/Generic/ValueSet.cs @@ -99,7 +99,6 @@ public ValueSet(int capacity) /// The implementation to use when comparing values in the set, or /// null to use the default implementation for the set type. /// - /// public ValueSet(int capacity, IEqualityComparer? comparer) : this(comparer) { @@ -145,7 +144,6 @@ public ValueSet(scoped ReadOnlySpan collection, int capacity) /// The implementation to use when comparing values in the set, or /// null to use the default implementation for the set type. /// - /// public ValueSet(scoped ReadOnlySpan collection, int capacity, IEqualityComparer? comparer) : this(comparer) { @@ -191,7 +189,8 @@ public ValueSet(IEnumerable collection, IEqualityComparer? comparer) /// /// The set to be converted. /// A span covering the content of the . - public static implicit operator ReadOnlySpan(ValueSet set) => MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(set._buffer), set._count); + public static implicit operator ReadOnlySpan(ValueSet set) + => set._buffer.Slice(0, set._count); /// /// Returns a span that represents the content of the . @@ -200,7 +199,7 @@ public ValueSet(IEnumerable collection, IEqualityComparer? comparer) /// In the case of direct modification, the uniqueness of the elements is no longer guaranteed. /// /// A span covering the content of the . - public readonly Span AsSpan() => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_buffer), _count); + public readonly Span AsSpan() => _buffer.Slice(0, _count); /// /// Returns a span that represents a segment of the set starting from the specified index. @@ -210,7 +209,6 @@ public ValueSet(IEnumerable collection, IEqualityComparer? comparer) /// /// The start index of the segment. /// A span covering the segment of the set. - /// public readonly Span AsSpan(int start) => AsSpan(start, _count - start); /// @@ -222,13 +220,11 @@ public ValueSet(IEnumerable collection, IEqualityComparer? comparer) /// The start index of the segment. /// The length of the segment. /// A span covering the segment of the set. - /// public readonly Span AsSpan(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - // Skip additional bound checks. - return MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), start), length); + return _buffer.Slice(start, length); } /// @@ -253,7 +249,6 @@ public readonly Span AsSpan(int start, int length) /// /// The number of elements contained in the . /// - /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -270,7 +265,6 @@ public int Count /// The total number of elements the internal data structure can hold /// without resizing. /// - /// public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -300,7 +294,6 @@ public int Capacity /// /// The zero-based index of the element to get or set. /// The element at the specified index. - /// public readonly ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -338,7 +331,6 @@ internal readonly T[] DebuggerItems /// /// The new capacity of this set. /// The minimum capacity to ensure. - /// public int EnsureCapacity(int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity); @@ -469,11 +461,8 @@ public bool Remove(T item) if (index < 0) return false; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - _buffer[index] = default!; - } + // Clear the element so that the GC can reclaim its reference. + _buffer[index] = default!; _buffer.Slice(index + 1).CopyTo(_buffer.Slice(index)); _count--; @@ -523,17 +512,13 @@ internal int RemoveWhere(scoped ValueBitArray indices) /// Removes the element at the specified index of the . /// /// The zero-based index of the element to remove. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveAt(int index) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidIndex(index, _count); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - _buffer[index] = default!; - } + // Clear the element so that the GC can reclaim its reference. + _buffer[index] = default!; _buffer.Slice(index + 1).CopyTo(_buffer.Slice(index)); _count--; @@ -544,16 +529,12 @@ public void RemoveAt(int index) /// /// The zero-based starting index of the range of elements to remove. /// The number of elements to remove. - /// public void RemoveRange(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan(start, length).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan(start, length).Clear(); _buffer.Slice(start + length).CopyTo(_buffer.Slice(start)); _count -= length; @@ -573,11 +554,8 @@ public void RemoveRange(int start, int length) /// public void Clear() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _count = 0; } @@ -605,7 +583,6 @@ public void Clear() /// The zero-based starting index of the search. /// 0 (zero) is valid in an empty set. /// - /// public readonly int IndexOf(T item, int index) => IndexOf(item, index, _count - index); /// @@ -618,7 +595,6 @@ public void Clear() /// 0 (zero) is valid in an empty set. /// /// The number of elements in the section to search. - /// public readonly int IndexOf(T item, int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); @@ -1413,7 +1389,6 @@ private readonly (int FoundCount, int NotFoundCount) CompareElements(scoped Valu /// /// A shallow copy of a range of elements in the source . /// - /// public readonly ValueSet Slice(int start, int length) { // This method is needed for the slicing syntax ([i..n]) to work. @@ -1445,7 +1420,6 @@ public readonly ValueSet Slice(int start, int length) /// The destination of the elements copied from . /// /// The zero-based index in the destination at which copying begins. - /// public readonly void CopyTo(T[] destination, int destinationStart) => CopyTo(0, destination, destinationStart, _count); /// @@ -1458,7 +1432,6 @@ public readonly ValueSet Slice(int start, int length) /// /// The number of elements to copy. /// - /// public readonly void CopyTo(int start, scoped Span destination, int length) => AsSpan(start, length).CopyTo(destination); /// @@ -1467,7 +1440,6 @@ public readonly ValueSet Slice(int start, int length) /// /// The zero-based index in the destination at which copying begins. /// The length of the destination. - /// [EditorBrowsable(EditorBrowsableState.Never)] public readonly void CopyTo(T[] destination, int destinationStart, int destinationLength) { @@ -1491,7 +1463,6 @@ public readonly void CopyTo(T[] destination, int destinationStart, int destinati /// /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. - /// public readonly void CopyTo(int start, T[] destination, int destinationStart, int length) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1534,7 +1505,6 @@ public readonly bool TryCopyTo(T[] destination, out int written) /// The destination of the elements copied from . /// The zero-based index in the destination at which copying begins. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(T[] destination, int destinationStart, out int written) => TryCopyTo(0, destination, destinationStart, _count, out written); @@ -1545,7 +1515,6 @@ public readonly bool TryCopyTo(T[] destination, int destinationStart, out int wr /// The destination of the elements copied from . /// The number of elements to copy. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(int start, scoped Span destination, int length, out int written) { if (AsSpan(start, length).TryCopyTo(destination)) @@ -1567,7 +1536,6 @@ public readonly bool TryCopyTo(int start, scoped Span destination, int length /// The zero-based index in the destination at which copying begins. /// The length of the destination. /// The number of elements copied to the array. - /// [EditorBrowsable(EditorBrowsableState.Never)] public readonly bool TryCopyTo(T[] destination, int destinationStart, int destinationLength, out int written) { @@ -1592,7 +1560,6 @@ public readonly bool TryCopyTo(T[] destination, int destinationStart, int destin /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. /// The number of elements copied to the array. - /// public readonly bool TryCopyTo(int start, T[] destination, int destinationStart, int length, out int written) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1638,7 +1605,7 @@ public T[] ToArray(bool dispose) /// public readonly HashSet ToHashSet() { - HashSet set = new(_count, _comparer); + HashSet set = new(_comparer); for (int i = 0; i < _count; i++) set.Add(_buffer[i]); @@ -1676,13 +1643,13 @@ public HashSet ToHashSet(bool dispose) [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Span.Enumerator GetEnumerator() => AsSpan().GetEnumerator(); - /// + /// [Obsolete("Equals(object) on ValueSet will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueSet will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() @@ -1694,11 +1661,8 @@ public override readonly int GetHashCode() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); if (_rentedBuffer is not null) { @@ -1726,7 +1690,6 @@ public void TrimExcess() /// without any further expansion of its backing storage. /// /// The new capacity. - /// public void TrimExcess(int capacity) { if (capacity < _buffer.Length) @@ -1764,11 +1727,8 @@ private void ResizeBuffer(int capacity) T[]? oldRentedBuffer = _rentedBuffer; AsSpan().CopyTo(newRentedBuffer); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); _buffer = _rentedBuffer = newRentedBuffer; if (oldRentedBuffer is not null) diff --git a/src/Spanned/Collections/Generic/ValueStack.cs b/src/Spanned/Collections/Generic/ValueStack.cs index f54e888..d2c76fb 100644 --- a/src/Spanned/Collections/Generic/ValueStack.cs +++ b/src/Spanned/Collections/Generic/ValueStack.cs @@ -40,7 +40,6 @@ public ValueStack(Span buffer) /// is empty and has the specified initial capacity. /// /// The number of elements that the new stack can initially store. - /// public ValueStack(int capacity) { _rentedBuffer = ArrayPool.Shared.Rent(capacity); @@ -65,7 +64,6 @@ public ValueStack(scoped ReadOnlySpan collection) /// /// The collection whose elements are copied to the new stack. /// The number of elements that the new stack can initially store. - /// public ValueStack(scoped ReadOnlySpan collection, int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity, collection.Length); @@ -123,7 +121,7 @@ public ValueStack(IEnumerable collection) /// The stack to be converted. /// A span covering the content of the . public static implicit operator ReadOnlySpan(ValueStack stack) - => MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(stack._buffer), stack._buffer.Length - stack._count), stack._count); + => stack._buffer.Slice(stack._buffer.Length - stack._count, stack._count); /// /// Returns a span that represents the content of the . @@ -133,7 +131,7 @@ public static implicit operator ReadOnlySpan(ValueStack stack) /// /// A span covering the content of the . public readonly Span AsSpan() - => MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), _buffer.Length - _count), _count); + => _buffer.Slice(_buffer.Length - _count, _count); /// /// Returns a span that represents a segment of the stack starting from the specified index. @@ -144,7 +142,6 @@ public readonly Span AsSpan() /// /// The start index of the segment. /// A span covering the segment of the stack. - /// public readonly Span AsSpan(int start) => AsSpan(start, _count - start); /// @@ -157,13 +154,11 @@ public readonly Span AsSpan() /// The start index of the segment. /// The length of the segment. /// A span covering the segment of the stack. - /// public readonly Span AsSpan(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _count); - // Skip additional bound checks. - return MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), _buffer.Length - _count + start), length); + return _buffer.Slice(_buffer.Length - _count + start, length); } /// @@ -191,7 +186,6 @@ public readonly Span AsSpan(int start, int length) /// /// The number of elements contained in the . /// - /// public int Count { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -208,7 +202,6 @@ public int Count /// The total number of elements the internal data structure can hold /// without resizing. /// - /// public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -233,7 +226,6 @@ public int Capacity /// /// The zero-based index of the element to get or set. /// The element at the specified index. - /// public readonly ref T this[int index] { get @@ -270,7 +262,6 @@ internal readonly T[] DebuggerItems /// /// The new capacity of this stack. /// The minimum capacity to ensure. - /// public int EnsureCapacity(int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity); @@ -344,7 +335,6 @@ private void GrowAndPush(T item) /// /// The length of the span to reserve. /// A span that represents the reserved space. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span PushSpan(int length) { @@ -474,7 +464,6 @@ public void PushRange(IEnumerable collection) /// /// The value can be null for reference types. /// - /// public void Push(int index, T item) { if ((uint)index > (uint)_count) @@ -512,7 +501,6 @@ public void Push(int index, T item) /// The collection can contain elements that are null, if type /// is a reference type. /// - /// public void PushRange(int index, scoped ReadOnlySpan collection) { if ((uint)index > (uint)_count) @@ -663,7 +651,6 @@ public readonly bool TryPeek([MaybeNullWhen(false)] out T result) /// The object at the specified position of the . /// /// The element at the specified index. - /// public readonly T Peek(int index) => this[index]; /// @@ -712,7 +699,6 @@ public readonly bool TryPeek(int index, [MaybeNullWhen(false)] out T result) /// /// The zero-based index of the starting element to copy. /// A span to which the elements are copied. - /// public readonly void Peek(int start, scoped Span result) { if (!TryPeek(start, result)) @@ -752,7 +738,6 @@ public readonly bool TryPeek(int start, scoped Span result) /// /// The object removed from the top of the . /// - /// public T Pop() { Span buffer = _buffer; @@ -766,11 +751,9 @@ public T Pop() T item = buffer[index]; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - buffer[index] = default!; - } + + // Clear the element so that the GC can reclaim its reference. + buffer[index] = default!; _count = count - 1; @@ -803,11 +786,9 @@ public bool TryPop([MaybeNullWhen(false)] out T result) } result = buffer[index]; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - buffer[index] = default!; - } + + // Clear the element so that the GC can reclaim its reference. + buffer[index] = default!; _count = count - 1; @@ -825,7 +806,6 @@ public bool TryPop([MaybeNullWhen(false)] out T result) /// /// The object removed from the specified position of the . /// - /// public T Pop(int index) { Span buffer = _buffer; @@ -836,11 +816,9 @@ public T Pop(int index) ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidIndex(itemIndex, buffer.Length); T item = buffer[itemIndex]; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - buffer[itemIndex] = default!; - } + + // Clear the element so that the GC can reclaim its reference. + buffer[itemIndex] = default!; buffer.Slice(bufferStart, index).CopyTo(buffer.Slice(bufferStart + 1)); _count = count - 1; @@ -881,11 +859,9 @@ public bool TryPop(int index, [MaybeNullWhen(false)] out T result) result = buffer[itemIndex]; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the element so that the GC can reclaim its reference. - buffer[itemIndex] = default!; - } + + // Clear the element so that the GC can reclaim its reference. + buffer[itemIndex] = default!; buffer.Slice(bufferStart, index).CopyTo(buffer.Slice(bufferStart + 1)); _count = count - 1; @@ -903,7 +879,6 @@ public bool TryPop(int index, [MaybeNullWhen(false)] out T result) /// /// The zero-based index of the starting element to pop. /// A span to which the elements are copied. - /// public void Pop(int start, scoped Span result) { if (!TryPop(start, result)) @@ -935,11 +910,9 @@ public bool TryPop(int start, scoped Span result) buffer.Slice(segmentStart, result.Length).CopyTo(result); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - _buffer.Slice(segmentStart, result.Length).Clear(); - } + + // Clear the elements so that the GC can reclaim the references. + _buffer.Slice(segmentStart, result.Length).Clear(); buffer.Slice(bufferStart, start).CopyTo(buffer.Slice(bufferStart + result.Length)); _count = count - result.Length; @@ -957,7 +930,6 @@ public bool TryPop(int start, scoped Span result) /// /// The zero-based index of the starting element to pop. /// The number of elements to pop. - /// public void Pop(int start, int length) { if (!TryPop(start, length)) @@ -987,11 +959,8 @@ public bool TryPop(int start, int length) if ((ulong)(uint)segmentStart + (ulong)(uint)length > (ulong)(uint)buffer.Length) return false; - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - _buffer.Slice(segmentStart, length).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + _buffer.Slice(segmentStart, length).Clear(); buffer.Slice(bufferStart, start).CopyTo(buffer.Slice(bufferStart + length)); _count = count - length; @@ -1018,11 +987,8 @@ public bool TryPop(int start, int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - _buffer.Slice(_buffer.Length - _count, _count).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + _buffer.Slice(_buffer.Length - _count, _count).Clear(); _count = 0; } @@ -1039,7 +1005,6 @@ public void Clear() /// /// A shallow copy of a range of elements in the source . /// - /// public readonly ValueStack Slice(int start, int length) { // This method is needed for the slicing syntax ([i..n]) to work. @@ -1076,7 +1041,6 @@ public readonly ValueStack Slice(int start, int length) /// The destination of the elements copied from . /// /// The zero-based index in the destination at which copying begins. - /// public readonly void CopyTo(T[] destination, int destinationStart) => CopyTo(0, destination, destinationStart, _count); /// @@ -1089,7 +1053,6 @@ public readonly ValueStack Slice(int start, int length) /// /// The number of elements to copy. /// - /// public readonly void CopyTo(int start, scoped Span destination, int length) => AsSpan(start, length).CopyTo(destination); /// @@ -1101,7 +1064,6 @@ public readonly ValueStack Slice(int start, int length) /// /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. - /// public readonly void CopyTo(int start, T[] destination, int destinationStart, int length) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1144,7 +1106,6 @@ public readonly bool TryCopyTo(T[] destination, out int written) /// The destination of the elements copied from . /// The zero-based index in the destination at which copying begins. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(T[] destination, int destinationStart, out int written) => TryCopyTo(0, destination, destinationStart, _count, out written); @@ -1155,7 +1116,6 @@ public readonly bool TryCopyTo(T[] destination, int destinationStart, out int wr /// The destination of the elements copied from . /// The number of elements to copy. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(int start, scoped Span destination, int length, out int written) { if (AsSpan(start, length).TryCopyTo(destination)) @@ -1180,7 +1140,6 @@ public readonly bool TryCopyTo(int start, scoped Span destination, int length /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. /// The number of elements copied to the array. - /// public readonly bool TryCopyTo(int start, T[] destination, int destinationStart, int length, out int written) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -1266,13 +1225,13 @@ public Stack ToStack(bool dispose) [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Span.Enumerator GetEnumerator() => AsSpan().GetEnumerator(); - /// + /// [Obsolete("Equals(object) on ValueStack will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueStack will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() @@ -1284,11 +1243,8 @@ public override readonly int GetHashCode() [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - AsSpan().Clear(); - } + // Clear the elements so that the GC can reclaim the references. + AsSpan().Clear(); if (_rentedBuffer is not null) { @@ -1315,7 +1271,6 @@ public void TrimExcess() /// without any further expansion of its backing storage. /// /// The new capacity. - /// public void TrimExcess(int capacity) { if (capacity < _buffer.Length) @@ -1356,11 +1311,8 @@ private void ResizeBuffer(int capacity) Span to = newRentedBuffer.AsSpan(newRentedBuffer.Length - _count, _count); from.CopyTo(to); - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - from.Clear(); - } + // Clear the elements so that the GC can reclaim the references. + from.Clear(); _buffer = _rentedBuffer = newRentedBuffer; if (oldRentedBuffer is not null) diff --git a/src/Spanned/SpanOwner.cs b/src/Spanned/SpanOwner.cs index 3fd4651..6c2386d 100644 --- a/src/Spanned/SpanOwner.cs +++ b/src/Spanned/SpanOwner.cs @@ -138,13 +138,13 @@ public void Dispose() } } - /// + /// [Obsolete("Equals(object) on SpanOwner will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on SpanOwner will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() diff --git a/src/Spanned/Spanned.csproj b/src/Spanned/Spanned.csproj index a5bc4bf..07bebfc 100644 --- a/src/Spanned/Spanned.csproj +++ b/src/Spanned/Spanned.csproj @@ -1,7 +1,7 @@ - netstandard2.1 + netstandard2.0 @@ -31,6 +31,8 @@ + + diff --git a/src/Spanned/Spans.Contains.cs b/src/Spanned/Spans.Contains.cs index 5b084b1..54fc937 100644 --- a/src/Spanned/Spans.Contains.cs +++ b/src/Spanned/Spans.Contains.cs @@ -18,39 +18,6 @@ public static bool Contains(this scoped Span span, T value, IEqualityCompa { if (comparer is null || comparer == EqualityComparer.Default) { - if (typeof(T) == typeof(byte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (byte)(object)value!) >= 0; - - if (typeof(T) == typeof(sbyte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (sbyte)(object)value!) >= 0; - - if (typeof(T) == typeof(short)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (short)(object)value!) >= 0; - - if (typeof(T) == typeof(ushort)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ushort)(object)value!) >= 0; - - if (typeof(T) == typeof(int)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (int)(object)value!) >= 0; - - if (typeof(T) == typeof(uint)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (uint)(object)value!) >= 0; - - if (typeof(T) == typeof(long)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (long)(object)value!) >= 0; - - if (typeof(T) == typeof(ulong)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ulong)(object)value!) >= 0; - - if (typeof(T) == typeof(float)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (float)(object)value!) >= 0; - - if (typeof(T) == typeof(double)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (double)(object)value!) >= 0; - - if (typeof(T) == typeof(char)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (char)(object)value!) >= 0; - if (value is IEquatable) return IndexOfEquatable(ref MemoryMarshal.GetReference(span), span.Length, value) >= 0; } @@ -64,39 +31,6 @@ public static bool Contains(this scoped ReadOnlySpan span, T value, IEqual { if (comparer is null || comparer == EqualityComparer.Default) { - if (typeof(T) == typeof(byte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (byte)(object)value!) >= 0; - - if (typeof(T) == typeof(sbyte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (sbyte)(object)value!) >= 0; - - if (typeof(T) == typeof(short)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (short)(object)value!) >= 0; - - if (typeof(T) == typeof(ushort)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ushort)(object)value!) >= 0; - - if (typeof(T) == typeof(int)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (int)(object)value!) >= 0; - - if (typeof(T) == typeof(uint)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (uint)(object)value!) >= 0; - - if (typeof(T) == typeof(long)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (long)(object)value!) >= 0; - - if (typeof(T) == typeof(ulong)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ulong)(object)value!) >= 0; - - if (typeof(T) == typeof(float)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (float)(object)value!) >= 0; - - if (typeof(T) == typeof(double)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (double)(object)value!) >= 0; - - if (typeof(T) == typeof(char)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (char)(object)value!) >= 0; - if (value is IEquatable) return IndexOfEquatable(ref MemoryMarshal.GetReference(span), span.Length, value) >= 0; } diff --git a/src/Spanned/Spans.Extremum.cs b/src/Spanned/Spans.Extremum.cs deleted file mode 100644 index e24fe09..0000000 --- a/src/Spanned/Spans.Extremum.cs +++ /dev/null @@ -1,94 +0,0 @@ -namespace Spanned; - -public static partial class Spans -{ - /// - /// Performs a vectorized search for the extremum in a memory block. - /// - /// The type of the value. - /// The extremum type. - /// The reference to the start of the search space. - /// The length of the search space. - /// The extremum value. - /// . - /// contains no elements. - private static T Extremum(ref T searchSpace, int length) - where T : struct - where TExtremum : struct, IExtremum - { - if (length == 0) - ThrowHelper.ThrowInvalidOperationException_NoElements(); - - // Note, we use `<=` instead of `<`, because in the end we need to - // manually process every lane of the resulting vector. - // Therefore, when `length == Vector.Count` a vectorized solution - // would just end up performing unnecessary operations, becoming - // slower than the regular loop without those. - if (!Vector.IsHardwareAccelerated || length <= Vector.Count) - { - ref T current = ref searchSpace; - ref T end = ref Unsafe.Add(ref current, length); - - T extremumValue = current; - current = ref Unsafe.Add(ref current, (nint)1); - - while (Unsafe.IsAddressLessThan(ref current, ref end)) - { - if (default(TExtremum).Compare(current, extremumValue)) - extremumValue = current; - - current = ref Unsafe.Add(ref current, (nint)1); - } - return extremumValue; - } - else - { - ref T current = ref Unsafe.Add(ref searchSpace, (nint)Vector.Count); - ref T lastVectorStart = ref Unsafe.Add(ref searchSpace, length - Vector.Count); - Vector extremum = new(MemoryMarshal.CreateSpan(ref searchSpace, Vector.Count)); - - while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart)) - { - extremum = default(TExtremum).Compare(extremum, new(MemoryMarshal.CreateSpan(ref current, Vector.Count))); - current = ref Unsafe.Add(ref current, (nint)Vector.Count); - } - extremum = default(TExtremum).Compare(extremum, new(MemoryMarshal.CreateSpan(ref lastVectorStart, Vector.Count))); - - T extremumValue = extremum[0]; - for (int i = 1; i < Vector.Count; i++) - { - if (default(TExtremum).Compare(extremum[i], extremumValue)) - extremumValue = extremum[i]; - } - return extremumValue; - } - } - - /// - /// Defines methods for comparing values or vectors of type - /// to determine the extremum (minimum or maximum). - /// - /// The type of values to compare. - private interface IExtremum where T : struct - { - /// - /// Compares two values of type to determine which one - /// qualifies as the extremum. - /// - /// The value to compare with . - /// The value to compare with . - /// - /// true if qualifies as the extremum; - /// otherwise, false. - /// - bool Compare(T left, T right); - - /// - /// Computes the extremum of two vectors on a per-element basis. - /// - /// The vector to compare with . - /// The vector to compare with . - /// A vector whose elements are the extremum of the corresponding elements in left and right. - Vector Compare(Vector left, Vector right); - } -} diff --git a/src/Spanned/Spans.IndexOf.cs b/src/Spanned/Spans.IndexOf.cs index 55caae1..b4785a1 100644 --- a/src/Spanned/Spans.IndexOf.cs +++ b/src/Spanned/Spans.IndexOf.cs @@ -18,39 +18,6 @@ public static int IndexOf(this scoped Span span, T value, IEqualityCompare { if (comparer is null || comparer == EqualityComparer.Default) { - if (typeof(T) == typeof(byte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (byte)(object)value!); - - if (typeof(T) == typeof(sbyte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (sbyte)(object)value!); - - if (typeof(T) == typeof(short)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (short)(object)value!); - - if (typeof(T) == typeof(ushort)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ushort)(object)value!); - - if (typeof(T) == typeof(int)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (int)(object)value!); - - if (typeof(T) == typeof(uint)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (uint)(object)value!); - - if (typeof(T) == typeof(long)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (long)(object)value!); - - if (typeof(T) == typeof(ulong)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ulong)(object)value!); - - if (typeof(T) == typeof(float)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (float)(object)value!); - - if (typeof(T) == typeof(double)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (double)(object)value!); - - if (typeof(T) == typeof(char)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (char)(object)value!); - if (value is IEquatable) return IndexOfEquatable(ref MemoryMarshal.GetReference(span), span.Length, value); } @@ -64,39 +31,6 @@ public static int IndexOf(this scoped ReadOnlySpan span, T value, IEqualit { if (comparer is null || comparer == EqualityComparer.Default) { - if (typeof(T) == typeof(byte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (byte)(object)value!); - - if (typeof(T) == typeof(sbyte)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (sbyte)(object)value!); - - if (typeof(T) == typeof(short)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (short)(object)value!); - - if (typeof(T) == typeof(ushort)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ushort)(object)value!); - - if (typeof(T) == typeof(int)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (int)(object)value!); - - if (typeof(T) == typeof(uint)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (uint)(object)value!); - - if (typeof(T) == typeof(long)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (long)(object)value!); - - if (typeof(T) == typeof(ulong)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (ulong)(object)value!); - - if (typeof(T) == typeof(float)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (float)(object)value!); - - if (typeof(T) == typeof(double)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (double)(object)value!); - - if (typeof(T) == typeof(char)) - return MemoryExtensions.IndexOf(UnsafeCast(span), (char)(object)value!); - if (value is IEquatable) return IndexOfEquatable(ref MemoryMarshal.GetReference(span), span.Length, value); } diff --git a/src/Spanned/Spans.LastIndexOf.cs b/src/Spanned/Spans.LastIndexOf.cs index 412c3ae..c4d5267 100644 --- a/src/Spanned/Spans.LastIndexOf.cs +++ b/src/Spanned/Spans.LastIndexOf.cs @@ -18,39 +18,6 @@ public static int LastIndexOf(this scoped Span span, T value, IEqualityCom { if (comparer is null || comparer == EqualityComparer.Default) { - if (typeof(T) == typeof(byte)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (byte)(object)value!); - - if (typeof(T) == typeof(sbyte)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (sbyte)(object)value!); - - if (typeof(T) == typeof(short)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (short)(object)value!); - - if (typeof(T) == typeof(ushort)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (ushort)(object)value!); - - if (typeof(T) == typeof(int)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (int)(object)value!); - - if (typeof(T) == typeof(uint)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (uint)(object)value!); - - if (typeof(T) == typeof(long)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (long)(object)value!); - - if (typeof(T) == typeof(ulong)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (ulong)(object)value!); - - if (typeof(T) == typeof(float)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (float)(object)value!); - - if (typeof(T) == typeof(double)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (double)(object)value!); - - if (typeof(T) == typeof(char)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (char)(object)value!); - if (value is IEquatable) return LastIndexOfEquatable(ref MemoryMarshal.GetReference(span), span.Length, value); } @@ -64,39 +31,6 @@ public static int LastIndexOf(this scoped ReadOnlySpan span, T value, IEqu { if (comparer is null || comparer == EqualityComparer.Default) { - if (typeof(T) == typeof(byte)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (byte)(object)value!); - - if (typeof(T) == typeof(sbyte)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (sbyte)(object)value!); - - if (typeof(T) == typeof(short)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (short)(object)value!); - - if (typeof(T) == typeof(ushort)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (ushort)(object)value!); - - if (typeof(T) == typeof(int)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (int)(object)value!); - - if (typeof(T) == typeof(uint)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (uint)(object)value!); - - if (typeof(T) == typeof(long)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (long)(object)value!); - - if (typeof(T) == typeof(ulong)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (ulong)(object)value!); - - if (typeof(T) == typeof(float)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (float)(object)value!); - - if (typeof(T) == typeof(double)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (double)(object)value!); - - if (typeof(T) == typeof(char)) - return MemoryExtensions.LastIndexOf(UnsafeCast(span), (char)(object)value!); - if (value is IEquatable) return LastIndexOfEquatable(ref MemoryMarshal.GetReference(span), span.Length, value); } diff --git a/src/Spanned/Spans.LongSum.cs b/src/Spanned/Spans.LongSum.cs index 5c2180e..01842d0 100644 --- a/src/Spanned/Spans.LongSum.cs +++ b/src/Spanned/Spans.LongSum.cs @@ -2,44 +2,6 @@ namespace Spanned; public static partial class Spans { - /// - /// A vectorized solution to compute the sum of the values in the specified memory block. - /// - /// The reference to the start of the memory block. - /// The length of the memory block. - /// The sum of the values in the memory block. - private static long LongSum(ref int searchSpace, int length) - { - long sum = 0; - ref int current = ref searchSpace; - ref int end = ref Unsafe.Add(ref current, length); - - if (Vector.IsHardwareAccelerated && length >= Vector.Count) - { - Vector sums = Vector.Zero; - ref int lastVectorStart = ref Unsafe.Add(ref current, length - Vector.Count); - - do - { - Vector.Widen(new(MemoryMarshal.CreateSpan(ref current, Vector.Count)), out Vector currentA, out Vector currentB); - sums += currentA + currentB; - current = ref Unsafe.Add(ref current, (nint)Vector.Count); - } - while (!Unsafe.IsAddressGreaterThan(ref current, ref lastVectorStart)); - - for (int i = 0; i < Vector.Count; i++) - sum += sums[i]; - } - - while (Unsafe.IsAddressLessThan(ref current, ref end)) - { - sum += current; - current = ref Unsafe.Add(ref current, (nint)1); - } - - return sum; - } - /// /// Computes the sum of a span of values. /// @@ -147,10 +109,24 @@ public static ulong LongSum(this scoped ReadOnlySpan span) /// /// A span of values to calculate the sum of. /// The sum of the values in the span. - public static long LongSum(this scoped Span span) => LongSum(ref MemoryMarshal.GetReference(span), span.Length); + public static long LongSum(this scoped Span span) + { + long sum = 0; + for (int i = 0; i < span.Length; i++) + sum += span[i]; + + return sum; + } /// - public static long LongSum(this scoped ReadOnlySpan span) => LongSum(ref MemoryMarshal.GetReference(span), span.Length); + public static long LongSum(this scoped ReadOnlySpan span) + { + long sum = 0; + for (int i = 0; i < span.Length; i++) + sum += span[i]; + + return sum; + } /// /// Computes the sum of a span of values. @@ -184,10 +160,10 @@ public static ulong LongSum(this scoped ReadOnlySpan span) /// A span of values to calculate the sum of. /// The sum of the values in the span. /// The addition operation in a checked context resulted in an overflow. - public static long LongSum(this scoped Span span) => Sum, Int64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static long LongSum(this scoped Span span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static long LongSum(this scoped ReadOnlySpan span) => Sum, Int64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static long LongSum(this scoped ReadOnlySpan span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -230,10 +206,10 @@ public static double LongSum(this scoped ReadOnlySpan span) /// /// A span of values to calculate the sum of. /// The sum of the values in the span. - public static double LongSum(this scoped Span span) => Sum, DoubleNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static double LongSum(this scoped Span span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static double LongSum(this scoped ReadOnlySpan span) => Sum, DoubleNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static double LongSum(this scoped ReadOnlySpan span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. diff --git a/src/Spanned/Spans.Max.cs b/src/Spanned/Spans.Max.cs index e93deb5..b02800d 100644 --- a/src/Spanned/Spans.Max.cs +++ b/src/Spanned/Spans.Max.cs @@ -23,30 +23,6 @@ public static partial class Spans comparer = null; } - if (typeof(T) == typeof(byte) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(sbyte) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(short) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(ushort) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(int) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(uint) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(long) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(ulong) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - if (length == 0) { if (default(T) is null) @@ -102,72 +78,16 @@ public static partial class Spans return maxValue; } - /// - /// Returns the maximum value in a memory block. - /// - /// - /// If is an IEEE 754 floating-point type, this method does not account - /// for NaN values, so it will produce incorrect results if a NaN is present in the span. - /// Use with caution and ensure the span does not contain any NaN values if accurate maximum - /// computation is required. - /// - /// The type of the value. - /// The reference to the start of the search space. - /// The length of the search space. - /// The maximum value in the memory block. - /// contains no elements. - private static T? UnsafeMax(ref T searchSpace, int length) - { - if (typeof(T) == typeof(float)) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(double)) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - return Max(ref searchSpace, length, comparer: null); - } - - /// - /// Provides methods for calculating the maximum value between two values or vectors - /// of type . - /// - /// The type of values to compare. - private readonly struct Maximum : IExtremum where T : struct, IComparable - { - /// - /// Compares two values to determine which is less. - /// - /// The value to compare with . - /// The value to compare with . - /// - /// true if is less than ; - /// otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Compare(T left, T right) => left.CompareTo(right) > 0; - - /// - /// Computes the minimum of two vectors on a per-element basis. - /// - /// The vector to compare with . - /// The vector to compare with . - /// - /// A vector whose elements are the minimum of the corresponding elements in left and right. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector Compare(Vector left, Vector right) => Vector.Max(left, right); - } - /// /// Returns the maximum value in a span of values. /// /// A span of values to determine the maximum value of. /// The maximum value in the span. /// contains no elements. - public static byte Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static byte Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static byte Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static byte Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. @@ -175,21 +95,21 @@ public static partial class Spans /// A span of values to determine the maximum value of. /// [CLSCompliant(false)] - public static sbyte Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static sbyte Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. /// /// A span of values to determine the maximum value of. /// - public static short Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static short Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static short Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static short Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. @@ -197,21 +117,21 @@ public static partial class Spans /// A span of values to determine the maximum value of. /// [CLSCompliant(false)] - public static ushort Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ushort Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static ushort Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ushort Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. /// /// A span of values to determine the maximum value of. /// - public static int Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static int Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static int Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static int Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. @@ -219,21 +139,21 @@ public static partial class Spans /// A span of values to determine the maximum value of. /// [CLSCompliant(false)] - public static uint Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static uint Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static uint Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static uint Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. /// /// A span of values to determine the maximum value of. /// - public static long Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static long Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static long Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static long Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. @@ -241,11 +161,11 @@ public static partial class Spans /// A span of values to determine the maximum value of. /// [CLSCompliant(false)] - public static ulong Max(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ulong Max(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static ulong Max(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ulong Max(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. @@ -316,20 +236,20 @@ public static partial class Spans /// A span of values to determine the maximum value of. /// The maximum value in the span. /// contains no elements. - public static float UnsafeMax(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static float UnsafeMax(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static float UnsafeMax(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static float UnsafeMax(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a span of values. /// /// A span of values to determine the maximum value of. /// - public static double UnsafeMax(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static double UnsafeMax(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static double UnsafeMax(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static double UnsafeMax(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the maximum value in a generic span. @@ -344,8 +264,8 @@ public static partial class Spans /// A span of values to determine the maximum value of. /// The maximum value in the span. /// contains no elements. - public static T? UnsafeMax(this scoped Span span) => UnsafeMax(ref MemoryMarshal.GetReference(span), span.Length); + public static T? UnsafeMax(this scoped Span span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static T? UnsafeMax(this scoped ReadOnlySpan span) => UnsafeMax(ref MemoryMarshal.GetReference(span), span.Length); + public static T? UnsafeMax(this scoped ReadOnlySpan span) => Max(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); } diff --git a/src/Spanned/Spans.Min.cs b/src/Spanned/Spans.Min.cs index 6a382ae..5c733c6 100644 --- a/src/Spanned/Spans.Min.cs +++ b/src/Spanned/Spans.Min.cs @@ -23,30 +23,6 @@ public static partial class Spans comparer = null; } - if (typeof(T) == typeof(byte) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(sbyte) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(short) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(ushort) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(int) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(uint) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(long) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(ulong) && comparer is null) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - if (length == 0) { if (default(T) is null) @@ -103,72 +79,16 @@ public static partial class Spans return minValue; } - /// - /// Returns the minimum value in a memory block. - /// - /// - /// If is an IEEE 754 floating-point type, this method does not account - /// for NaN values, so it will produce incorrect results if a NaN is present in the span. - /// Use with caution and ensure the span does not contain any NaN values if accurate minimum - /// computation is required. - /// - /// The type of the value. - /// The reference to the start of the search space. - /// The length of the search space. - /// The minimum value in the memory block. - /// contains no elements. - private static T? UnsafeMin(ref T searchSpace, int length) - { - if (typeof(T) == typeof(float)) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - if (typeof(T) == typeof(double)) - return (T)(object)Extremum>(ref Unsafe.As(ref searchSpace), length); - - return Min(ref searchSpace, length, comparer: null); - } - - /// - /// Provides methods for calculating the minimum value between two values or vectors - /// of type . - /// - /// The type of values to compare. - private readonly struct Minimum : IExtremum where T : struct, IComparable - { - /// - /// Compares two values to determine which is less. - /// - /// The value to compare with . - /// The value to compare with . - /// - /// true if is less than ; - /// otherwise, false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Compare(T left, T right) => left.CompareTo(right) < 0; - - /// - /// Computes the minimum of two vectors on a per-element basis. - /// - /// The vector to compare with . - /// The vector to compare with . - /// - /// A vector whose elements are the minimum of the corresponding elements in left and right. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector Compare(Vector left, Vector right) => Vector.Min(left, right); - } - /// /// Returns the minimum value in a span of values. /// /// A span of values to determine the minimum value of. /// The minimum value in the span. /// contains no elements. - public static byte Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static byte Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static byte Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static byte Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. @@ -176,21 +96,21 @@ public static partial class Spans /// A span of values to determine the minimum value of. /// [CLSCompliant(false)] - public static sbyte Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static sbyte Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. /// /// A span of values to determine the minimum value of. /// - public static short Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static short Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static short Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static short Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. @@ -198,21 +118,21 @@ public static partial class Spans /// A span of values to determine the minimum value of. /// [CLSCompliant(false)] - public static ushort Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ushort Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static ushort Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ushort Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. /// /// A span of values to determine the minimum value of. /// - public static int Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static int Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static int Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static int Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. @@ -220,21 +140,21 @@ public static partial class Spans /// A span of values to determine the minimum value of. /// [CLSCompliant(false)] - public static uint Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static uint Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static uint Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static uint Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. /// /// A span of values to determine the minimum value of. /// - public static long Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static long Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static long Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static long Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. @@ -242,11 +162,11 @@ public static partial class Spans /// A span of values to determine the minimum value of. /// [CLSCompliant(false)] - public static ulong Min(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ulong Min(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// [CLSCompliant(false)] - public static ulong Min(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static ulong Min(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. @@ -317,20 +237,20 @@ public static partial class Spans /// A span of values to determine the minimum value of. /// The minimum value in the span. /// contains no elements. - public static float UnsafeMin(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static float UnsafeMin(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static float UnsafeMin(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static float UnsafeMin(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a span of values. /// /// A span of values to determine the minimum value of. /// - public static double UnsafeMin(this scoped Span span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static double UnsafeMin(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static double UnsafeMin(this scoped ReadOnlySpan span) => Extremum>(ref MemoryMarshal.GetReference(span), span.Length); + public static double UnsafeMin(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// /// Returns the minimum value in a generic span. @@ -345,8 +265,8 @@ public static partial class Spans /// A span of values to determine the minimum value of. /// The minimum value in the span. /// contains no elements. - public static T? UnsafeMin(this scoped Span span) => UnsafeMin(ref MemoryMarshal.GetReference(span), span.Length); + public static T? UnsafeMin(this scoped Span span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); /// - public static T? UnsafeMin(this scoped ReadOnlySpan span) => UnsafeMin(ref MemoryMarshal.GetReference(span), span.Length); + public static T? UnsafeMin(this scoped ReadOnlySpan span) => Min(ref MemoryMarshal.GetReference(span), span.Length, comparer: null); } diff --git a/src/Spanned/Spans.RemoveWhere.cs b/src/Spanned/Spans.RemoveWhere.cs index ae8f58d..1642dd5 100644 --- a/src/Spanned/Spans.RemoveWhere.cs +++ b/src/Spanned/Spans.RemoveWhere.cs @@ -51,11 +51,8 @@ internal static int RemoveWhere(this scoped Span span, Predicate predic } } - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - span.Slice(freeIndex, span.Length - freeIndex).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + span.Slice(freeIndex, span.Length - freeIndex).Clear(); return span.Length - freeIndex; } @@ -105,11 +102,8 @@ internal static int RemoveWhere(this scoped Span span, scoped ValueBitArra } } - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - // Clear the elements so that the GC can reclaim the references. - span.Slice(freeIndex, span.Length - freeIndex).Clear(); - } + // Clear the elements so that the GC can reclaim the references. + span.Slice(freeIndex, span.Length - freeIndex).Clear(); return span.Length - freeIndex; } diff --git a/src/Spanned/Spans.Sum.cs b/src/Spanned/Spans.Sum.cs index 4a419f2..0df611a 100644 --- a/src/Spanned/Spans.Sum.cs +++ b/src/Spanned/Spans.Sum.cs @@ -2,77 +2,6 @@ namespace Spanned; public static partial class Spans { - /// - /// A vectorized solution to compute the sum of the values in the specified memory block. - /// - /// The type of the values. - /// The type for tracking overflow in vectorized arithmetic operations. - /// The type of the values. - /// The reference to the start of the memory block. - /// The length of the memory block. - /// The sum of the values in the memory block. - /// is not supported. - /// The addition operation in a checked context resulted in an overflow. - private static T Sum(ref T searchSpace, int length) - where T : struct - where TOverflowTracker : struct, IOverflowTracker - where TNumber : struct, INumber - { - ref T current = ref searchSpace; - ref T end = ref Unsafe.Add(ref current, length); - T sum = default; - - // Note, we use `<=` instead of `<`, because in the end we need to - // manually process every lane of the resulting vector. - // Therefore, when `length == Vector.Count` a vectorized solution - // would just end up performing unnecessary operations, becoming - // slower than the regular loop without those. - if (Vector.IsHardwareAccelerated && length > Vector.Count) - { - ref T lastVectorStart = ref Unsafe.Add(ref searchSpace, length - Vector.Count); - Vector overflowTrackerState = Vector.Zero; - Vector sums = Vector.Zero; - - do - { - Vector currentVector = new(MemoryMarshal.CreateSpan(ref current, Vector.Count)); - Vector newSums = sums + currentVector; - overflowTrackerState = default(TOverflowTracker).UpdateState(overflowTrackerState, sums, currentVector, newSums); - - sums = newSums; - current = ref Unsafe.Add(ref current, (nint)Vector.Count); - } - while (!Unsafe.IsAddressGreaterThan(ref current, ref lastVectorStart)); - - if (default(TOverflowTracker).HasOverflow(overflowTrackerState)) - ThrowHelper.ThrowOverflowException(); - - if (default(TOverflowTracker).IsSupported) - { - for (int i = 0; i < Vector.Count; i++) - sum = default(TNumber).AddChecked(sum, sums[i]); - } - else - { - for (int i = 0; i < Vector.Count; i++) - sum = default(TNumber).Add(sum, sums[i]); - } - } - - if (default(TOverflowTracker).IsSupported) - { - for (; Unsafe.IsAddressLessThan(ref current, ref end); current = ref Unsafe.Add(ref current, (nint)1)) - sum = default(TNumber).AddChecked(sum, current); - } - else - { - for (; Unsafe.IsAddressLessThan(ref current, ref end); current = ref Unsafe.Add(ref current, (nint)1)) - sum = default(TNumber).Add(sum, current); - } - - return sum; - } - /// /// Computes the sum of the values in the specified memory block. /// @@ -116,82 +45,6 @@ private static T UnsafeSum(ref T searchSpace, int length) return sum; } - /// - /// Defines an interface for tracking overflow in vectorized arithmetic operations on numeric types. - /// - /// The numeric type. - private interface IOverflowTracker - where T : struct - { - /// - /// Gets a value indicating whether overflow tracking is supported for the specified numeric type. - /// - bool IsSupported { get; } - - /// - /// Determines whether the overflow has occurred. - /// - /// The overflow tracking state. - /// - /// true if the overflow has occurred; otherwise, false. - /// - bool HasOverflow(Vector state); - - /// - /// Updates the overflow tracking state. - /// - /// The current overflow tracking state. - /// The left operand of the vector operation. - /// The right operand of the vector operation. - /// The result of the vector operation. - /// The updated overflow tracking state. - Vector UpdateState(Vector state, Vector leftOperand, Vector rightOperand, Vector result); - } - - /// - /// Represents a stub for an overflow tracker that does not perform any checks. - /// - /// The numeric type. - private readonly struct NullOverflowTracker : IOverflowTracker - where T : struct - { - public bool IsSupported - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool HasOverflow(Vector state) => false; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector UpdateState(Vector state, Vector leftOperand, Vector rightOperand, Vector result) => state; - } - - /// - /// Represents an overflow tracker for signed integer types. - /// - /// The numeric type. - /// The numeric type. - private readonly struct SignedIntegerOverflowTracker : IOverflowTracker - where T : struct - where TNumber : struct, INumber - { - public bool IsSupported - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool HasOverflow(Vector state) - => (state & new Vector(default(TNumber).MinValue)) != Vector.Zero; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Vector UpdateState(Vector state, Vector leftOperand, Vector rightOperand, Vector result) - => state | ((result ^ leftOperand) & (result ^ rightOperand)); - } - /// /// Computes the sum of a span of values. /// @@ -209,21 +62,21 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// A span of values to calculate the sum of. /// [CLSCompliant(false)] - public static sbyte Sum(this scoped Span span) => Sum, SByteNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte Sum(this scoped Span span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// [CLSCompliant(false)] - public static sbyte Sum(this scoped ReadOnlySpan span) => Sum, SByteNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte Sum(this scoped ReadOnlySpan span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. /// /// A span of values to calculate the sum of. /// - public static short Sum(this scoped Span span) => Sum, Int16Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static short Sum(this scoped Span span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static short Sum(this scoped ReadOnlySpan span) => Sum, Int16Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static short Sum(this scoped ReadOnlySpan span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -242,10 +95,10 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// /// A span of values to calculate the sum of. /// - public static int Sum(this scoped Span span) => Sum, Int32Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static int Sum(this scoped Span span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static int Sum(this scoped ReadOnlySpan span) => Sum, Int32Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static int Sum(this scoped ReadOnlySpan span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -264,10 +117,10 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// /// A span of values to calculate the sum of. /// - public static long Sum(this scoped Span span) => Sum, Int64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static long Sum(this scoped Span span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static long Sum(this scoped ReadOnlySpan span) => Sum, Int64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static long Sum(this scoped ReadOnlySpan span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -286,30 +139,30 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// /// A span of values to calculate the sum of. /// The sum of the values in the sequence. - public static float Sum(this scoped Span span) => Sum, SingleNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static float Sum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static float Sum(this scoped ReadOnlySpan span) => Sum, SingleNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static float Sum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. /// /// A span of values to calculate the sum of. /// The sum of the values in the sequence. - public static double Sum(this scoped Span span) => Sum, DoubleNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static double Sum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static double Sum(this scoped ReadOnlySpan span) => Sum, DoubleNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static double Sum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. /// /// A span of values to calculate the sum of. /// - public static decimal Sum(this scoped Span span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); + public static decimal Sum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static decimal Sum(this scoped ReadOnlySpan span) => Sum(ref MemoryMarshal.GetReference(span), span.Length); + public static decimal Sum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -320,10 +173,10 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// /// A span of values to calculate the sum of. /// The sum of the values in the span. - public static byte UnsafeSum(this scoped Span span) => Sum, ByteNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static byte UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static byte UnsafeSum(this scoped ReadOnlySpan span) => Sum, ByteNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static byte UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -331,21 +184,21 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// A span of values to calculate the sum of. /// [CLSCompliant(false)] - public static sbyte UnsafeSum(this scoped Span span) => Sum, SByteNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// [CLSCompliant(false)] - public static sbyte UnsafeSum(this scoped ReadOnlySpan span) => Sum, SByteNumber>(ref MemoryMarshal.GetReference(span), span.Length); + public static sbyte UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. /// /// A span of values to calculate the sum of. /// - public static short UnsafeSum(this scoped Span span) => Sum, Int16Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static short UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static short UnsafeSum(this scoped ReadOnlySpan span) => Sum, Int16Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static short UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -353,21 +206,21 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// A span of values to calculate the sum of. /// [CLSCompliant(false)] - public static ushort UnsafeSum(this scoped Span span) => Sum, UInt16Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static ushort UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// [CLSCompliant(false)] - public static ushort UnsafeSum(this scoped ReadOnlySpan span) => Sum, UInt16Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static ushort UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. /// /// A span of values to calculate the sum of. /// - public static int UnsafeSum(this scoped Span span) => Sum, Int32Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static int UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static int UnsafeSum(this scoped ReadOnlySpan span) => Sum, Int32Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static int UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -375,21 +228,21 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// A span of values to calculate the sum of. /// [CLSCompliant(false)] - public static uint UnsafeSum(this scoped Span span) => Sum, UInt32Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static uint UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// [CLSCompliant(false)] - public static uint UnsafeSum(this scoped ReadOnlySpan span) => Sum, UInt32Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static uint UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. /// /// A span of values to calculate the sum of. /// - public static long UnsafeSum(this scoped Span span) => Sum, Int64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static long UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// - public static long UnsafeSum(this scoped ReadOnlySpan span) => Sum, Int64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static long UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// /// Computes the sum of a span of values. @@ -397,9 +250,9 @@ public Vector UpdateState(Vector state, Vector leftOperand, Vector r /// A span of values to calculate the sum of. /// [CLSCompliant(false)] - public static ulong UnsafeSum(this scoped Span span) => Sum, UInt64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static ulong UnsafeSum(this scoped Span span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); /// [CLSCompliant(false)] - public static ulong UnsafeSum(this scoped ReadOnlySpan span) => Sum, UInt64Number>(ref MemoryMarshal.GetReference(span), span.Length); + public static ulong UnsafeSum(this scoped ReadOnlySpan span) => UnsafeSum(ref MemoryMarshal.GetReference(span), span.Length); } diff --git a/src/Spanned/Spans.cs b/src/Spanned/Spans.cs index 9d89d05..1282f26 100644 --- a/src/Spanned/Spans.cs +++ b/src/Spanned/Spans.cs @@ -27,12 +27,6 @@ public static bool TryGetSpan(this IEnumerable? enumerable, out ReadOnlySp span = (T[])enumerable; return true; } - - if (typeof(T) == typeof(char) && enumerable is string str) - { - span = UnsafeCast(str); - return true; - } } span = default; @@ -71,43 +65,10 @@ public static List ToList(this scoped ReadOnlySpan span) /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. /// /// The source slice, of type . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span UnsafeCast(this scoped Span span) - { - // Source: dotnet/runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs#Cast - - // Use unsigned integers - unsigned division by constant (especially by power of 2) - // and checked casts are faster and smaller. - uint fromSize = (uint)Unsafe.SizeOf(); - uint toSize = (uint)Unsafe.SizeOf(); - uint fromLength = (uint)span.Length; - int toLength; - - if (fromSize == toSize) - { - // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` - // should be optimized to just `length` but the JIT doesn't do that today. - toLength = (int)fromLength; - } - else if (fromSize == 1) - { - // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` - // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int` - // and can't eliminate the checked cast. This also avoids a 32 bit specific issue, - // the JIT can't eliminate long multiply by 1. - toLength = (int)(fromLength / toSize); - } - else - { - // Ensure that casts are done in such a way that the JIT is able to "see" - // the uint->ulong casts and the multiply together so that on 32 bit targets - // 32x32to64 multiplication is used. - ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize; - toLength = checked((int)toLengthUInt64); - } - - return MemoryMarshal.CreateSpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), toLength); - } + public static Span UnsafeCast(this Span span) + where TFrom : struct + where TTo : struct + => MemoryMarshal.Cast(span); /// /// Casts a Span of to a Span of . @@ -116,40 +77,8 @@ public static Span UnsafeCast(this scoped Span span) /// Supported only for platforms that support misaligned memory access or when the memory block is aligned by other means. /// /// The source slice, of type . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan UnsafeCast(this scoped ReadOnlySpan span) - { - // Source: dotnet/runtime/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/MemoryMarshal.cs#Cast - - // Use unsigned integers - unsigned division by constant (especially by power of 2) - // and checked casts are faster and smaller. - uint fromSize = (uint)Unsafe.SizeOf(); - uint toSize = (uint)Unsafe.SizeOf(); - uint fromLength = (uint)span.Length; - int toLength; - if (fromSize == toSize) - { - // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` - // should be optimized to just `length` but the JIT doesn't do that today. - toLength = (int)fromLength; - } - else if (fromSize == 1) - { - // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize` - // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int` - // and can't eliminate the checked cast. This also avoids a 32 bit specific issue, - // the JIT can't eliminate long multiply by 1. - toLength = (int)(fromLength / toSize); - } - else - { - // Ensure that casts are done in such a way that the JIT is able to "see" - // the uint->ulong casts and the multiply together so that on 32 bit targets - // 32x32to64 multiplication is used. - ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize; - toLength = checked((int)toLengthUInt64); - } - - return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(span)), toLength); - } + public static ReadOnlySpan UnsafeCast(this ReadOnlySpan span) + where TFrom : struct + where TTo : struct + => MemoryMarshal.Cast(span); } diff --git a/src/Spanned/Text/ValueStringBuilder.Append.cs b/src/Spanned/Text/ValueStringBuilder.Append.cs index 5dfd01b..97ea9e0 100644 --- a/src/Spanned/Text/ValueStringBuilder.Append.cs +++ b/src/Spanned/Text/ValueStringBuilder.Append.cs @@ -109,7 +109,6 @@ private void AppendString(string value) /// /// is null, and and are not zero. /// - /// public void Append(string? value, int start, int length) { if (start is not 0 && length is not 0) @@ -134,7 +133,6 @@ public void Append(scoped in ValueStringBuilder value) /// The string builder that contains the substring to append. /// The starting position of the substring within value. /// The number of characters in value to append. - /// public void Append(scoped in ValueStringBuilder value, int start, int length) { ReadOnlySpan valueSpan = value.AsSpan(start, length); @@ -186,7 +184,10 @@ private unsafe void Append(in char value, int valueCount) Grow(valueCount); } - MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(in value), valueCount).CopyTo(_buffer.Slice(length)); + Span target = _buffer.Slice(length); + for (int i = 0; i < valueCount; i++) + target[i] = Unsafe.Add(ref Unsafe.AsRef(in value), i); + _length = length + valueCount; } @@ -236,7 +237,6 @@ public void Append(char[]? value) /// /// is null, and and are not zero. /// - /// public void Append(char[]? value, int start, int length) { if (start is not 0 && length is not 0) @@ -283,264 +283,76 @@ public void Append(bool value) /// instance. /// /// The value to append. - [SkipLocalsInit] - public void Append(byte value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxByteStringLength]; - - // Since the `byte` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `byte`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Append(chars.Slice(0, charsWritten)); - } + public void Append(byte value) => Append(value.ToString()); /// /// Appends the string representation of a specified 8-bit signed integer to this instance. /// /// The value to append. [CLSCompliant(false)] - [SkipLocalsInit] - public void Append(sbyte value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxSbyteStringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Append(chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - AppendString(value.ToString()); - } - } + public void Append(sbyte value) => Append(value.ToString()); /// /// Appends the string representation of a specified 16-bit signed integer to this /// instance. /// /// The value to append. - [SkipLocalsInit] - public void Append(short value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxInt16StringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Append(chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - AppendString(value.ToString()); - } - } + public void Append(short value) => Append(value.ToString()); /// /// Appends the string representation of a specified 16-bit unsigned integer to this instance. /// /// The value to append. [CLSCompliant(false)] - [SkipLocalsInit] - public void Append(ushort value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxUInt16StringLength]; - - // Since the `ushort` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `ushort`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Append(chars.Slice(0, charsWritten)); - } + public void Append(ushort value) => Append(value.ToString()); /// /// Appends the string representation of a specified 32-bit signed integer to this /// instance. /// /// The value to append. - [SkipLocalsInit] - public void Append(int value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxInt32StringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Append(chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - AppendString(value.ToString()); - } - } + public void Append(int value) => Append(value.ToString()); /// /// Appends the string representation of a specified 32-bit unsigned integer to this instance. /// /// The value to append. [CLSCompliant(false)] - [SkipLocalsInit] - public void Append(uint value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxUInt32StringLength]; - - // Since the `uint` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `uint`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Append(chars.Slice(0, charsWritten)); - } + public void Append(uint value) => Append(value.ToString()); /// /// Appends the string representation of a specified 64-bit signed integer to this /// instance. /// /// The value to append. - [SkipLocalsInit] - public void Append(long value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxInt64StringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Append(chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - AppendString(value.ToString()); - } - } + public void Append(long value) => Append(value.ToString()); /// /// Appends the string representation of a specified 64-bit unsigned integer to this /// /// The value to append. [CLSCompliant(false)] - [SkipLocalsInit] - public void Append(ulong value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxUInt64StringLength]; - - // Since the `ulong` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `ulong`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Append(chars.Slice(0, charsWritten)); - } + public void Append(ulong value) => Append(value.ToString()); /// /// Appends the string representation of a specified single-precision floating-point /// number to this instance. /// /// The value to append. - [SkipLocalsInit] - public void Append(float value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxSingleStringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Append(chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - AppendString(value.ToString()); - } - } + public void Append(float value) => Append(value.ToString()); /// /// Appends the string representation of a specified double-precision floating-point /// number to this instance. /// /// The value to append. - [SkipLocalsInit] - public void Append(double value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxDoubleStringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Append(chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - AppendString(value.ToString()); - } - } + public void Append(double value) => Append(value.ToString()); /// /// Appends the string representation of a specified decimal number to this instance. /// /// The value to append. - [SkipLocalsInit] - public void Append(decimal value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxDecimalStringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Append(chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - AppendString(value.ToString()); - } - } + public void Append(decimal value) => Append(value.ToString()); /// /// Appends a formatted representation of the specified value to this instance. diff --git a/src/Spanned/Text/ValueStringBuilder.AppendFormat.cs b/src/Spanned/Text/ValueStringBuilder.AppendFormat.cs index 7b57fce..4350b23 100644 --- a/src/Spanned/Text/ValueStringBuilder.AppendFormat.cs +++ b/src/Spanned/Text/ValueStringBuilder.AppendFormat.cs @@ -1,72 +1,7 @@ -using Spanned.Collections.Generic; - namespace Spanned.Text; public ref partial struct ValueStringBuilder { - /// - /// Appends the string returned by processing a composite format string, which contains - /// zero or more format items, to this instance. Each format item is replaced by - /// the string representation of the provided argument. - /// - /// A composite format string. - /// The object to format. - /// is null. - /// - /// is invalid. - /// -or- - /// The index of a format item is less than 0 (zero), or - /// greater than or equal to 1 (three). - /// - public void AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) - { - InlineArray1 args = new(arg0); - AppendFormatCore(provider: null, format, args); - } - - /// - /// Appends the string returned by processing a composite format string, which contains - /// zero or more format items, to this instance. Each format item is replaced by - /// the string representation of either of two arguments. - /// - /// A composite format string. - /// The first object to format. - /// The second object to format. - /// is null. - /// - /// is invalid. - /// -or- - /// The index of a format item is less than 0 (zero), or - /// greater than or equal to 2 (three). - /// - public void AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) - { - InlineArray2 args = new(arg0, arg1); - AppendFormatCore(provider: null, format, args); - } - - /// - /// Appends the string returned by processing a composite format string, which contains - /// zero or more format items, to this instance. Each format item is replaced by - /// the string representation of either of three arguments. - /// - /// A composite format string. - /// The first object to format. - /// The second object to format. - /// The third object to format. - /// is null. - /// - /// is invalid. - /// -or- - /// The index of a format item is less than 0 (zero), or - /// greater than or equal to 3 (three). - /// - public void AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) - { - InlineArray3 args = new(arg0, arg1, arg2); - AppendFormatCore(provider: null, format, args); - } - /// /// Appends the string returned by processing a composite format string, which contains /// zero or more format items, to this instance. Each format item is replaced by @@ -105,75 +40,6 @@ public void AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] s public void AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, scoped ReadOnlySpan args) => AppendFormatCore(provider: null, format, args); - /// - /// Appends the string returned by processing a composite format string, which contains - /// zero or more format items, to this instance. Each format item is replaced by - /// the string representation of the given arguments using a specified format - /// provider. - /// - /// An object that supplies culture-specific formatting information. - /// A composite format string. - /// The object to format. - /// is null. - /// - /// is invalid. - /// -or- - /// The index of a format item is less than 0 (zero), or - /// greater than or equal to 1 (three). - /// - public void AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) - { - InlineArray1 args = new(arg0); - AppendFormatCore(provider, format, args); - } - - /// - /// Appends the string returned by processing a composite format string, which contains - /// zero or more format items, to this instance. Each format item is replaced by - /// the string representation of either of two arguments using a specified format - /// provider. - /// - /// An object that supplies culture-specific formatting information. - /// A composite format string. - /// The first object to format. - /// The second object to format. - /// is null. - /// - /// is invalid. - /// -or- - /// The index of a format item is less than 0 (zero), or - /// greater than or equal to 2 (three). - /// - public void AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) - { - InlineArray2 args = new(arg0, arg1); - AppendFormatCore(provider, format, args); - } - - /// - /// Appends the string returned by processing a composite format string, which contains - /// zero or more format items, to this instance. Each format item is replaced by - /// the string representation of either of three arguments using a specified format - /// provider. - /// - /// An object that supplies culture-specific formatting information. - /// A composite format string. - /// The first object to format. - /// The second object to format. - /// The third object to format. - /// is null. - /// - /// is invalid. - /// -or- - /// The index of a format item is less than 0 (zero), or - /// greater than or equal to 3 (three). - /// - public void AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) - { - InlineArray3 args = new(arg0, arg1, arg2); - AppendFormatCore(provider, format, args); - } - /// /// Appends the string returned by processing a composite format string, which contains /// zero or more format items, to this instance. Each format item is replaced by @@ -410,7 +276,7 @@ private void AppendFormatCore(IFormatProvider? provider, string format, scoped R { if (!itemFormatSpan.IsEmpty) { - itemFormat = new string(itemFormatSpan); + itemFormat = itemFormatSpan.ToString(); } s = cf.Format(itemFormat, arg, provider); @@ -424,7 +290,7 @@ private void AppendFormatCore(IFormatProvider? provider, string format, scoped R { if (itemFormatSpan.Length != 0) { - itemFormat ??= new string(itemFormatSpan); + itemFormat ??= itemFormatSpan.ToString(); } s = formattableArg.ToString(itemFormat, provider); } diff --git a/src/Spanned/Text/ValueStringBuilder.AppendJoin.cs b/src/Spanned/Text/ValueStringBuilder.AppendJoin.cs index a1a9455..04184ed 100644 --- a/src/Spanned/Text/ValueStringBuilder.AppendJoin.cs +++ b/src/Spanned/Text/ValueStringBuilder.AppendJoin.cs @@ -207,7 +207,7 @@ private void AppendJoinCore(ref char separator, int separatorLength, IEnumera Debug.Assert(separatorLength >= 0); Debug.Assert(values is not null); - using IEnumerator en = values.GetEnumerator(); + using IEnumerator en = values!.GetEnumerator(); if (!en.MoveNext()) { return; diff --git a/src/Spanned/Text/ValueStringBuilder.Insert.cs b/src/Spanned/Text/ValueStringBuilder.Insert.cs index 9302528..3144cb0 100644 --- a/src/Spanned/Text/ValueStringBuilder.Insert.cs +++ b/src/Spanned/Text/ValueStringBuilder.Insert.cs @@ -8,7 +8,6 @@ public ref partial struct ValueStringBuilder /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, char value) { if ((uint)index > (uint)_length) @@ -37,7 +36,6 @@ public void Insert(int index, char value) /// The value to insert. /// The number of times to insert the value. /// is less than zero. - /// public void Insert(int index, char value, int repeatCount) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidLength(repeatCount); @@ -64,7 +62,6 @@ public void Insert(int index, char value, int repeatCount) /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, string? value) { if (value is null) @@ -78,7 +75,6 @@ public void Insert(int index, string? value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// [MethodImpl(MethodImplOptions.NoInlining)] private void InsertString(int index, string value) => Insert(index, value.AsSpan()); @@ -124,7 +120,6 @@ public void Insert(int index, string? value, int startIndex, int count) /// The value to insert. /// The number of times to insert value. /// is less than zero. - /// public void Insert(int index, string? value, int repeatCount) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidLength(repeatCount); @@ -161,7 +156,6 @@ public void Insert(int index, string? value, int repeatCount) /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, scoped in ValueStringBuilder value) => Insert(index, value, 0, value._length); @@ -217,7 +211,6 @@ public unsafe void Insert(int index, char* value, int valueCount) /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, scoped ReadOnlySpan value) { if ((uint)index > (uint)_length) @@ -246,7 +239,6 @@ public void Insert(int index, scoped ReadOnlySpan value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, char[]? value) { if (value is null) @@ -294,7 +286,6 @@ public void Insert(int index, char[]? value, int startIndex, int count) /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, ReadOnlyMemory value) => Insert(index, value.Span); @@ -304,7 +295,6 @@ public void Insert(int index, ReadOnlyMemory value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, object? value) { if (value is not null) @@ -319,7 +309,6 @@ public void Insert(int index, object? value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// public void Insert(int index, bool value) { // `ToString()` on booleans does not allocate, @@ -333,20 +322,7 @@ public void Insert(int index, bool value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// - [SkipLocalsInit] - public void Insert(int index, byte value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxByteStringLength]; - - // Since the `byte` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `byte`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Insert(index, chars.Slice(0, charsWritten)); - } + public void Insert(int index, byte value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a specified 8-bit signed integer into this @@ -354,28 +330,8 @@ public void Insert(int index, byte value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// [CLSCompliant(false)] - [SkipLocalsInit] - public void Insert(int index, sbyte value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxSbyteStringLength]; - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Insert(index, chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - InsertString(index, value.ToString()); - } - } + public void Insert(int index, sbyte value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a specified 16-bit signed integer into this @@ -383,27 +339,7 @@ public void Insert(int index, sbyte value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// - [SkipLocalsInit] - public void Insert(int index, short value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxInt16StringLength]; - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Insert(index, chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - InsertString(index, value.ToString()); - } - } + public void Insert(int index, short value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a 16-bit unsigned integer into this instance @@ -411,21 +347,8 @@ public void Insert(int index, short value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// [CLSCompliant(false)] - [SkipLocalsInit] - public void Insert(int index, ushort value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxUInt16StringLength]; - - // Since the `ushort` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `ushort`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Insert(index, chars.Slice(0, charsWritten)); - } + public void Insert(int index, ushort value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a specified 32-bit signed integer into this @@ -433,28 +356,7 @@ public void Insert(int index, ushort value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// - [SkipLocalsInit] - public void Insert(int index, int value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxInt32StringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Insert(index, chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - InsertString(index, value.ToString()); - } - } + public void Insert(int index, int value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a 32-bit unsigned integer into this instance @@ -462,21 +364,8 @@ public void Insert(int index, int value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// [CLSCompliant(false)] - [SkipLocalsInit] - public void Insert(int index, uint value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxUInt32StringLength]; - - // Since the `uint` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `uint`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Insert(index, chars.Slice(0, charsWritten)); - } + public void Insert(int index, uint value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a 64-bit signed integer into this instance @@ -484,28 +373,7 @@ public void Insert(int index, uint value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// - [SkipLocalsInit] - public void Insert(int index, long value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxInt64StringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Insert(index, chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - InsertString(index, value.ToString()); - } - } + public void Insert(int index, long value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a 64-bit unsigned integer into this instance @@ -513,21 +381,8 @@ public void Insert(int index, long value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// [CLSCompliant(false)] - [SkipLocalsInit] - public void Insert(int index, ulong value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxUInt64StringLength]; - - // Since the `ulong` data type is unsigned, its formatting is not influenced by the current culture. - // The current culture could provide a custom sign, which might be longer than one symbol, - // but in the case of `ulong`, this operation will always succeed. - value.TryFormat(chars, out int charsWritten, format: default, provider: null); - Insert(index, chars.Slice(0, charsWritten)); - } + public void Insert(int index, ulong value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a single-precision floating point number @@ -535,28 +390,7 @@ public void Insert(int index, ulong value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// - [SkipLocalsInit] - public void Insert(int index, float value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxSingleStringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Insert(index, chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - InsertString(index, value.ToString()); - } - } + public void Insert(int index, float value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a double-precision floating-point number @@ -564,28 +398,7 @@ public void Insert(int index, float value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// - [SkipLocalsInit] - public void Insert(int index, double value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxDoubleStringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Insert(index, chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - InsertString(index, value.ToString()); - } - } + public void Insert(int index, double value) => Insert(index, value.ToString()); /// /// Inserts the string representation of a decimal number into this instance at the @@ -593,28 +406,7 @@ public void Insert(int index, double value) /// /// The position in this instance where insertion begins. /// The value to insert. - /// - [SkipLocalsInit] - public void Insert(int index, decimal value) - { - // JIT does not inline methods that use stackalloc yet, so - // we cannot move this logic to a shared location, sadly. - Span chars = stackalloc char[StringHelper.MaxDecimalStringLength]; - - if (value.TryFormat(chars, out int charsWritten, format: default, provider: null)) - { - Insert(index, chars.Slice(0, charsWritten)); - } - else - { - // This path should never be taken. But if it is, - // it means that there's something wrong with the current culture. - // We still need to make it work, but we must not optimize this case, - // since it will actually degrade the overall performance and increase - // the assembly size for nothing. - InsertString(index, value.ToString()); - } - } + public void Insert(int index, decimal value) => Insert(index, value.ToString()); /// /// Inserts a formatted representation of the specified value into this instance @@ -625,7 +417,6 @@ public void Insert(int index, decimal value) /// The value to insert. /// The format string to be used. /// An object that supplies culture-specific formatting information. - /// public void Insert(int index, T? value, string? format = null, IFormatProvider? provider = null) { // If there's a custom formatter, let it deal with the formatting. diff --git a/src/Spanned/Text/ValueStringBuilder.cs b/src/Spanned/Text/ValueStringBuilder.cs index 4b71439..0c56eab 100644 --- a/src/Spanned/Text/ValueStringBuilder.cs +++ b/src/Spanned/Text/ValueStringBuilder.cs @@ -38,7 +38,6 @@ public ValueStringBuilder(Span buffer) /// capacity. /// /// The suggested starting size of this instance. - /// public ValueStringBuilder(int capacity) { _rentedBuffer = ArrayPool.Shared.Rent(capacity); @@ -60,7 +59,6 @@ public ValueStringBuilder(scoped ReadOnlySpan value) /// /// The initial content for this instance. /// The suggested starting size of this instance. - /// public ValueStringBuilder(scoped ReadOnlySpan value, int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity, value.Length); @@ -103,13 +101,13 @@ public ValueStringBuilder(string? value, int startIndex, int length, int capacit /// The string builder to be converted. /// A span covering the content of the . public static implicit operator ReadOnlySpan(ValueStringBuilder sb) - => MemoryMarshal.CreateReadOnlySpan(ref MemoryMarshal.GetReference(sb._buffer), sb._length); + => sb._buffer.Slice(0, sb._length); /// /// Returns a span that represents the content of the . /// /// A span covering the content of the . - public readonly Span AsSpan() => MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_buffer), _length); + public readonly Span AsSpan() => _buffer.Slice(0, _length); /// /// Returns a span that represents the content of the , @@ -124,7 +122,7 @@ public Span AsSpan(bool ensureNullTerminator) if (ensureNullTerminator) EnsureNullTerminator(); - return MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_buffer), _length); + return _buffer.Slice(0, _length); } /// @@ -132,7 +130,6 @@ public Span AsSpan(bool ensureNullTerminator) /// /// The start index of the segment. /// A span covering the segment of the content. - /// public readonly Span AsSpan(int start) => AsSpan(start, _length - start); /// @@ -141,13 +138,11 @@ public Span AsSpan(bool ensureNullTerminator) /// The start index of the segment. /// The length of the segment. /// A span covering the segment of the content. - /// public readonly Span AsSpan(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _length); - // Skip additional bound checks. - return MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), start), length); + return _buffer.Slice(start, length); } /// @@ -166,7 +161,6 @@ public readonly Span AsSpan(int start, int length) /// /// The length of this instance. /// - /// public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -183,7 +177,6 @@ public int Length /// The maximum number of characters that can be contained in the memory /// allocated by the current instance. /// - /// public int Capacity { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -204,7 +197,6 @@ public int Capacity /// /// The position of the character. /// The Unicode character at position index. - /// public readonly ref char this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -249,7 +241,6 @@ public void EnsureNullTerminator() /// /// The minimum capacity to ensure. /// The new capacity of this instance. - /// public int EnsureCapacity(int capacity) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidCapacity(capacity); @@ -363,7 +354,6 @@ public void AppendLine(scoped ReadOnlySpan value) /// /// The zero-based position in this instance where removal begins. /// The number of characters to remove. - /// public void Remove(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _length); @@ -389,7 +379,6 @@ public readonly void Replace(char oldChar, char newChar) /// The character that replaces oldChar. /// The position in this instance where the substring begins. /// The length of the substring. - /// public readonly void Replace(char oldChar, char newChar, int start, int length) { Span chars = AsSpan(start, length); @@ -427,7 +416,6 @@ public void Replace(string oldValue, string? newValue) /// The position in this instance where the substring begins. /// The length of the substring. /// is null. - /// public void Replace(string oldValue, string? newValue, int start, int length) { ThrowHelper.ThrowArgumentNullException_IfNull(oldValue); @@ -454,7 +442,6 @@ public void Replace(scoped ReadOnlySpan oldValue, scoped ReadOnlySpanThe position in this instance where the substring begins. /// The length of the substring. /// The length of is zero. - /// public void Replace(scoped ReadOnlySpan oldValue, scoped ReadOnlySpan newValue, int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _length); @@ -565,7 +552,6 @@ public void Clear() /// The destination of the elements copied from . /// /// The zero-based index in the destination at which copying begins. - /// public readonly void CopyTo(char[] destination, int destinationStart) => CopyTo(0, destination, destinationStart, _length); /// @@ -578,7 +564,6 @@ public void Clear() /// /// The number of elements to copy. /// - /// public readonly void CopyTo(int start, scoped Span destination, int length) => AsSpan(start, length).CopyTo(destination); /// @@ -590,7 +575,6 @@ public void Clear() /// /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. - /// public readonly void CopyTo(int start, char[] destination, int destinationStart, int length) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -633,7 +617,6 @@ public readonly bool TryCopyTo(char[] destination, out int written) /// The destination of the elements copied from . /// The zero-based index in the destination at which copying begins. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(char[] destination, int destinationStart, out int written) => TryCopyTo(0, destination, destinationStart, _length, out written); @@ -644,7 +627,6 @@ public readonly bool TryCopyTo(char[] destination, int destinationStart, out int /// The destination of the elements copied from . /// The number of elements to copy. /// The number of elements copied to the destination. - /// public readonly bool TryCopyTo(int start, scoped Span destination, int length, out int written) { if (AsSpan(start, length).TryCopyTo(destination)) @@ -669,7 +651,6 @@ public readonly bool TryCopyTo(int start, scoped Span destination, int len /// The zero-based index in the destination at which copying begins. /// The number of elements to copy. /// The number of elements copied to the array. - /// public readonly bool TryCopyTo(int start, char[] destination, int destinationStart, int length, out int written) { ThrowHelper.ThrowArgumentNullException_IfNull(destination); @@ -683,16 +664,12 @@ public readonly bool TryCopyTo(int start, char[] destination, int destinationSta /// The starting index of the segment in this instance. /// The length of the segment. /// The string representation of the specified segment. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly string ToString(int start, int length) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _length); - // Skip bound checks. - Span chars = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), start), length); - - return chars.ToString(); + return _buffer.Slice(start, length).ToString(); } /// @@ -705,14 +682,12 @@ public readonly string ToString(int start, int length) /// The length of the segment. /// A flag indicating whether to dispose of internal resources. /// The string representation of the specified segment. - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(int start, int length, bool dispose) { ThrowHelper.ThrowArgumentOutOfRangeException_IfInvalidRange(start, length, _length); - // Skip bound checks. - Span chars = MemoryMarshal.CreateSpan(ref Unsafe.Add(ref MemoryMarshal.GetReference(_buffer), start), length); + Span chars = _buffer.Slice(start, length); string s = chars.ToString(); if (dispose) @@ -732,14 +707,7 @@ public string ToString(int start, int length, bool dispose) /// Returns a string representation of this instance. /// /// The string representation of this instance. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public override readonly string ToString() - { - // Skip additional bound checks. - Span chars = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_buffer), _length); - - return chars.ToString(); - } + public override readonly string ToString() => _buffer.Slice(0, _length).ToString(); /// /// Returns a string representation of this instance. @@ -752,8 +720,7 @@ public override readonly string ToString() [MethodImpl(MethodImplOptions.AggressiveInlining)] public string ToString(bool dispose) { - // Skip additional bound checks. - Span chars = MemoryMarshal.CreateSpan(ref MemoryMarshal.GetReference(_buffer), _length); + Span chars = _buffer.Slice(0, _length); string s = chars.ToString(); if (dispose) @@ -785,13 +752,13 @@ public string ToString(bool dispose) /// true if the characters in this instance and another string builder are the same; otherwise, false. public readonly bool Equals(in ValueStringBuilder sb) => AsSpan().SequenceEqual(sb.AsSpan()); - /// + /// [Obsolete("Equals(object) on ValueStringBuilder will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnRefStruct(); - /// + /// [Obsolete("GetHashCode() on ValueStringBuilder will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly int GetHashCode() @@ -828,7 +795,6 @@ public void TrimExcess() /// without any further expansion of its backing storage. /// /// The new capacity. - /// public void TrimExcess(int capacity) { if (capacity < _buffer.Length)