com.wallstop-studios.unity-helpers 2.0.0-rc80.5 → 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.
- package/Runtime/Core/Helper/ReflectionHelpers.cs +27 -0
- package/Runtime/Core/Threading/SingleThreadedThreadPool.cs +2 -0
- package/Runtime/Utils/Buffers.cs +322 -20
- package/Tests/Runtime/Core/Threading/SingleThreadedThreadPoolTests.cs +2 -0
- package/Tests/Runtime/Utils/BuffersTests.cs +673 -3
- package/package.json +3 -1
|
@@ -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,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
namespace WallstopStudios.UnityHelpers.Core.Threading
|
|
2
2
|
{
|
|
3
|
+
#if !SINGLE_THREADED
|
|
3
4
|
using System;
|
|
4
5
|
using System.Collections.Concurrent;
|
|
5
6
|
using System.Threading;
|
|
@@ -223,4 +224,5 @@ namespace WallstopStudios.UnityHelpers.Core.Threading
|
|
|
223
224
|
}
|
|
224
225
|
}
|
|
225
226
|
}
|
|
227
|
+
#endif
|
|
226
228
|
}
|
package/Runtime/Utils/Buffers.cs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
namespace WallstopStudios.UnityHelpers.Utils
|
|
2
2
|
{
|
|
3
3
|
using System;
|
|
4
|
+
using System.Collections.Concurrent;
|
|
4
5
|
using System.Collections.Generic;
|
|
5
|
-
using System.Reflection;
|
|
6
6
|
using System.Text;
|
|
7
|
+
using System.Threading;
|
|
7
8
|
using UnityEngine;
|
|
8
|
-
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
9
9
|
|
|
10
10
|
public static class Buffers
|
|
11
11
|
{
|
|
@@ -36,33 +36,42 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
36
36
|
public static readonly Stack<T> Stack = new();
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
#if SINGLE_THREADED
|
|
40
|
+
public sealed class WallstopGenericPool<T> : IDisposable
|
|
41
41
|
{
|
|
42
|
-
|
|
42
|
+
private readonly Func<T> _producer;
|
|
43
|
+
private readonly Action<T> _onGet;
|
|
44
|
+
private readonly Action<T> _onRelease;
|
|
45
|
+
private readonly Action<T> _onDispose;
|
|
43
46
|
|
|
44
|
-
private
|
|
45
|
-
private static readonly Action<T> _onDispose = Release;
|
|
47
|
+
private readonly Stack<T> _pool = new();
|
|
46
48
|
|
|
47
|
-
|
|
49
|
+
public WallstopGenericPool(
|
|
50
|
+
Func<T> producer,
|
|
51
|
+
Action<T> onGet = null,
|
|
52
|
+
Action<T> onRelease = null,
|
|
53
|
+
Action<T> onDisposal = null
|
|
54
|
+
)
|
|
48
55
|
{
|
|
49
|
-
|
|
56
|
+
_producer = producer ?? throw new ArgumentNullException(nameof(producer));
|
|
57
|
+
_onGet = onGet;
|
|
58
|
+
_onRelease = onRelease ?? (_ => { });
|
|
59
|
+
_onRelease += _pool.Push;
|
|
60
|
+
_onDispose = onDisposal;
|
|
50
61
|
}
|
|
51
62
|
|
|
52
|
-
public
|
|
63
|
+
public PooledResource<T> Get()
|
|
53
64
|
{
|
|
54
|
-
if (_pool.
|
|
65
|
+
if (!_pool.TryPop(out T value))
|
|
55
66
|
{
|
|
56
|
-
|
|
67
|
+
value = _producer();
|
|
57
68
|
}
|
|
58
69
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
_pool.RemoveAt(lastIndex);
|
|
62
|
-
return new PooledResource<T>(instance, _onDispose);
|
|
70
|
+
_onGet?.Invoke(value);
|
|
71
|
+
return new PooledResource<T>(value, _onRelease);
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
public static Action<T> GetClearAction()
|
|
66
75
|
{
|
|
67
76
|
try
|
|
68
77
|
{
|
|
@@ -89,13 +98,72 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
89
98
|
return null;
|
|
90
99
|
}
|
|
91
100
|
|
|
92
|
-
|
|
101
|
+
public void Dispose()
|
|
93
102
|
{
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
if (_onDispose == null)
|
|
104
|
+
{
|
|
105
|
+
_pool.Clear();
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
while (_pool.TryPop(out T value))
|
|
110
|
+
{
|
|
111
|
+
_onDispose(value);
|
|
112
|
+
}
|
|
96
113
|
}
|
|
97
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();
|
|
98
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
|
+
}
|
|
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
|
|
99
167
|
public static class WallstopArrayPool<T>
|
|
100
168
|
{
|
|
101
169
|
private static readonly Dictionary<int, List<T[]>> _pool = new();
|
|
@@ -148,6 +216,240 @@ namespace WallstopStudios.UnityHelpers.Utils
|
|
|
148
216
|
pool.Add(resource);
|
|
149
217
|
}
|
|
150
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
|
|
261
|
+
|
|
262
|
+
#if SINGLE_THREADED
|
|
263
|
+
public static class WallstopFastArrayPool<T>
|
|
264
|
+
{
|
|
265
|
+
private static readonly List<Stack<T[]>> _pool = new();
|
|
266
|
+
private static readonly Action<T[]> _onRelease = Release;
|
|
267
|
+
|
|
268
|
+
public static PooledResource<T[]> Get(int size)
|
|
269
|
+
{
|
|
270
|
+
switch (size)
|
|
271
|
+
{
|
|
272
|
+
case < 0:
|
|
273
|
+
{
|
|
274
|
+
throw new ArgumentOutOfRangeException(
|
|
275
|
+
nameof(size),
|
|
276
|
+
size,
|
|
277
|
+
"Must be non-negative."
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
case 0:
|
|
281
|
+
{
|
|
282
|
+
return new PooledResource<T[]>(Array.Empty<T>(), _ => { });
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
while (_pool.Count <= size)
|
|
287
|
+
{
|
|
288
|
+
_pool.Add(null);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
Stack<T[]> pool = _pool[size];
|
|
292
|
+
if (pool == null)
|
|
293
|
+
{
|
|
294
|
+
pool = new Stack<T[]>();
|
|
295
|
+
_pool[size] = pool;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!pool.TryPop(out T[] instance))
|
|
299
|
+
{
|
|
300
|
+
instance = new T[size];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return new PooledResource<T[]>(instance, _onRelease);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private static void Release(T[] resource)
|
|
307
|
+
{
|
|
308
|
+
_pool[resource.Length].Push(resource);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
#else
|
|
312
|
+
|
|
313
|
+
public static class WallstopFastArrayPool<T>
|
|
314
|
+
{
|
|
315
|
+
private static readonly ReaderWriterLockSlim _lock = new();
|
|
316
|
+
private static readonly List<ConcurrentStack<T[]>> _pool = new();
|
|
317
|
+
private static readonly Action<T[]> _onRelease = Release;
|
|
318
|
+
|
|
319
|
+
public static PooledResource<T[]> Get(int size)
|
|
320
|
+
{
|
|
321
|
+
switch (size)
|
|
322
|
+
{
|
|
323
|
+
case < 0:
|
|
324
|
+
{
|
|
325
|
+
throw new ArgumentOutOfRangeException(
|
|
326
|
+
nameof(size),
|
|
327
|
+
size,
|
|
328
|
+
"Must be non-negative."
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
case 0:
|
|
332
|
+
{
|
|
333
|
+
return new PooledResource<T[]>(Array.Empty<T>(), _ => { });
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
bool withinRange;
|
|
338
|
+
ConcurrentStack<T[]> pool = null;
|
|
339
|
+
_lock.EnterReadLock();
|
|
340
|
+
try
|
|
341
|
+
{
|
|
342
|
+
withinRange = size < _pool.Count;
|
|
343
|
+
if (withinRange)
|
|
344
|
+
{
|
|
345
|
+
pool = _pool[size];
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
finally
|
|
349
|
+
{
|
|
350
|
+
_lock.ExitReadLock();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (withinRange)
|
|
354
|
+
{
|
|
355
|
+
if (pool == null)
|
|
356
|
+
{
|
|
357
|
+
_lock.EnterUpgradeableReadLock();
|
|
358
|
+
try
|
|
359
|
+
{
|
|
360
|
+
pool = _pool[size];
|
|
361
|
+
if (pool == null)
|
|
362
|
+
{
|
|
363
|
+
_lock.EnterWriteLock();
|
|
364
|
+
try
|
|
365
|
+
{
|
|
366
|
+
pool = _pool[size];
|
|
367
|
+
if (pool == null)
|
|
368
|
+
{
|
|
369
|
+
pool = new ConcurrentStack<T[]>();
|
|
370
|
+
_pool[size] = pool;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
finally
|
|
374
|
+
{
|
|
375
|
+
_lock.ExitWriteLock();
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
finally
|
|
380
|
+
{
|
|
381
|
+
_lock.ExitUpgradeableReadLock();
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else
|
|
386
|
+
{
|
|
387
|
+
_lock.EnterUpgradeableReadLock();
|
|
388
|
+
try
|
|
389
|
+
{
|
|
390
|
+
if (size < _pool.Count)
|
|
391
|
+
{
|
|
392
|
+
pool = _pool[size];
|
|
393
|
+
if (pool == null)
|
|
394
|
+
{
|
|
395
|
+
_lock.EnterWriteLock();
|
|
396
|
+
try
|
|
397
|
+
{
|
|
398
|
+
pool = _pool[size];
|
|
399
|
+
if (pool == null)
|
|
400
|
+
{
|
|
401
|
+
pool = new ConcurrentStack<T[]>();
|
|
402
|
+
_pool[size] = pool;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
finally
|
|
406
|
+
{
|
|
407
|
+
_lock.ExitWriteLock();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else
|
|
412
|
+
{
|
|
413
|
+
_lock.EnterWriteLock();
|
|
414
|
+
try
|
|
415
|
+
{
|
|
416
|
+
while (_pool.Count <= size)
|
|
417
|
+
{
|
|
418
|
+
_pool.Add(null);
|
|
419
|
+
}
|
|
420
|
+
pool = _pool[size];
|
|
421
|
+
if (pool == null)
|
|
422
|
+
{
|
|
423
|
+
pool = new ConcurrentStack<T[]>();
|
|
424
|
+
_pool[size] = pool;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
finally
|
|
428
|
+
{
|
|
429
|
+
_lock.ExitWriteLock();
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
finally
|
|
434
|
+
{
|
|
435
|
+
_lock.ExitUpgradeableReadLock();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (!pool.TryPop(out T[] instance))
|
|
440
|
+
{
|
|
441
|
+
instance = new T[size];
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return new PooledResource<T[]>(instance, _onRelease);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
private static void Release(T[] resource)
|
|
448
|
+
{
|
|
449
|
+
_pool[resource.Length].Push(resource);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
#endif
|
|
151
453
|
|
|
152
454
|
public readonly struct PooledResource<T> : IDisposable
|
|
153
455
|
{
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
namespace WallstopStudios.UnityHelpers.Tests.Core.Threading
|
|
2
2
|
{
|
|
3
|
+
#if !SINGLE_THREADED
|
|
3
4
|
using System.Collections;
|
|
4
5
|
using System.Collections.Generic;
|
|
5
6
|
using NUnit.Framework;
|
|
@@ -51,4 +52,5 @@ namespace WallstopStudios.UnityHelpers.Tests.Core.Threading
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
#endif
|
|
54
56
|
}
|
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
2
2
|
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections;
|
|
3
5
|
using System.Collections.Generic;
|
|
6
|
+
using System.Linq;
|
|
4
7
|
using NUnit.Framework;
|
|
8
|
+
using UnityEngine.TestTools;
|
|
9
|
+
using WallstopStudios.UnityHelpers.Core.Extension;
|
|
5
10
|
using WallstopStudios.UnityHelpers.Core.Random;
|
|
6
11
|
using WallstopStudios.UnityHelpers.Utils;
|
|
12
|
+
#if !SINGLETHREADED
|
|
13
|
+
using System.Threading;
|
|
14
|
+
using System.Threading.Tasks;
|
|
15
|
+
#endif
|
|
7
16
|
|
|
8
17
|
public sealed class BuffersTests
|
|
9
18
|
{
|
|
19
|
+
private readonly WallstopGenericPool<List<int>> _intPool = new(
|
|
20
|
+
() => new List<int>(),
|
|
21
|
+
onRelease: list => list.Clear()
|
|
22
|
+
);
|
|
23
|
+
|
|
10
24
|
[Test]
|
|
11
25
|
public void GenericPoolListTests()
|
|
12
26
|
{
|
|
13
27
|
{
|
|
14
|
-
using PooledResource<List<int>> firstList =
|
|
15
|
-
using PooledResource<List<int>> secondList =
|
|
28
|
+
using PooledResource<List<int>> firstList = _intPool.Get();
|
|
29
|
+
using PooledResource<List<int>> secondList = _intPool.Get();
|
|
16
30
|
Assert.AreNotEqual(firstList, secondList);
|
|
17
31
|
firstList.resource.Add(1);
|
|
18
32
|
Assert.AreEqual(1, firstList.resource.Count);
|
|
@@ -23,7 +37,7 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
23
37
|
}
|
|
24
38
|
{
|
|
25
39
|
// Ensure cleared
|
|
26
|
-
using PooledResource<List<int>> firstList =
|
|
40
|
+
using PooledResource<List<int>> firstList = _intPool.Get();
|
|
27
41
|
Assert.AreEqual(0, firstList.resource.Count);
|
|
28
42
|
}
|
|
29
43
|
}
|
|
@@ -51,5 +65,661 @@ namespace WallstopStudios.UnityHelpers.Tests.Utils
|
|
|
51
65
|
}
|
|
52
66
|
}
|
|
53
67
|
}
|
|
68
|
+
|
|
69
|
+
[Test]
|
|
70
|
+
public void WallstopFastArrayPoolGetNegativeSizeThrowsArgumentOutOfRangeException()
|
|
71
|
+
{
|
|
72
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => WallstopFastArrayPool<int>.Get(-1));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
[Test]
|
|
76
|
+
public void WallstopFastArrayPoolGetZeroSizeReturnsEmptyArrayWithNoOpDispose()
|
|
77
|
+
{
|
|
78
|
+
using PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(0);
|
|
79
|
+
Assert.NotNull(pooled.resource);
|
|
80
|
+
Assert.AreEqual(0, pooled.resource.Length);
|
|
81
|
+
Assert.AreSame(Array.Empty<int>(), pooled.resource);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
[Test]
|
|
85
|
+
public void WallstopFastArrayPoolGetPositiveSizeReturnsArrayWithCorrectLength()
|
|
86
|
+
{
|
|
87
|
+
const int size = 10;
|
|
88
|
+
using PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(size);
|
|
89
|
+
|
|
90
|
+
Assert.NotNull(pooled.resource);
|
|
91
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
[Test]
|
|
95
|
+
public void WallstopFastArrayPoolGetSameSizeReusesArrayAfterDispose()
|
|
96
|
+
{
|
|
97
|
+
const int size = 5;
|
|
98
|
+
int[] firstArray;
|
|
99
|
+
|
|
100
|
+
using (PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(size))
|
|
101
|
+
{
|
|
102
|
+
firstArray = pooled.resource;
|
|
103
|
+
firstArray[0] = 42;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
using PooledResource<int[]> pooledReused = WallstopFastArrayPool<int>.Get(size);
|
|
107
|
+
Assert.AreSame(firstArray, pooledReused.resource);
|
|
108
|
+
Assert.AreEqual(42, pooledReused.resource[0]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[Test]
|
|
112
|
+
public void WallstopFastArrayPoolGetDifferentSizesReturnsCorrectArrays()
|
|
113
|
+
{
|
|
114
|
+
int[] sizes = { 1, 5, 10, 100, 1000 };
|
|
115
|
+
List<PooledResource<int[]>> pooledArrays = new();
|
|
116
|
+
|
|
117
|
+
try
|
|
118
|
+
{
|
|
119
|
+
foreach (int size in sizes)
|
|
120
|
+
{
|
|
121
|
+
PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(size);
|
|
122
|
+
pooledArrays.Add(pooled);
|
|
123
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
finally
|
|
127
|
+
{
|
|
128
|
+
foreach (PooledResource<int[]> pooled in pooledArrays)
|
|
129
|
+
{
|
|
130
|
+
pooled.Dispose();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
[Test]
|
|
136
|
+
public void WallstopFastArrayPoolArraysNotClearedOnRelease()
|
|
137
|
+
{
|
|
138
|
+
const int size = 10;
|
|
139
|
+
|
|
140
|
+
using (PooledResource<string[]> pooled = WallstopFastArrayPool<string>.Get(size))
|
|
141
|
+
{
|
|
142
|
+
for (int i = 0; i < size; i++)
|
|
143
|
+
{
|
|
144
|
+
pooled.resource[i] = $"test{i}";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
using PooledResource<string[]> pooledReused = WallstopFastArrayPool<string>.Get(size);
|
|
149
|
+
for (int i = 0; i < size; i++)
|
|
150
|
+
{
|
|
151
|
+
Assert.AreEqual($"test{i}", pooledReused.resource[i]);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
[Test]
|
|
156
|
+
public void WallstopFastArrayPoolPoolGrowsAsNeeded()
|
|
157
|
+
{
|
|
158
|
+
const int size = 7;
|
|
159
|
+
const int count = 5;
|
|
160
|
+
List<PooledResource<int[]>> pooledArrays = new();
|
|
161
|
+
|
|
162
|
+
try
|
|
163
|
+
{
|
|
164
|
+
for (int i = 0; i < count; i++)
|
|
165
|
+
{
|
|
166
|
+
PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(size);
|
|
167
|
+
pooledArrays.Add(pooled);
|
|
168
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
HashSet<int[]> distinctArrays = pooledArrays.Select(p => p.resource).ToHashSet();
|
|
172
|
+
Assert.AreEqual(count, distinctArrays.Count);
|
|
173
|
+
}
|
|
174
|
+
finally
|
|
175
|
+
{
|
|
176
|
+
foreach (PooledResource<int[]> pooled in pooledArrays)
|
|
177
|
+
{
|
|
178
|
+
pooled.Dispose();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
[Test]
|
|
184
|
+
public void WallstopFastArrayPoolDifferentTypesUseDifferentPools()
|
|
185
|
+
{
|
|
186
|
+
const int size = 10;
|
|
187
|
+
|
|
188
|
+
using PooledResource<int[]> intPooled = WallstopFastArrayPool<int>.Get(size);
|
|
189
|
+
using PooledResource<string[]> stringPooled = WallstopFastArrayPool<string>.Get(size);
|
|
190
|
+
using PooledResource<float[]> floatPooled = WallstopFastArrayPool<float>.Get(size);
|
|
191
|
+
|
|
192
|
+
Assert.AreEqual(size, intPooled.resource.Length);
|
|
193
|
+
Assert.AreEqual(size, stringPooled.resource.Length);
|
|
194
|
+
Assert.AreEqual(size, floatPooled.resource.Length);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
[Test]
|
|
198
|
+
public void WallstopFastArrayPoolLargeArraysWork()
|
|
199
|
+
{
|
|
200
|
+
const int size = 100000;
|
|
201
|
+
using PooledResource<byte[]> pooled = WallstopFastArrayPool<byte>.Get(size);
|
|
202
|
+
|
|
203
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
[Test]
|
|
207
|
+
public void WallstopFastArrayPoolNestedUsageWorks()
|
|
208
|
+
{
|
|
209
|
+
const int outerSize = 5;
|
|
210
|
+
const int innerSize = 3;
|
|
211
|
+
|
|
212
|
+
using PooledResource<int[]> outer = WallstopFastArrayPool<int>.Get(outerSize);
|
|
213
|
+
Assert.AreEqual(outerSize, outer.resource.Length);
|
|
214
|
+
Array.Clear(outer.resource, 0, outer.resource.Length);
|
|
215
|
+
outer.resource[0] = 1;
|
|
216
|
+
|
|
217
|
+
using (PooledResource<int[]> inner = WallstopFastArrayPool<int>.Get(innerSize))
|
|
218
|
+
{
|
|
219
|
+
inner.resource[0] = 2;
|
|
220
|
+
Assert.AreEqual(innerSize, inner.resource.Length);
|
|
221
|
+
Assert.AreEqual(1, outer.resource[0]);
|
|
222
|
+
Assert.AreEqual(2, inner.resource[0]);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
Assert.AreEqual(outerSize, outer.resource.Length);
|
|
226
|
+
Assert.AreEqual(1, outer.resource[0]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
[UnityTest]
|
|
230
|
+
public IEnumerator WallstopFastArrayPoolStressTest()
|
|
231
|
+
{
|
|
232
|
+
const int iterations = 1000;
|
|
233
|
+
const int maxSize = 100;
|
|
234
|
+
Random random = new(42);
|
|
235
|
+
|
|
236
|
+
for (int i = 0; i < iterations; i++)
|
|
237
|
+
{
|
|
238
|
+
int size = random.Next(1, maxSize);
|
|
239
|
+
using PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(size);
|
|
240
|
+
|
|
241
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
242
|
+
|
|
243
|
+
for (int j = 0; j < Math.Min(10, size); j++)
|
|
244
|
+
{
|
|
245
|
+
pooled.resource[j] = random.Next();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (i % 100 == 0)
|
|
249
|
+
{
|
|
250
|
+
yield return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
#if !SINGLETHREADED
|
|
256
|
+
[Test]
|
|
257
|
+
public void WallstopFastArrayPoolConcurrentAccessDifferentSizes()
|
|
258
|
+
{
|
|
259
|
+
const int threadCount = 10;
|
|
260
|
+
const int operationsPerThread = 100;
|
|
261
|
+
Task[] tasks = new Task[threadCount];
|
|
262
|
+
List<Exception> exceptions = new();
|
|
263
|
+
|
|
264
|
+
for (int t = 0; t < threadCount; t++)
|
|
265
|
+
{
|
|
266
|
+
int threadId = t;
|
|
267
|
+
tasks[t] = Task.Run(async () =>
|
|
268
|
+
{
|
|
269
|
+
try
|
|
270
|
+
{
|
|
271
|
+
PcgRandom random = new(threadId);
|
|
272
|
+
foreach (int i in Enumerable.Range(0, operationsPerThread).Shuffled(random))
|
|
273
|
+
{
|
|
274
|
+
int size = random.Next(1, 50) + threadId;
|
|
275
|
+
using PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(
|
|
276
|
+
size
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
280
|
+
|
|
281
|
+
for (int j = 0; j < Math.Min(5, size); j++)
|
|
282
|
+
{
|
|
283
|
+
pooled.resource[j] = threadId * 1000 + i * 10 + j;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
await Task.Delay(TimeSpan.FromMilliseconds(random.NextDouble()));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch (Exception ex)
|
|
290
|
+
{
|
|
291
|
+
lock (exceptions)
|
|
292
|
+
{
|
|
293
|
+
exceptions.Add(ex);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
Task.WaitAll(tasks);
|
|
300
|
+
|
|
301
|
+
if (exceptions.Count > 0)
|
|
302
|
+
{
|
|
303
|
+
throw new AggregateException(exceptions);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
[Test]
|
|
308
|
+
public void WallstopFastArrayPoolConcurrentAccessSameSize()
|
|
309
|
+
{
|
|
310
|
+
const int threadCount = 8;
|
|
311
|
+
const int operationsPerThread = 200;
|
|
312
|
+
const int arraySize = 25;
|
|
313
|
+
Task[] tasks = new Task[threadCount];
|
|
314
|
+
List<Exception> exceptions = new();
|
|
315
|
+
|
|
316
|
+
for (int t = 0; t < threadCount; t++)
|
|
317
|
+
{
|
|
318
|
+
int threadId = t;
|
|
319
|
+
tasks[t] = Task.Run(() =>
|
|
320
|
+
{
|
|
321
|
+
try
|
|
322
|
+
{
|
|
323
|
+
PcgRandom random = new(threadId);
|
|
324
|
+
foreach (int i in Enumerable.Range(0, operationsPerThread).Shuffled(random))
|
|
325
|
+
{
|
|
326
|
+
using PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(
|
|
327
|
+
arraySize
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
Array.Clear(pooled.resource, 0, pooled.resource.Length);
|
|
331
|
+
Assert.AreEqual(arraySize, pooled.resource.Length);
|
|
332
|
+
|
|
333
|
+
for (int j = 0; j < arraySize; j++)
|
|
334
|
+
{
|
|
335
|
+
Assert.AreEqual(0, pooled.resource[j]);
|
|
336
|
+
pooled.resource[j] = threadId * 1000 + i;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
catch (Exception ex)
|
|
341
|
+
{
|
|
342
|
+
lock (exceptions)
|
|
343
|
+
{
|
|
344
|
+
exceptions.Add(ex);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
Task.WaitAll(tasks);
|
|
351
|
+
|
|
352
|
+
if (exceptions.Count > 0)
|
|
353
|
+
{
|
|
354
|
+
throw new AggregateException(exceptions);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
[Test]
|
|
359
|
+
public void WallstopFastArrayPoolConcurrentAccessMixedSizes()
|
|
360
|
+
{
|
|
361
|
+
const int threadCount = 6;
|
|
362
|
+
const int operationsPerThread = 150;
|
|
363
|
+
Task[] tasks = new Task[threadCount];
|
|
364
|
+
List<Exception> exceptions = new();
|
|
365
|
+
int[] sizes = { 1, 5, 10, 20, 50, 100 };
|
|
366
|
+
|
|
367
|
+
for (int t = 0; t < threadCount; t++)
|
|
368
|
+
{
|
|
369
|
+
int threadId = t;
|
|
370
|
+
tasks[t] = Task.Run(async () =>
|
|
371
|
+
{
|
|
372
|
+
try
|
|
373
|
+
{
|
|
374
|
+
PcgRandom random = new(threadId + 100);
|
|
375
|
+
foreach (int i in Enumerable.Range(0, operationsPerThread).Shuffled(random))
|
|
376
|
+
{
|
|
377
|
+
int size = random.NextOf(sizes);
|
|
378
|
+
using PooledResource<string[]> pooled =
|
|
379
|
+
WallstopFastArrayPool<string>.Get(size);
|
|
380
|
+
Array.Clear(pooled.resource, 0, pooled.resource.Length);
|
|
381
|
+
|
|
382
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
383
|
+
|
|
384
|
+
for (int j = 0; j < size; j++)
|
|
385
|
+
{
|
|
386
|
+
Assert.IsNull(pooled.resource[j]);
|
|
387
|
+
pooled.resource[j] = $"T{threadId}-I{i}-J{j}";
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (i % 50 == 0)
|
|
391
|
+
{
|
|
392
|
+
await Task.Delay(TimeSpan.FromMilliseconds(random.NextDouble()));
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
catch (Exception ex)
|
|
397
|
+
{
|
|
398
|
+
lock (exceptions)
|
|
399
|
+
{
|
|
400
|
+
exceptions.Add(ex);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
Task.WaitAll(tasks);
|
|
407
|
+
|
|
408
|
+
if (exceptions.Count > 0)
|
|
409
|
+
{
|
|
410
|
+
throw new AggregateException(exceptions);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
[Test]
|
|
415
|
+
public void WallstopFastArrayPoolConcurrentAccessRapidAllocationDeallocation()
|
|
416
|
+
{
|
|
417
|
+
const int threadCount = 12;
|
|
418
|
+
const int operationsPerThread = 500;
|
|
419
|
+
Task[] tasks = new Task[threadCount];
|
|
420
|
+
List<Exception> exceptions = new();
|
|
421
|
+
|
|
422
|
+
for (int t = 0; t < threadCount; t++)
|
|
423
|
+
{
|
|
424
|
+
int threadId = t;
|
|
425
|
+
tasks[t] = Task.Run(() =>
|
|
426
|
+
{
|
|
427
|
+
try
|
|
428
|
+
{
|
|
429
|
+
PcgRandom random = new(threadId + 100);
|
|
430
|
+
foreach (int i in Enumerable.Range(0, operationsPerThread).Shuffled(random))
|
|
431
|
+
{
|
|
432
|
+
int size = random.Next(1, 30);
|
|
433
|
+
using PooledResource<byte[]> pooled = WallstopFastArrayPool<byte>.Get(
|
|
434
|
+
size
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
438
|
+
|
|
439
|
+
for (int j = 0; j < size; j++)
|
|
440
|
+
{
|
|
441
|
+
pooled.resource[j] = (byte)(threadId + i + j);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
catch (Exception ex)
|
|
446
|
+
{
|
|
447
|
+
lock (exceptions)
|
|
448
|
+
{
|
|
449
|
+
exceptions.Add(ex);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
Task.WaitAll(tasks);
|
|
456
|
+
|
|
457
|
+
if (exceptions.Count > 0)
|
|
458
|
+
{
|
|
459
|
+
throw new AggregateException(exceptions);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
[Test]
|
|
464
|
+
public void WallstopFastArrayPoolThreadSafetyPoolExpansion()
|
|
465
|
+
{
|
|
466
|
+
const int threadCount = 8;
|
|
467
|
+
const int maxPoolSize = 1000;
|
|
468
|
+
Task[] tasks = new Task[threadCount];
|
|
469
|
+
List<Exception> exceptions = new();
|
|
470
|
+
Barrier barrier = new(threadCount);
|
|
471
|
+
|
|
472
|
+
for (int t = 0; t < threadCount; t++)
|
|
473
|
+
{
|
|
474
|
+
int threadId = t;
|
|
475
|
+
tasks[t] = Task.Run(() =>
|
|
476
|
+
{
|
|
477
|
+
try
|
|
478
|
+
{
|
|
479
|
+
barrier.SignalAndWait();
|
|
480
|
+
|
|
481
|
+
for (int size = threadId + 1; size <= maxPoolSize; size += threadCount)
|
|
482
|
+
{
|
|
483
|
+
using PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(
|
|
484
|
+
size
|
|
485
|
+
);
|
|
486
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
487
|
+
|
|
488
|
+
pooled.resource[0] = threadId;
|
|
489
|
+
pooled.resource[size - 1] = threadId;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
catch (Exception ex)
|
|
493
|
+
{
|
|
494
|
+
lock (exceptions)
|
|
495
|
+
{
|
|
496
|
+
exceptions.Add(ex);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
Task.WaitAll(tasks);
|
|
503
|
+
|
|
504
|
+
if (exceptions.Count > 0)
|
|
505
|
+
{
|
|
506
|
+
throw new AggregateException(exceptions);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
[UnityTest]
|
|
511
|
+
public IEnumerator WallstopFastArrayPoolConcurrentStressTest()
|
|
512
|
+
{
|
|
513
|
+
const int threadCount = 4;
|
|
514
|
+
const int operationsPerThread = 250;
|
|
515
|
+
Task[] tasks = new Task[threadCount];
|
|
516
|
+
List<Exception> exceptions = new();
|
|
517
|
+
int completedThreads = 0;
|
|
518
|
+
|
|
519
|
+
for (int t = 0; t < threadCount; t++)
|
|
520
|
+
{
|
|
521
|
+
int threadId = t;
|
|
522
|
+
tasks[t] = Task.Run(() =>
|
|
523
|
+
{
|
|
524
|
+
try
|
|
525
|
+
{
|
|
526
|
+
PcgRandom random = new(threadId + 100);
|
|
527
|
+
foreach (int i in Enumerable.Range(0, operationsPerThread).Shuffled(random))
|
|
528
|
+
{
|
|
529
|
+
int size = random.Next(1, 100);
|
|
530
|
+
using PooledResource<float[]> pooled = WallstopFastArrayPool<float>.Get(
|
|
531
|
+
size
|
|
532
|
+
);
|
|
533
|
+
|
|
534
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
535
|
+
|
|
536
|
+
for (int j = 0; j < Math.Min(10, size); j++)
|
|
537
|
+
{
|
|
538
|
+
pooled.resource[j] = threadId * 1000.0f + i + j * 0.1f;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
catch (Exception ex)
|
|
543
|
+
{
|
|
544
|
+
lock (exceptions)
|
|
545
|
+
{
|
|
546
|
+
exceptions.Add(ex);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
finally
|
|
550
|
+
{
|
|
551
|
+
Interlocked.Increment(ref completedThreads);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
while (completedThreads < threadCount)
|
|
557
|
+
{
|
|
558
|
+
yield return null;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
Task.WaitAll(tasks);
|
|
562
|
+
|
|
563
|
+
if (exceptions.Count > 0)
|
|
564
|
+
{
|
|
565
|
+
throw new AggregateException(exceptions);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
#endif
|
|
569
|
+
|
|
570
|
+
[Test]
|
|
571
|
+
public void WallstopFastArrayPoolEdgeCaseVeryLargeSize()
|
|
572
|
+
{
|
|
573
|
+
const int veryLargeSize = 1000000;
|
|
574
|
+
using PooledResource<int[]> pooled = WallstopFastArrayPool<int>.Get(veryLargeSize);
|
|
575
|
+
|
|
576
|
+
Assert.AreEqual(veryLargeSize, pooled.resource.Length);
|
|
577
|
+
Assert.AreEqual(0, pooled.resource[0]);
|
|
578
|
+
Assert.AreEqual(0, pooled.resource[veryLargeSize - 1]);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
[Test]
|
|
582
|
+
public void WallstopFastArrayPoolEdgeCaseSizeOne()
|
|
583
|
+
{
|
|
584
|
+
using PooledResource<string[]> pooled = WallstopFastArrayPool<string>.Get(1);
|
|
585
|
+
Array.Clear(pooled.resource, 0, pooled.resource.Length);
|
|
586
|
+
Assert.AreEqual(1, pooled.resource.Length);
|
|
587
|
+
Assert.IsNull(pooled.resource[0]);
|
|
588
|
+
|
|
589
|
+
pooled.resource[0] = "test";
|
|
590
|
+
Assert.AreEqual("test", pooled.resource[0]);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
[Test]
|
|
594
|
+
public void WallstopFastArrayPoolPoolingBehaviorLifo()
|
|
595
|
+
{
|
|
596
|
+
const int size = 15;
|
|
597
|
+
int[][] arrays = new int[3][];
|
|
598
|
+
{
|
|
599
|
+
using PooledResource<int[]> pooled1 = WallstopFastArrayPool<int>.Get(size);
|
|
600
|
+
arrays[0] = pooled1.resource;
|
|
601
|
+
using PooledResource<int[]> pooled2 = WallstopFastArrayPool<int>.Get(size);
|
|
602
|
+
arrays[1] = pooled2.resource;
|
|
603
|
+
using PooledResource<int[]> pooled3 = WallstopFastArrayPool<int>.Get(size);
|
|
604
|
+
arrays[2] = pooled3.resource;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
using PooledResource<int[]> pooledReuse1 = WallstopFastArrayPool<int>.Get(size);
|
|
608
|
+
Assert.AreSame(arrays[0], pooledReuse1.resource);
|
|
609
|
+
|
|
610
|
+
using PooledResource<int[]> pooledReuse2 = WallstopFastArrayPool<int>.Get(size);
|
|
611
|
+
Assert.AreSame(arrays[1], pooledReuse2.resource);
|
|
612
|
+
|
|
613
|
+
using PooledResource<int[]> pooledReuse3 = WallstopFastArrayPool<int>.Get(size);
|
|
614
|
+
Assert.AreSame(arrays[2], pooledReuse3.resource);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
[Test]
|
|
618
|
+
public void WallstopArrayPoolGetNegativeSizeThrowsArgumentOutOfRangeException()
|
|
619
|
+
{
|
|
620
|
+
Assert.Throws<ArgumentOutOfRangeException>(() => WallstopArrayPool<int>.Get(-1));
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
[Test]
|
|
624
|
+
public void WallstopArrayPoolGetZeroSizeReturnsEmptyArrayWithNoOpDispose()
|
|
625
|
+
{
|
|
626
|
+
using PooledResource<int[]> pooled = WallstopArrayPool<int>.Get(0);
|
|
627
|
+
Assert.NotNull(pooled.resource);
|
|
628
|
+
Assert.AreEqual(0, pooled.resource.Length);
|
|
629
|
+
Assert.AreSame(Array.Empty<int>(), pooled.resource);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
[Test]
|
|
633
|
+
public void WallstopArrayPoolGetPositiveSizeReturnsArrayWithCorrectLength()
|
|
634
|
+
{
|
|
635
|
+
const int size = 10;
|
|
636
|
+
using PooledResource<int[]> pooled = WallstopArrayPool<int>.Get(size);
|
|
637
|
+
|
|
638
|
+
Assert.NotNull(pooled.resource);
|
|
639
|
+
Assert.AreEqual(size, pooled.resource.Length);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
[Test]
|
|
643
|
+
public void WallstopArrayPoolGetSameSizeReusesArrayAfterDispose()
|
|
644
|
+
{
|
|
645
|
+
const int size = 5;
|
|
646
|
+
int[] firstArray;
|
|
647
|
+
|
|
648
|
+
using (PooledResource<int[]> pooled = WallstopArrayPool<int>.Get(size))
|
|
649
|
+
{
|
|
650
|
+
firstArray = pooled.resource;
|
|
651
|
+
firstArray[0] = 42;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
using PooledResource<int[]> pooledReused = WallstopArrayPool<int>.Get(size);
|
|
655
|
+
Assert.AreSame(firstArray, pooledReused.resource);
|
|
656
|
+
Assert.AreEqual(0, pooledReused.resource[0]);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
[Test]
|
|
660
|
+
public void WallstopGenericPoolGetReturnsValidPooledResource()
|
|
661
|
+
{
|
|
662
|
+
using PooledResource<List<int>> pooled = _intPool.Get();
|
|
663
|
+
|
|
664
|
+
Assert.NotNull(pooled.resource);
|
|
665
|
+
Assert.AreEqual(0, pooled.resource.Count);
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
[Test]
|
|
669
|
+
public void WallstopGenericPoolGetReusesInstanceAfterDispose()
|
|
670
|
+
{
|
|
671
|
+
List<int> firstList;
|
|
672
|
+
|
|
673
|
+
using (PooledResource<List<int>> pooled = _intPool.Get())
|
|
674
|
+
{
|
|
675
|
+
firstList = pooled.resource;
|
|
676
|
+
firstList.Add(42);
|
|
677
|
+
firstList.Add(100);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
using PooledResource<List<int>> pooledReused = _intPool.Get();
|
|
681
|
+
Assert.AreSame(firstList, pooledReused.resource);
|
|
682
|
+
Assert.AreEqual(0, pooledReused.resource.Count);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
[Test]
|
|
686
|
+
public void WallstopGenericPoolClearActionWorksWithCustomType()
|
|
687
|
+
{
|
|
688
|
+
using WallstopGenericPool<HashSet<string>> pool = new(() => new HashSet<string>());
|
|
689
|
+
using PooledResource<HashSet<string>> pooled = pool.Get();
|
|
690
|
+
|
|
691
|
+
pooled.resource.Add("test1");
|
|
692
|
+
pooled.resource.Add("test2");
|
|
693
|
+
Assert.AreEqual(2, pooled.resource.Count);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
[Test]
|
|
697
|
+
public void PooledResourceDisposeCallsOnDisposeAction()
|
|
698
|
+
{
|
|
699
|
+
bool clearCalled = false;
|
|
700
|
+
bool disposeCalled = false;
|
|
701
|
+
{
|
|
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())
|
|
713
|
+
{
|
|
714
|
+
Assert.NotNull(pooled.resource);
|
|
715
|
+
Assert.IsFalse(clearCalled);
|
|
716
|
+
Assert.IsFalse(disposeCalled);
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
Assert.IsTrue(clearCalled);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
Assert.IsTrue(disposeCalled);
|
|
723
|
+
}
|
|
54
724
|
}
|
|
55
725
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "com.wallstop-studios.unity-helpers",
|
|
3
|
-
"version": "2.0.0-rc80.
|
|
3
|
+
"version": "2.0.0-rc80.7",
|
|
4
4
|
"displayName": "Unity Helpers",
|
|
5
5
|
"description": "Various Unity Helper Library",
|
|
6
6
|
"dependencies": {},
|
|
@@ -56,6 +56,8 @@
|
|
|
56
56
|
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
|
|
60
|
+
|
|
59
61
|
|
|
60
62
|
|
|
61
63
|
|