Skip to content

Commit

Permalink
Add an overload for DoForEach() that allows cancellation of the itera…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
CodeDevAM committed Jun 7, 2024
1 parent 0eea939 commit 94e39a0
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 11 deletions.
11 changes: 10 additions & 1 deletion BTree.Benchmark/BTree.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<LangVersion>latest</LangVersion>
<InvariantGlobalization>true</InvariantGlobalization>
<Title>BTree.Benchmark</Title>
<Authors>DevAM</Authors>
<Company>DevAM</Company>
<Product>BTree</Product>
<Version>1.1.0</Version>
<Copyright>Copyright (c) 2024 DevAM. All Rights Reserved.</Copyright>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions BTree.Benchmark/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using BenchmarkDotNet.Reports;
using BenchmarkDotNet.Running;
using BTree;
using System;
using System.Linq;

Summary summary = BenchmarkRunner.Run<BTreeBenchmark>();

Expand Down
9 changes: 8 additions & 1 deletion BTree.Test/BTree.Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>disable</Nullable>
<LangVersion>latest</LangVersion>
<InvariantGlobalization>true</InvariantGlobalization>
<Title>BTree.Benchmark</Title>
<Authors>DevAM</Authors>
<Company>DevAM</Company>
<Product>BTree</Product>
<Version>1.1.0</Version>
<Copyright>Copyright (c) 2024 DevAM. All Rights Reserved.</Copyright>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>

<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
Expand Down
22 changes: 22 additions & 0 deletions BTree.Test/BTree/GetTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace BTree.Test.BTree;

Expand Down Expand Up @@ -54,6 +56,16 @@ public void Get(ushort degree, int count, int minItem, int maxItem)
tree.DoForEach<Ref<int>>(actualRangeList.Add, minItem, maxItem, true);
CollectionAssert.AreEqual(expectedRangeSequence, actualRangeList);

actualRangeList = [];
tree.DoForEach<Ref<int>>(item =>
{
actualRangeList.Add(item);
return true;
}, minItem, maxItem, true);

expectedRangeSequence = orderedItems.Where(item => item >= minItem && item <= maxItem).Take(1).ToArray();
CollectionAssert.AreEqual(expectedRangeSequence, actualRangeList);

// exclusive max
expectedRangeSequence = orderedItems.Where(item => item >= minItem && item < maxItem).ToArray();
actualRangeSequence = tree.GetRange<Ref<int>>(minItem, maxItem, false).ToArray();
Expand All @@ -62,6 +74,16 @@ public void Get(ushort degree, int count, int minItem, int maxItem)
actualRangeList = [];
tree.DoForEach<Ref<int>>(actualRangeList.Add, minItem, maxItem, false);
CollectionAssert.AreEqual(expectedRangeSequence, actualRangeList);

actualRangeList = [];
tree.DoForEach<Ref<int>>(item =>
{
actualRangeList.Add(item);
return true;
}, minItem, maxItem, false);

expectedRangeSequence = orderedItems.Where(item => item >= minItem && item < maxItem).Take(1).ToArray();
CollectionAssert.AreEqual(expectedRangeSequence, actualRangeList);
}

public static IEnumerable TestCases
Expand Down
1 change: 1 addition & 0 deletions BTree.Test/BTree/InsertTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections;
using System.Linq;

namespace BTree.Test.BTree;

Expand Down
4 changes: 3 additions & 1 deletion BTree.Test/BTree/RemoveTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Collections;
using System;
using System.Collections;
using System.Linq;

namespace BTree.Test.BTree;

Expand Down
4 changes: 3 additions & 1 deletion BTree.Test/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace BTree.Test;
using System;

namespace BTree.Test;

internal static class Extensions
{
Expand Down
4 changes: 3 additions & 1 deletion BTree.Test/Ref.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace BTree.Test
using System;

namespace BTree.Test
{
internal record Ref<T>(T Value) : IComparable<Ref<T>>, IComparable<T> where T : IComparable<T>
{
Expand Down
8 changes: 8 additions & 0 deletions BTree.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"folders": [
{
"path": "."
}
],
"settings": {}
}
143 changes: 141 additions & 2 deletions BTree/BTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,54 @@ internal void DoForEach<TKey>(Action<T> action, TKey minKey, TKey maxKey, bool m
}
}

internal bool DoForEach<TKey>(Func<T, bool> actionAndCancelFunction, TKey minKey, TKey maxKey, bool maxInclusive) where TKey : IComparable<T>
{
int index = FindNextGreaterOrEqual(minKey);

for (int i = index; i <= Count; i++)
{
// Handle children first
if (!IsLeaf)
{
Node child = Children[i];
bool cancel = child.DoForEach(actionAndCancelFunction, minKey, maxKey, maxInclusive);
if (cancel)
{
return true;
}
}

if (i < Count)
{
T currentItem = Items[i];
int comparisonResult = maxKey.CompareTo(currentItem);

if (maxInclusive)
{
if (comparisonResult < 0)
{
return true;
}
}
else
{
if (comparisonResult <= 0)
{
return true;
}
}

bool cancel = actionAndCancelFunction.Invoke(currentItem);
if (cancel)
{
return true;
}
}
}

return false;
}

internal void DoForEach(Action<T> action)
{
for (int i = 0; i <= Count; i++)
Expand All @@ -702,6 +750,35 @@ internal void DoForEach(Action<T> action)
}
}

internal bool DoForEach(Func<T, bool> actionAndCancelFunction)
{
for (int i = 0; i <= Count; i++)
{
// Handle children first
if (!IsLeaf)
{
Node child = Children[i];
bool cancel = child.DoForEach(actionAndCancelFunction);
if (cancel)
{
return true;
}
}

if (i < Count)
{
T item = Items[i];
bool cancel = actionAndCancelFunction.Invoke(item);
if (cancel)
{
return true;
}
}
}

return false;
}

internal IEnumerable<T> GetRange<TKey>(TKey minKey, TKey maxKey, bool maxInclusive) where TKey : IComparable<T>
{
int index = FindNextGreaterOrEqual(minKey);
Expand Down Expand Up @@ -1060,7 +1137,7 @@ public bool GetMax(out T maxItem)
/// The upper limit is inclusive if <paramref name="maxInclusive"/> is true otherwise the upper limit is exclusive.
/// Use this over <see cref="GetRange(T, T, bool)"/> in performance critical paths.
/// </summary>
/// <param name="action"></param>
/// <param name="action">Function that will be called for every relevant item.</param>
/// <param name="minKey">Inclusive lower limit</param>
/// <param name="maxKey">Upper limit</param>
/// <param name="maxInclusive">The upper limit is inclusive if true otherwise the upper limit is exclusive</param>
Expand Down Expand Up @@ -1091,14 +1168,53 @@ public void DoForEach<TKey>(Action<T> action, TKey minKey, TKey maxKey, bool max
{
Interlocked.Add(ref _IterationCount, -1);
}
}

/// <summary>
/// Performs an action for every single item within an inclusive lower limit (<paramref name="minKey"/>) and an upper limit (<paramref name="maxKey"/>).
/// The parameter <paramref name="maxKey"/> and <paramref name="minKey"/> can be a reduced version of an item as long as they implement <see cref="IComparable{T}"/>.
/// The upper limit is inclusive if <paramref name="maxInclusive"/> is true otherwise the upper limit is exclusive.
/// Use this over <see cref="GetRange(T, T, bool)"/> in performance critical paths.
/// It offers the possibility to cancel the iteration.
/// </summary>
/// <param name="actionAndCancelFunction">Function that will be called for every relevant item. It returns true to cancel or false to continue.</param>
/// <param name="minKey">Inclusive lower limit</param>
/// <param name="maxKey">Upper limit</param>
/// <param name="maxInclusive">The upper limit is inclusive if true otherwise the upper limit is exclusive</param>
/// <exception cref="ArgumentNullException"></exception>
public void DoForEach<TKey>(Func<T, bool> actionAndCancelFunction, TKey minKey, TKey maxKey, bool maxInclusive) where TKey : IComparable<T>
{
if (actionAndCancelFunction == null)
{
return;
}

if (minKey is null)
{
throw new ArgumentNullException(nameof(minKey));
}

if (maxKey is null)
{
throw new ArgumentNullException(nameof(maxKey));
}

try
{
Interlocked.Add(ref _IterationCount, 1);
_Root.DoForEach(actionAndCancelFunction, minKey, maxKey, maxInclusive);
}
finally
{
Interlocked.Add(ref _IterationCount, -1);
}
}

/// <summary>
/// Performs an action for every single item.
/// Use this over <see cref="GetAll"/> if performance is critical.
/// </summary>
/// <param name="action"></param>
/// <param name="action">Function that will be called for every item.</param>
public void DoForEach(Action<T> action)
{
if (action == null)
Expand All @@ -1117,6 +1233,29 @@ public void DoForEach(Action<T> action)
}
}

/// <summary>
/// Performs an action for every single item. It offers the possibility to cancel the iteration.
/// Use this over <see cref="GetAll"/> if performance is critical.
/// </summary>
/// <param name="actionAndCancelFunction">Function that will be called for every item. It returns true to cancel or false to continue.</param>
public void DoForEach(Func<T, bool> actionAndCancelFunction)
{
if (actionAndCancelFunction == null)
{
return;
}

try
{
Interlocked.Add(ref _IterationCount, 1);
_Root.DoForEach(actionAndCancelFunction);
}
finally
{
Interlocked.Add(ref _IterationCount, -1);
}
}

/// <summary>
/// Gets a range of items limited by an inclusive lower limit (<paramref name="minKey"/>) and an upper limit (<paramref name="maxKey"/>).
/// The parameter <paramref name="maxKey"/> and <paramref name="minKey"/> can be a reduced version of an item as long as they implement <see cref="IComparable{T}"/>.
Expand Down
8 changes: 4 additions & 4 deletions BTree/BTree.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
<Authors>DevAM</Authors>
<Company>DevAM</Company>
<Product>BTree</Product>
<Version>1.0.0</Version>
<Version>1.1.0</Version>
<Description>Simple and high performant BTree for C# and .NET.</Description>
<Copyright>DevAM</Copyright>
<Copyright>Copyright (c) 2024 DevAM. All Rights Reserved.</Copyright>
<PackageIcon>icon.jpeg</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryType>git</RepositoryType>
<PackageTags>BTree</PackageTags>
<AssemblyVersion>1.0.0</AssemblyVersion>
<FileVersion>1.0.0</FileVersion>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<AssemblyName>BTree</AssemblyName>
Expand Down

0 comments on commit 94e39a0

Please sign in to comment.