Skip to content

Commit 91fdb77

Browse files
authored
Merge pull request #701 from CommunityToolkit/dev/generator-array-pool
Improve ImmutableArrayBuilder<T> type in MVVM Toolkit generators
2 parents eef21e2 + d2f77a9 commit 91fdb77

File tree

3 files changed

+29
-206
lines changed

3 files changed

+29
-206
lines changed

src/CommunityToolkit.Mvvm.SourceGenerators/CommunityToolkit.Mvvm.SourceGenerators.projitems

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@
6767
<Compile Include="$(MSBuildThisFileDirectory)Helpers\EquatableArray{T}.cs" />
6868
<Compile Include="$(MSBuildThisFileDirectory)Helpers\HashCode.cs" />
6969
<Compile Include="$(MSBuildThisFileDirectory)Helpers\ImmutableArrayBuilder{T}.cs" />
70-
<Compile Include="$(MSBuildThisFileDirectory)Helpers\ObjectPool{T}.cs" />
7170
<Compile Include="$(MSBuildThisFileDirectory)Input\Models\CanExecuteExpressionType.cs" />
7271
<Compile Include="$(MSBuildThisFileDirectory)Input\Models\CommandInfo.cs" />
7372
<Compile Include="$(MSBuildThisFileDirectory)Input\RelayCommandGenerator.cs" />

src/CommunityToolkit.Mvvm.SourceGenerators/Helpers/ImmutableArrayBuilder{T}.cs

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
// more info in ThirdPartyNotices.txt in the root of the project.
77

88
using System;
9+
using System.Buffers;
910
using System.Collections.Immutable;
11+
using System.Diagnostics.CodeAnalysis;
1012
using System.Runtime.CompilerServices;
1113

1214
namespace CommunityToolkit.Mvvm.SourceGenerators.Helpers;
@@ -15,13 +17,8 @@ namespace CommunityToolkit.Mvvm.SourceGenerators.Helpers;
1517
/// A helper type to build sequences of values with pooled buffers.
1618
/// </summary>
1719
/// <typeparam name="T">The type of items to create sequences for.</typeparam>
18-
internal struct ImmutableArrayBuilder<T> : IDisposable
20+
internal ref struct ImmutableArrayBuilder<T>
1921
{
20-
/// <summary>
21-
/// The shared <see cref="ObjectPool{T}"/> instance to share <see cref="Writer"/> objects.
22-
/// </summary>
23-
private static readonly ObjectPool<Writer> SharedObjectPool = new(static () => new Writer());
24-
2522
/// <summary>
2623
/// The rented <see cref="Writer"/> instance to use.
2724
/// </summary>
@@ -33,7 +30,7 @@ internal struct ImmutableArrayBuilder<T> : IDisposable
3330
/// <returns>A <see cref="ImmutableArrayBuilder{T}"/> instance to write data to.</returns>
3431
public static ImmutableArrayBuilder<T> Rent()
3532
{
36-
return new(SharedObjectPool.Allocate());
33+
return new(new Writer());
3734
}
3835

3936
/// <summary>
@@ -46,7 +43,7 @@ private ImmutableArrayBuilder(Writer writer)
4643
}
4744

4845
/// <inheritdoc cref="ImmutableArray{T}.Builder.Count"/>
49-
public int Count
46+
public readonly int Count
5047
{
5148
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5249
get => this.writer!.Count;
@@ -55,6 +52,7 @@ public int Count
5552
/// <summary>
5653
/// Gets the data written to the underlying buffer so far, as a <see cref="ReadOnlySpan{T}"/>.
5754
/// </summary>
55+
[UnscopedRef]
5856
public readonly ReadOnlySpan<T> WrittenSpan
5957
{
6058
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -71,7 +69,7 @@ public readonly void Add(T item)
7169
/// Adds the specified items to the end of the array.
7270
/// </summary>
7371
/// <param name="items">The items to add at the end of the array.</param>
74-
public readonly void AddRange(ReadOnlySpan<T> items)
72+
public readonly void AddRange(scoped ReadOnlySpan<T> items)
7573
{
7674
this.writer!.AddRange(items);
7775
}
@@ -96,30 +94,25 @@ public override readonly string ToString()
9694
return this.writer!.WrittenSpan.ToString();
9795
}
9896

99-
/// <inheritdoc/>
97+
/// <inheritdoc cref="IDisposable.Dispose"/>
10098
public void Dispose()
10199
{
102100
Writer? writer = this.writer;
103101

104102
this.writer = null;
105103

106-
if (writer is not null)
107-
{
108-
writer.Clear();
109-
110-
SharedObjectPool.Free(writer);
111-
}
104+
writer?.Dispose();
112105
}
113106

114107
/// <summary>
115108
/// A class handling the actual buffer writing.
116109
/// </summary>
117-
private sealed class Writer
110+
private sealed class Writer : IDisposable
118111
{
119112
/// <summary>
120113
/// The underlying <typeparamref name="T"/> array.
121114
/// </summary>
122-
private T[] array;
115+
private T?[]? array;
123116

124117
/// <summary>
125118
/// The starting offset within <see cref="array"/>.
@@ -131,15 +124,7 @@ private sealed class Writer
131124
/// </summary>
132125
public Writer()
133126
{
134-
if (typeof(T) == typeof(char))
135-
{
136-
this.array = new T[1024];
137-
}
138-
else
139-
{
140-
this.array = new T[8];
141-
}
142-
127+
this.array = ArrayPool<T?>.Shared.Rent(typeof(T) == typeof(char) ? 1024 : 8);
143128
this.index = 0;
144129
}
145130

@@ -154,38 +139,38 @@ public int Count
154139
public ReadOnlySpan<T> WrittenSpan
155140
{
156141
[MethodImpl(MethodImplOptions.AggressiveInlining)]
157-
get => new(this.array, 0, this.index);
142+
get => new(this.array!, 0, this.index);
158143
}
159144

160145
/// <inheritdoc cref="ImmutableArrayBuilder{T}.Add"/>
161146
public void Add(T value)
162147
{
163148
EnsureCapacity(1);
164149

165-
this.array[this.index++] = value;
150+
this.array![this.index++] = value;
166151
}
167152

168153
/// <inheritdoc cref="ImmutableArrayBuilder{T}.AddRange"/>
169154
public void AddRange(ReadOnlySpan<T> items)
170155
{
171156
EnsureCapacity(items.Length);
172157

173-
items.CopyTo(this.array.AsSpan(this.index));
158+
items.CopyTo(this.array.AsSpan(this.index)!);
174159

175160
this.index += items.Length;
176161
}
177162

178-
/// <summary>
179-
/// Clears the items in the current writer.
180-
/// </summary>
181-
public void Clear()
163+
/// <inheritdoc/>
164+
public void Dispose()
182165
{
183-
if (typeof(T) != typeof(char))
166+
T?[]? array = this.array;
167+
168+
this.array = null;
169+
170+
if (array is not null)
184171
{
185-
this.array.AsSpan(0, this.index).Clear();
172+
ArrayPool<T?>.Shared.Return(array, clearArray: typeof(T) != typeof(char));
186173
}
187-
188-
this.index = 0;
189174
}
190175

191176
/// <summary>
@@ -195,7 +180,7 @@ public void Clear()
195180
[MethodImpl(MethodImplOptions.AggressiveInlining)]
196181
private void EnsureCapacity(int requestedSize)
197182
{
198-
if (requestedSize > this.array.Length - this.index)
183+
if (requestedSize > this.array!.Length - this.index)
199184
{
200185
ResizeBuffer(requestedSize);
201186
}
@@ -209,13 +194,15 @@ private void EnsureCapacity(int requestedSize)
209194
private void ResizeBuffer(int sizeHint)
210195
{
211196
int minimumSize = this.index + sizeHint;
212-
int requestedSize = Math.Max(this.array.Length * 2, minimumSize);
213197

214-
T[] newArray = new T[requestedSize];
198+
T?[] oldArray = this.array!;
199+
T?[] newArray = ArrayPool<T?>.Shared.Rent(minimumSize);
215200

216-
Array.Copy(this.array, newArray, this.index);
201+
Array.Copy(oldArray, newArray, this.index);
217202

218203
this.array = newArray;
204+
205+
ArrayPool<T?>.Shared.Return(oldArray, clearArray: typeof(T) != typeof(char));
219206
}
220207
}
221208
}

src/CommunityToolkit.Mvvm.SourceGenerators/Helpers/ObjectPool{T}.cs

Lines changed: 0 additions & 163 deletions
This file was deleted.

0 commit comments

Comments
 (0)