com.wallstop-studios.unity-helpers 2.0.0-rc80.6 → 2.0.0-rc80.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1013,6 +1013,33 @@ namespace WallstopStudios.UnityHelpers.Core.Helper
|
|
|
1013
1013
|
}
|
|
1014
1014
|
}
|
|
1015
1015
|
|
|
1016
|
+
public static Action<T> BuildParameterlessInstanceMethodIfExists<T>(string methodName)
|
|
1017
|
+
{
|
|
1018
|
+
try
|
|
1019
|
+
{
|
|
1020
|
+
Type type = typeof(T);
|
|
1021
|
+
foreach (
|
|
1022
|
+
MethodInfo method in type.GetMethods(
|
|
1023
|
+
BindingFlags.Instance | BindingFlags.Public
|
|
1024
|
+
)
|
|
1025
|
+
)
|
|
1026
|
+
{
|
|
1027
|
+
if (
|
|
1028
|
+
string.Equals(method.Name, methodName, StringComparison.Ordinal)
|
|
1029
|
+
&& method.GetParameters().Length == 0
|
|
1030
|
+
)
|
|
1031
|
+
{
|
|
1032
|
+
return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), method);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
catch
|
|
1037
|
+
{
|
|
1038
|
+
// Swallow
|
|
1039
|
+
}
|
|
1040
|
+
return null;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1016
1043
|
public static bool HasAttributeSafe(
|
|
1017
1044
|
ICustomAttributeProvider provider,
|
|
1018
1045
|
Type attributeType,
|
package/Runtime/Utils/Buffers.cs
CHANGED
|
@@ -3,11 +3,9 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
3
3
|
using System;
|
|
4
4
|
using System.Collections.Concurrent;
|
|
5
5
|
using System.Collections.Generic;
|
|
6
|
-
using System.Reflection;
|
|
7
6
|
using System.Text;
|
|
8
7
|
using System.Threading;
|
|
9
8
|
using UnityEngine;
|
|
10
|
-
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
11
9
|
|
|
12
10
|
public static class Buffers
|
|
13
11
|
{
|
|
@@ -38,33 +36,42 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
38
36
|
public static readonly Stack<T> Stack = new();
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
#if SINGLE_THREADED
|
|
40
|
+
public sealed class WallstopGenericPool<T> : IDisposable
|
|
43
41
|
{
|
|
44
|
-
|
|
42
|
+
private readonly Func<T> _producer;
|
|
43
|
+
private readonly Action<T> _onGet;
|
|
44
|
+
private readonly Action<T> _onRelease;
|
|
45
|
+
private readonly Action<T> _onDispose;
|
|
45
46
|
|
|
46
|
-
private
|
|
47
|
-
private static readonly Action<T> _onDispose = Release;
|
|
47
|
+
private readonly Stack<T> _pool = new();
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
public WallstopGenericPool(
|
|
50
|
+
Func<T> producer,
|
|
51
|
+
Action<T> onGet = null,
|
|
52
|
+
Action<T> onRelease = null,
|
|
53
|
+
Action<T> onDisposal = null
|
|
54
|
+
)
|
|
50
55
|
{
|
|
51
|
-
|
|
56
|
+
_producer = producer ?? throw new ArgumentNullException(nameof(producer));
|
|
57
|
+
_onGet = onGet;
|
|
58
|
+
_onRelease = onRelease ?? (_ => { });
|
|
59
|
+
_onRelease += _pool.Push;
|
|
60
|
+
_onDispose = onDisposal;
|
|
52
61
|
}
|
|
53
62
|
|
|
54
|
-
public
|
|
63
|
+
public PooledResource<T> Get()
|
|
55
64
|
{
|
|
56
|
-
if (_pool.
|
|
65
|
+
if (!_pool.TryPop(out T value))
|
|
57
66
|
{
|
|
58
|
-
|
|
67
|
+
value = _producer();
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
_pool.RemoveAt(lastIndex);
|
|
64
|
-
return new PooledResource<T>(instance, _onDispose);
|
|
70
|
+
_onGet?.Invoke(value);
|
|
71
|
+
return new PooledResource<T>(value, _onRelease);
|
|
65
72
|
}
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
public static Action<T> GetClearAction()
|
|
68
75
|
{
|
|
69
76
|
try
|
|
70
77
|
{
|
|
@@ -91,13 +98,72 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
91
98
|
return null;
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
|
|
101
|
+
public void Dispose()
|
|
95
102
|
{
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
if (_onDispose == null)
|
|
104
|
+
{
|
|
105
|
+
_pool.Clear();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
while (_pool.TryPop(out T value))
|
|
110
|
+
{
|
|
111
|
+
_onDispose(value);
|
|
112
|
+
}
|
|
98
113
|
}
|
|
99
114
|
}
|
|
115
|
+
#else
|
|
116
|
+
public sealed class WallstopGenericPool<T> : IDisposable
|
|
117
|
+
{
|
|
118
|
+
private readonly Func<T> _producer;
|
|
119
|
+
private readonly Action<T> _onGet;
|
|
120
|
+
private readonly Action<T> _onRelease;
|
|
121
|
+
private readonly Action<T> _onDispose;
|
|
122
|
+
|
|
123
|
+
private readonly ConcurrentStack<T> _pool = new();
|
|
124
|
+
|
|
125
|
+
public WallstopGenericPool(
|
|
126
|
+
Func<T> producer,
|
|
127
|
+
Action<T> onGet = null,
|
|
128
|
+
Action<T> onRelease = null,
|
|
129
|
+
Action<T> onDisposal = null
|
|
130
|
+
)
|
|
131
|
+
{
|
|
132
|
+
_producer = producer ?? throw new ArgumentNullException(nameof(producer));
|
|
133
|
+
_onGet = onGet;
|
|
134
|
+
_onRelease = onRelease ?? (_ => { });
|
|
135
|
+
_onRelease += _pool.Push;
|
|
136
|
+
_onDispose = onDisposal;
|
|
137
|
+
}
|
|
100
138
|
|
|
139
|
+
public PooledResource<T> Get()
|
|
140
|
+
{
|
|
141
|
+
if (!_pool.TryPop(out T value))
|
|
142
|
+
{
|
|
143
|
+
value = _producer();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
_onGet?.Invoke(value);
|
|
147
|
+
return new PooledResource<T>(value, _onRelease);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
public void Dispose()
|
|
151
|
+
{
|
|
152
|
+
if (_onDispose == null)
|
|
153
|
+
{
|
|
154
|
+
_pool.Clear();
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
while (_pool.TryPop(out T value))
|
|
159
|
+
{
|
|
160
|
+
_onDispose(value);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
#endif
|
|
165
|
+
|
|
166
|
+
#if SINGLE_THREADED
|
|
101
167
|
public static class WallstopArrayPool<T>
|
|
102
168
|
{
|
|
103
169
|
private static readonly Dictionary<int, List<T[]>> _pool = new();
|
|
@@ -150,12 +216,54 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
150
216
|
pool.Add(resource);
|
|
151
217
|
}
|
|
152
218
|
}
|
|
219
|
+
#else
|
|
220
|
+
public static class WallstopArrayPool<T>
|
|
221
|
+
{
|
|
222
|
+
private static readonly ConcurrentDictionary<int, ConcurrentStack<T[]>> _pool = new();
|
|
223
|
+
private static readonly Action<T[]> _onRelease = Release;
|
|
224
|
+
|
|
225
|
+
public static PooledResource<T[]> Get(int size)
|
|
226
|
+
{
|
|
227
|
+
switch (size)
|
|
228
|
+
{
|
|
229
|
+
case < 0:
|
|
230
|
+
{
|
|
231
|
+
throw new ArgumentOutOfRangeException(
|
|
232
|
+
nameof(size),
|
|
233
|
+
size,
|
|
234
|
+
"Must be non-negative."
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
case 0:
|
|
238
|
+
{
|
|
239
|
+
return new PooledResource<T[]>(Array.Empty<T>(), _ => { });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
ConcurrentStack<T[]> result = _pool.GetOrAdd(size, _ => new ConcurrentStack<T[]>());
|
|
244
|
+
if (!result.TryPop(out T[] array))
|
|
245
|
+
{
|
|
246
|
+
array = new T[size];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return new PooledResource<T[]>(array, _onRelease);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private static void Release(T[] resource)
|
|
253
|
+
{
|
|
254
|
+
int length = resource.Length;
|
|
255
|
+
Array.Clear(resource, 0, length);
|
|
256
|
+
ConcurrentStack<T[]> result = _pool.GetOrAdd(length, _ => new ConcurrentStack<T[]>());
|
|
257
|
+
result.Push(resource);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
#endif
|
|
153
261
|
|
|
154
262
|
#if SINGLE_THREADED
|
|
155
263
|
public static class WallstopFastArrayPool<T>
|
|
156
264
|
{
|
|
157
|
-
private static readonly List<
|
|
158
|
-
private static readonly Action<T[]>
|
|
265
|
+
private static readonly List<Stack<T[]>> _pool = new();
|
|
266
|
+
private static readonly Action<T[]> _onRelease = Release;
|
|
159
267
|
|
|
160
268
|
public static PooledResource<T[]> Get(int size)
|
|
161
269
|
{
|
|
@@ -180,27 +288,24 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
180
288
|
_pool.Add(null);
|
|
181
289
|
}
|
|
182
290
|
|
|
183
|
-
|
|
291
|
+
Stack<T[]> pool = _pool[size];
|
|
184
292
|
if (pool == null)
|
|
185
293
|
{
|
|
186
|
-
pool = new
|
|
294
|
+
pool = new Stack<T[]>();
|
|
187
295
|
_pool[size] = pool;
|
|
188
296
|
}
|
|
189
297
|
|
|
190
|
-
if (pool.
|
|
298
|
+
if (!pool.TryPop(out T[] instance))
|
|
191
299
|
{
|
|
192
|
-
|
|
300
|
+
instance = new T[size];
|
|
193
301
|
}
|
|
194
302
|
|
|
195
|
-
|
|
196
|
-
T[] instance = pool[lastIndex];
|
|
197
|
-
pool.RemoveAt(lastIndex);
|
|
198
|
-
return new PooledResource<T[]>(instance, _onDispose);
|
|
303
|
+
return new PooledResource<T[]>(instance, _onRelease);
|
|
199
304
|
}
|
|
200
305
|
|
|
201
306
|
private static void Release(T[] resource)
|
|
202
307
|
{
|
|
203
|
-
_pool[resource.Length].
|
|
308
|
+
_pool[resource.Length].Push(resource);
|
|
204
309
|
}
|
|
205
310
|
}
|
|
206
311
|
#else
|
|
@@ -209,7 +314,7 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
209
314
|
{
|
|
210
315
|
private static readonly ReaderWriterLockSlim _lock = new();
|
|
211
316
|
private static readonly List<ConcurrentStack<T[]>> _pool = new();
|
|
212
|
-
private static readonly Action<T[]>
|
|
317
|
+
private static readonly Action<T[]> _onRelease = Release;
|
|
213
318
|
|
|
214
319
|
public static PooledResource<T[]> Get(int size)
|
|
215
320
|
{
|
|
@@ -331,11 +436,12 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
331
436
|
}
|
|
332
437
|
}
|
|
333
438
|
|
|
334
|
-
if (pool.TryPop(out T[] instance))
|
|
439
|
+
if (!pool.TryPop(out T[] instance))
|
|
335
440
|
{
|
|
336
|
-
|
|
441
|
+
instance = new T[size];
|
|
337
442
|
}
|
|
338
|
-
|
|
443
|
+
|
|
444
|
+
return new PooledResource<T[]>(instance, _onRelease);
|
|
339
445
|
}
|
|
340
446
|
|
|
341
447
|
private static void Release(T[] resource)
|
|
@@ -16,12 +16,17 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
16
16
|
|
|
17
17
|
public sealed class BuffersTests
|
|
18
18
|
{
|
|
19
|
+
private readonly WallstopGenericPool<List<int>> _intPool = new(
|
|
20
|
+
() => new List<int>(),
|
|
21
|
+
onRelease: list => list.Clear()
|
|
22
|
+
);
|
|
23
|
+
|
|
19
24
|
[Test]
|
|
20
25
|
public void GenericPoolListTests()
|
|
21
26
|
{
|
|
22
27
|
{
|
|
23
|
-
using PooledResource<List<int>> firstList =
|
|
24
|
-
using PooledResource<List<int>> secondList =
|
|
28
|
+
using PooledResource<List<int>> firstList = _intPool.Get();
|
|
29
|
+
using PooledResource<List<int>> secondList = _intPool.Get();
|
|
25
30
|
Assert.AreNotEqual(firstList, secondList);
|
|
26
31
|
firstList.resource.Add(1);
|
|
27
32
|
Assert.AreEqual(1, firstList.resource.Count);
|
|
@@ -32,7 +37,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
32
37
|
}
|
|
33
38
|
{
|
|
34
39
|
// Ensure cleared
|
|
35
|
-
using PooledResource<List<int>> firstList =
|
|
40
|
+
using PooledResource<List<int>> firstList = _intPool.Get();
|
|
36
41
|
Assert.AreEqual(0, firstList.resource.Count);
|
|
37
42
|
}
|
|
38
43
|
}
|
|
@@ -654,7 +659,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
654
659
|
[Test]
|
|
655
660
|
public void WallstopGenericPoolGetReturnsValidPooledResource()
|
|
656
661
|
{
|
|
657
|
-
using PooledResource<List<int>> pooled =
|
|
662
|
+
using PooledResource<List<int>> pooled = _intPool.Get();
|
|
658
663
|
|
|
659
664
|
Assert.NotNull(pooled.resource);
|
|
660
665
|
Assert.AreEqual(0, pooled.resource.Count);
|
|
@@ -665,14 +670,14 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
665
670
|
{
|
|
666
671
|
List<int> firstList;
|
|
667
672
|
|
|
668
|
-
using (PooledResource<List<int>> pooled =
|
|
673
|
+
using (PooledResource<List<int>> pooled = _intPool.Get())
|
|
669
674
|
{
|
|
670
675
|
firstList = pooled.resource;
|
|
671
676
|
firstList.Add(42);
|
|
672
677
|
firstList.Add(100);
|
|
673
678
|
}
|
|
674
679
|
|
|
675
|
-
using PooledResource<List<int>> pooledReused =
|
|
680
|
+
using PooledResource<List<int>> pooledReused = _intPool.Get();
|
|
676
681
|
Assert.AreSame(firstList, pooledReused.resource);
|
|
677
682
|
Assert.AreEqual(0, pooledReused.resource.Count);
|
|
678
683
|
}
|
|
@@ -680,9 +685,8 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
680
685
|
[Test]
|
|
681
686
|
public void WallstopGenericPoolClearActionWorksWithCustomType()
|
|
682
687
|
{
|
|
683
|
-
using
|
|
684
|
-
|
|
685
|
-
>.Get();
|
|
688
|
+
using WallstopGenericPool<HashSet<string>> pool = new(() => new HashSet<string>());
|
|
689
|
+
using PooledResource<HashSet<string>> pooled = pool.Get();
|
|
686
690
|
|
|
687
691
|
pooled.resource.Add("test1");
|
|
688
692
|
pooled.resource.Add("test2");
|
|
@@ -692,29 +696,30 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
692
696
|
[Test]
|
|
693
697
|
public void PooledResourceDisposeCallsOnDisposeAction()
|
|
694
698
|
{
|
|
699
|
+
bool clearCalled = false;
|
|
695
700
|
bool disposeCalled = false;
|
|
696
|
-
WallstopGenericPool<List<int>>.clearAction ??= list => list.Clear();
|
|
697
|
-
WallstopGenericPool<List<int>>.clearAction += Callback;
|
|
698
|
-
try
|
|
699
701
|
{
|
|
700
|
-
using
|
|
702
|
+
using WallstopGenericPool<List<int>> pool = new(
|
|
703
|
+
() => new List<int>(),
|
|
704
|
+
onRelease: list =>
|
|
705
|
+
{
|
|
706
|
+
list.Clear();
|
|
707
|
+
clearCalled = true;
|
|
708
|
+
},
|
|
709
|
+
onDisposal: _ => disposeCalled = true
|
|
710
|
+
);
|
|
711
|
+
|
|
712
|
+
using (PooledResource<List<int>> pooled = pool.Get())
|
|
701
713
|
{
|
|
702
714
|
Assert.NotNull(pooled.resource);
|
|
715
|
+
Assert.IsFalse(clearCalled);
|
|
703
716
|
Assert.IsFalse(disposeCalled);
|
|
704
717
|
}
|
|
705
718
|
|
|
706
|
-
Assert.IsTrue(
|
|
707
|
-
}
|
|
708
|
-
finally
|
|
709
|
-
{
|
|
710
|
-
WallstopGenericPool<List<int>>.clearAction -= Callback;
|
|
719
|
+
Assert.IsTrue(clearCalled);
|
|
711
720
|
}
|
|
712
721
|
|
|
713
|
-
|
|
714
|
-
void Callback(List<int> list)
|
|
715
|
-
{
|
|
716
|
-
disposeCalled = true;
|
|
717
|
-
}
|
|
722
|
+
Assert.IsTrue(disposeCalled);
|
|
718
723
|
}
|
|
719
724
|
}
|
|
720
725
|
}
|
package/package.json
CHANGED