Skip to content

Commit 753842a

Browse files
authored
Merge pull request #199 from ovska/feat/highperformance/arraypool-ensurecapacity
Add EnsureCapacity to ArrayPoolExtensions
2 parents f3bd3d8 + 7145762 commit 753842a

File tree

2 files changed

+116
-6
lines changed

2 files changed

+116
-6
lines changed

CommunityToolkit.HighPerformance/Extensions/ArrayPoolExtensions.cs

+41
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,45 @@ public static void Resize<T>(this ArrayPool<T> pool, [NotNull] ref T[]? array, i
5353

5454
array = newArray;
5555
}
56+
57+
/// <summary>
58+
/// Ensures that when the method returns <paramref name="array"/> is not null and is at least <paramref name="capacity"/> in length.
59+
/// Contents of <paramref name="array"/> are not copied if a new array is rented.
60+
/// </summary>
61+
/// <typeparam name="T">The type of items into the target array given as input.</typeparam>
62+
/// <param name="pool">The target <see cref="ArrayPool{T}"/> instance used to rent and/or return the array.</param>
63+
/// <param name="array">The rented <typeparamref name="T"/> array to ensure capacity for, or <see langword="null"/> to rent a new array.</param>
64+
/// <param name="capacity">The minimum length of <paramref name="array"/> when the method returns.</param>
65+
/// <param name="clearArray">Indicates whether the contents of the array should be cleared if returned to the pool.</param>
66+
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="capacity"/> is less than 0.</exception>
67+
/// <remarks>When this method returns, the caller must not use any references to the old array anymore.</remarks>
68+
public static void EnsureCapacity<T>(this ArrayPool<T> pool, [NotNull] ref T[]? array, int capacity, bool clearArray = false)
69+
{
70+
if (capacity < 0)
71+
{
72+
ThrowArgumentOutOfRangeExceptionForNegativeArrayCapacity();
73+
}
74+
75+
if (array is null)
76+
{
77+
array = pool.Rent(capacity);
78+
}
79+
else if (array.Length < capacity)
80+
{
81+
// Ensure rent succeeds before returning the original array to the pool
82+
T[] newArray = pool.Rent(capacity);
83+
84+
pool.Return(array, clearArray);
85+
86+
array = newArray;
87+
}
88+
}
89+
90+
/// <summary>
91+
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "capacity" parameter is negative.
92+
/// </summary>
93+
private static void ThrowArgumentOutOfRangeExceptionForNegativeArrayCapacity()
94+
{
95+
throw new ArgumentOutOfRangeException("capacity", "The array capacity must be a positive number.");
96+
}
5697
}

tests/CommunityToolkit.HighPerformance.UnitTests/Extensions/Test_ArrayPoolExtensions.cs

+75-6
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ public class Test_ArrayPoolExtensions
1515
{
1616
[TestMethod]
1717
[ExpectedException(typeof(ArgumentOutOfRangeException))]
18-
public void Test_ArrayExtensions_InvalidSize()
18+
public void Test_ArrayPoolExtensions_Resize_InvalidSize()
1919
{
2020
int[]? array = null;
2121

2222
ArrayPool<int>.Shared.Resize(ref array, -1);
2323
}
2424

2525
[TestMethod]
26-
public void Test_ArrayExtensions_NewArray()
26+
public void Test_ArrayPoolExtensions_Resize_NewArray()
2727
{
2828
int[]? array = null;
2929

@@ -34,7 +34,7 @@ public void Test_ArrayExtensions_NewArray()
3434
}
3535

3636
[TestMethod]
37-
public void Test_ArrayExtensions_SameSize()
37+
public void Test_ArrayPoolExtensions_Resize_SameSize()
3838
{
3939
int[] array = ArrayPool<int>.Shared.Rent(10);
4040
int[] backup = array;
@@ -45,7 +45,7 @@ public void Test_ArrayExtensions_SameSize()
4545
}
4646

4747
[TestMethod]
48-
public void Test_ArrayExtensions_Expand()
48+
public void Test_ArrayPoolExtensions_Resize_Expand()
4949
{
5050
int[] array = ArrayPool<int>.Shared.Rent(16);
5151
int[] backup = array;
@@ -60,7 +60,7 @@ public void Test_ArrayExtensions_Expand()
6060
}
6161

6262
[TestMethod]
63-
public void Test_ArrayExtensions_Shrink()
63+
public void Test_ArrayPoolExtensions_Resize_Shrink()
6464
{
6565
int[] array = ArrayPool<int>.Shared.Rent(32);
6666
int[] backup = array;
@@ -75,7 +75,7 @@ public void Test_ArrayExtensions_Shrink()
7575
}
7676

7777
[TestMethod]
78-
public void Test_ArrayExtensions_Clear()
78+
public void Test_ArrayPoolExtensions_Resize_Clear()
7979
{
8080
int[] array = ArrayPool<int>.Shared.Rent(16);
8181
int[] backup = array;
@@ -87,4 +87,73 @@ public void Test_ArrayExtensions_Clear()
8787
Assert.AreNotSame(array, backup);
8888
Assert.IsTrue(backup.AsSpan(0, 16).ToArray().All(i => i == 0));
8989
}
90+
91+
[TestMethod]
92+
[ExpectedException(typeof(ArgumentOutOfRangeException))]
93+
public void Test_ArrayPoolExtensions_EnsureCapacity_InvalidCapacity()
94+
{
95+
int[]? array = null;
96+
97+
ArrayPool<int>.Shared.EnsureCapacity(ref array, -1);
98+
}
99+
100+
[TestMethod]
101+
public void Test_ArrayPoolExtensions_EnsureCapacity_IdenticalCapacity()
102+
{
103+
int[]? array = ArrayPool<int>.Shared.Rent(10);
104+
int[]? backup = array;
105+
106+
ArrayPool<int>.Shared.EnsureCapacity(ref array, 10);
107+
Assert.AreSame(backup, array);
108+
Assert.IsTrue(array.Length >= 10);
109+
}
110+
111+
[TestMethod]
112+
public void Test_ArrayPoolExtensions_EnsureCapacity_NewArray()
113+
{
114+
int[]? array = null;
115+
116+
ArrayPool<int>.Shared.EnsureCapacity(ref array, 7);
117+
118+
Assert.IsNotNull(array);
119+
Assert.IsTrue(array.Length >= 7);
120+
int[]? backup = array;
121+
122+
ArrayPool<int>.Shared.EnsureCapacity(ref array, 64);
123+
124+
Assert.AreNotSame(backup, array);
125+
Assert.IsTrue(array.Length >= 64);
126+
}
127+
128+
[TestMethod]
129+
public void Test_ArrayPoolExtensions_EnsureCapacity_SufficientCapacity()
130+
{
131+
int[]? array = ArrayPool<int>.Shared.Rent(16);
132+
int[]? backup = array;
133+
134+
ArrayPool<int>.Shared.EnsureCapacity(ref array, 8);
135+
Assert.AreSame(backup, array);
136+
137+
ArrayPool<int>.Shared.EnsureCapacity(ref array, 16);
138+
Assert.AreSame(backup, array);
139+
140+
ArrayPool<int>.Shared.EnsureCapacity(ref array, 0);
141+
Assert.AreSame(backup, array);
142+
}
143+
144+
[TestMethod]
145+
public void Test_ArrayPoolExtensions_EnsureCapacity_ClearArray()
146+
{
147+
int[]? array = ArrayPool<int>.Shared.Rent(16);
148+
int[]? backup = array;
149+
150+
array.AsSpan().Fill(7);
151+
Assert.IsTrue(backup.All(i => i == 7));
152+
153+
ArrayPool<int>.Shared.EnsureCapacity(ref array, 256, true);
154+
155+
Assert.AreNotSame(backup, array);
156+
Assert.IsTrue(backup.All(i => i == default));
157+
Assert.IsTrue(array.Length >= 256);
158+
}
90159
}

0 commit comments

Comments
 (0)