Skip to content

Commit

Permalink
Add IUnsafeDataBatch<T> and various improvements/small optimizations.
Browse files Browse the repository at this point in the history
  • Loading branch information
xoofx committed Oct 27, 2024
1 parent 540a4e6 commit f1acdf3
Show file tree
Hide file tree
Showing 11 changed files with 120 additions and 76 deletions.
11 changes: 11 additions & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="MSTest" Version="3.6.1" />
</ItemGroup>
</Project>
2 changes: 1 addition & 1 deletion src/XenoAtom.Collections.Bench/BenchSort.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using XenoAtom.Collections;

namespace XenoAtom.Collections.Bench;

Expand All @@ -17,6 +16,7 @@ public class BenchSort
public BenchSort()
{
_data = Array.Empty<Item>();
_copy = Array.Empty<Item>();
}

public Item[] Data => _data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.11" />
<PackageReference Include="BenchmarkDotNet"/>
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MSTest" Version="3.*" />
<PackageReference Include="MSTest" />
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
</ItemGroup>

Expand Down
1 change: 1 addition & 0 deletions src/XenoAtom.Collections.sln
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\.gitattributes = ..\.gitattributes
..\.gitignore = ..\.gitignore
..\.github\workflows\ci.yml = ..\.github\workflows\ci.yml
Directory.Packages.props = Directory.Packages.props
dotnet-releaser.toml = dotnet-releaser.toml
global.json = global.json
..\license.txt = ..\license.txt
Expand Down
23 changes: 23 additions & 0 deletions src/XenoAtom.Collections/IUnsafeDataBatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Licensed under the BSD-Clause 2 license.
// See license.txt file in the project root for full license information.

namespace XenoAtom.Collections;

/// <summary>
/// Interface to get a batch of data elements.
/// </summary>
/// <typeparam name="T">The type of the element</typeparam>
public interface IUnsafeDataBatch<T>
{
/// <summary>
/// Gets a batch of elements from the data provider with the specified count and capacity.
/// </summary>
/// <param name="additionalCount">The number of elements to get.</param>
/// <param name="marginCount">The additional capacity of elements to get - in addition to the count.</param>
/// <returns>A reference to the first element of the batch of elements.</returns>
/// <remarks>
/// The returned reference is valid only if the data provider is not modified when using this reference.
/// </remarks>
ref T UnsafeGetBatch(int additionalCount, int marginCount);
}
12 changes: 2 additions & 10 deletions src/XenoAtom.Collections/IUnsafeListBatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,5 @@ namespace XenoAtom.Collections;
/// Interface to get a batch of elements from an <see cref="UnsafeList{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the element</typeparam>
public interface IUnsafeListBatch<T>
{
/// <summary>
/// Gets a batch of elements from the list with the specified additional count and capacity.
/// </summary>
/// <param name="additionalCount">The additional count of elements to get</param>
/// <param name="marginCount">The additional capacity of elements to get - in addition to the count</param>
/// <returns>A reference to the first element of the batch of elements.</returns>
ref T UnsafeGetBatch(int additionalCount, int marginCount); // TODO: rename marginCount to additionalCapacity during a breaking change version
}
[Obsolete($"Use IUnsafeDataBatch<T> instead")]
public interface IUnsafeListBatch<T> : IUnsafeDataBatch<T>;
119 changes: 68 additions & 51 deletions src/XenoAtom.Collections/UnsafeList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,21 +149,22 @@ public void Add(T item)
private void ResizeAndAdd(T item)
{
var count = _count;
EnsureCapacity(count + 1);
UnsafeGetRefAt(count) = item;
var items = EnsureCapacity(count + 1);
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), count) = item;
_count = count + 1;
}

public ref T UnsafeGetOrCreate(nint index)
{
var items = _items;
if (index >= _count)
{
var newCount = index + 1;
EnsureCapacity(newCount);
items = EnsureCapacity(newCount);
_count = (int)newCount;
}

return ref UnsafeGetRefAt(index);
return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -185,36 +186,38 @@ public void AddByRef(in T item)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Insert(nint index, T item)
{
if ((uint)index > (uint)_count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);

var count = _count;
if (count == _items.Length)
if ((uint)index > (uint)count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);

var items = _items;
if (count == items.Length)
{
EnsureCapacity(count + 1);
items = EnsureCapacity(count + 1);
}
if (index < count)
{
Array.Copy(_items, index, _items, index + 1, count - index);
Array.Copy(items, index, items, index + 1, count - index);
}
UnsafeGetRefAt(index) = item;
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), index) = item;
_count++;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InsertByRef(nint index, in T item)
{
if ((uint)index > (uint)_count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);

var count = _count;
if (count == _items.Length)
if ((uint)index > (uint)count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);

var items = _items;
if (count == items.Length)
{
EnsureCapacity(count + 1);
items = EnsureCapacity(count + 1);
}
if (index < count)
{
Array.Copy(_items, index, _items, index + 1, count - index);
Array.Copy(items, index, items, index + 1, count - index);
}
UnsafeGetRefAt(index) = item;
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), index) = item;
_count++;
}

Expand Down Expand Up @@ -243,41 +246,55 @@ public void SortByRef<TComparer>(in TComparer comparer) where TComparer : struct
AsSpan().SortByRef(comparer);
}

public readonly int IndexOf(T element) => Array.IndexOf(_items, element, 0, (int)_count);
public readonly nint IndexOf(T element) => Array.IndexOf(_items, element, 0, (int)_count);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void RemoveAt(nint index)
{
if ((uint)index >= (uint)_count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
_count--;
if (index < _count)
var count = _count;

if ((uint)index >= (uint)count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);

count--;
var items = _items;

if (index < count)
{
Array.Copy(_items, index + 1, _items, index, _count - index);
Array.Copy(items, index + 1, items, index, count - index);
}

if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
UnsafeGetRefAt(_count) = default!;
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), count) = default!;
}

_count = count;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T RemoveAtAndGet(nint index)
{
if ((uint)index >= (uint)_count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
_count--;
var count = _count;

if ((uint)index >= (uint)count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);

count--;
var items = _items;

var element = Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), index);

// previous children
var item = UnsafeGetRefAt(index);
if (index < _count)
if (index < count)
{
Array.Copy(_items, index + 1, _items, index, _count - index);
Array.Copy(items, index + 1, items, index, count - index);
}

if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
{
UnsafeGetRefAt(_count) = default!;
Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), count) = default!;
}
return item;

_count = count;
return element;
}

public T RemoveLast()
Expand Down Expand Up @@ -306,44 +323,43 @@ public ref T this[nint index]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ref T UnsafeGetRefAt(nint index) => ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_items), index);

public void Push(T element)
{
Add(element);
}
public void Push(T element) => Add(element);

public readonly ref T Peek()
{
if (_count == 0) ThrowHelper.ThrowInvalidOperationPeekOnEmptyList();
return ref UnsafeGetRefAt(_count - 1);
var count = _count;
if (count == 0) ThrowHelper.ThrowInvalidOperationPeekOnEmptyList();
return ref UnsafeGetRefAt(count - 1);
}

public T Pop() => RemoveAtAndGet(_count - 1);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T UnsafeGetBatch(int addCount, int additionalCapacity)
public ref T UnsafeGetBatch(int additionalCount, int marginCount)
{
var count = (nint)_count;
var thisCount = (nint)_count;
var items = _items;
if (items.Length < count + addCount + additionalCapacity)
var newCapacity = thisCount + additionalCount + marginCount;
if (items.Length < newCapacity)
{
return ref SlowUnsafeGetBatch(addCount, additionalCapacity);
return ref SlowUnsafeGetBatch(additionalCount, newCapacity);
}

_count = (int)(count + addCount);
return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), count);
_count = (int)(thisCount + additionalCount);
return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), thisCount);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private ref T SlowUnsafeGetBatch(int addCount, int additionalCapacity)
private ref T SlowUnsafeGetBatch(int additionalCount, nint newCapacity)
{
var count = _count;
var newCapacity = count + addCount + additionalCapacity;
EnsureCapacity(newCapacity);
_count = count + addCount;
return ref UnsafeGetRefAt(count);
var thisCount = (nint)_count;
var items = EnsureCapacity(newCapacity);
_count = (int)(thisCount + additionalCount);
return ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(items), thisCount);
}

private void EnsureCapacity(nint min)
[MethodImpl(MethodImplOptions.NoInlining)]
private T[] EnsureCapacity(nint min)
{
var items = _items;
Debug.Assert(min > items.Length);
Expand All @@ -357,7 +373,8 @@ private void EnsureCapacity(nint min)
{
Array.Copy(items, 0, destinationArray, 0, _count);
}
_items = destinationArray;
_items = items = destinationArray;
return items;
}

public Enumerator GetEnumerator() => new(this);
Expand Down
Loading

0 comments on commit f1acdf3

Please sign in to comment.