com.wallstop-studios.unity-helpers 2.0.0-rc80.6 → 2.0.0-rc80.8

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,
@@ -1,13 +1,13 @@
1
1
  namespace WallstopStudios.UnityHelpers.Utils
2
2
  {
3
3
  using System;
4
- using System.Collections.Concurrent;
5
4
  using System.Collections.Generic;
6
- using System.Reflection;
7
5
  using System.Text;
8
- using System.Threading;
9
6
  using UnityEngine;
10
- using WallstopStudios.UnityHelpers.Core.Helper;
7
+ #if !SINGLE_THREADED
8
+ using System.Threading;
9
+ using System.Collections.Concurrent;
10
+ #endif
11
11
 
12
12
  public static class Buffers
13
13
  {
@@ -38,66 +38,107 @@ namespace WallstopStudios.UnityHelpers.Utils
38
38
  public static readonly Stack<T> Stack = new();
39
39
  }
40
40
 
41
- public static class WallstopGenericPool<T>
42
- where T : new()
41
+ #if SINGLE_THREADED
42
+ public sealed class WallstopGenericPool<T> : IDisposable
43
43
  {
44
- public static Action<T> clearAction;
44
+ private readonly Func<T> _producer;
45
+ private readonly Action<T> _onGet;
46
+ private readonly Action<T> _onRelease;
47
+ private readonly Action<T> _onDispose;
45
48
 
46
- private static readonly List<T> _pool = new();
47
- private static readonly Action<T> _onDispose = Release;
49
+ private readonly Stack<T> _pool = new();
48
50
 
49
- static WallstopGenericPool()
51
+ public WallstopGenericPool(
52
+ Func<T> producer,
53
+ Action<T> onGet = null,
54
+ Action<T> onRelease = null,
55
+ Action<T> onDisposal = null
56
+ )
50
57
  {
51
- clearAction = GetClearAction();
58
+ _producer = producer ?? throw new ArgumentNullException(nameof(producer));
59
+ _onGet = onGet;
60
+ _onRelease = onRelease ?? (_ => { });
61
+ _onRelease += _pool.Push;
62
+ _onDispose = onDisposal;
52
63
  }
53
64
 
54
- public static PooledResource<T> Get()
65
+ public PooledResource<T> Get()
55
66
  {
56
- if (_pool.Count == 0)
67
+ if (!_pool.TryPop(out T value))
57
68
  {
58
- return new PooledResource<T>(new T(), _onDispose);
69
+ value = _producer();
59
70
  }
60
71
 
61
- int lastIndex = _pool.Count - 1;
62
- T instance = _pool[lastIndex];
63
- _pool.RemoveAt(lastIndex);
64
- return new PooledResource<T>(instance, _onDispose);
72
+ _onGet?.Invoke(value);
73
+ return new PooledResource<T>(value, _onRelease);
65
74
  }
66
75
 
67
- private static Action<T> GetClearAction()
76
+ public void Dispose()
68
77
  {
69
- try
78
+ if (_onDispose == null)
70
79
  {
71
- Type type = typeof(T);
72
- foreach (
73
- MethodInfo method in type.GetMethods(
74
- BindingFlags.Instance | BindingFlags.Public
75
- )
76
- )
77
- {
78
- if (
79
- string.Equals(method.Name, "Clear", StringComparison.Ordinal)
80
- && method.GetParameters().Length == 0
81
- )
82
- {
83
- return (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), method);
84
- }
85
- }
80
+ _pool.Clear();
81
+ return;
86
82
  }
87
- catch
83
+
84
+ while (_pool.TryPop(out T value))
88
85
  {
89
- // Swallow
86
+ _onDispose(value);
90
87
  }
91
- return null;
92
88
  }
89
+ }
90
+ #else
91
+ public sealed class WallstopGenericPool<T> : IDisposable
92
+ {
93
+ private readonly Func<T> _producer;
94
+ private readonly Action<T> _onGet;
95
+ private readonly Action<T> _onRelease;
96
+ private readonly Action<T> _onDispose;
97
+
98
+ private readonly ConcurrentStack<T> _pool = new();
93
99
 
94
- private static void Release(T resource)
100
+ public WallstopGenericPool(
101
+ Func<T> producer,
102
+ Action<T> onGet = null,
103
+ Action<T> onRelease = null,
104
+ Action<T> onDisposal = null
105
+ )
95
106
  {
96
- clearAction?.Invoke(resource);
97
- _pool.Add(resource);
107
+ _producer = producer ?? throw new ArgumentNullException(nameof(producer));
108
+ _onGet = onGet;
109
+ _onRelease = onRelease ?? (_ => { });
110
+ _onRelease += _pool.Push;
111
+ _onDispose = onDisposal;
112
+ }
113
+
114
+ public PooledResource<T> Get()
115
+ {
116
+ if (!_pool.TryPop(out T value))
117
+ {
118
+ value = _producer();
119
+ }
120
+
121
+ _onGet?.Invoke(value);
122
+ return new PooledResource<T>(value, _onRelease);
123
+ }
124
+
125
+ public void Dispose()
126
+ {
127
+ if (_onDispose == null)
128
+ {
129
+ _pool.Clear();
130
+ return;
131
+ }
132
+
133
+ while (_pool.TryPop(out T value))
134
+ {
135
+ _onDispose(value);
136
+ }
98
137
  }
99
138
  }
139
+ #endif
100
140
 
141
+ #if SINGLE_THREADED
101
142
  public static class WallstopArrayPool<T>
102
143
  {
103
144
  private static readonly Dictionary<int, List<T[]>> _pool = new();
@@ -150,12 +191,54 @@ namespace WallstopStudios.UnityHelpers.Utils
150
191
  pool.Add(resource);
151
192
  }
152
193
  }
194
+ #else
195
+ public static class WallstopArrayPool<T>
196
+ {
197
+ private static readonly ConcurrentDictionary<int, ConcurrentStack<T[]>> _pool = new();
198
+ private static readonly Action<T[]> _onRelease = Release;
199
+
200
+ public static PooledResource<T[]> Get(int size)
201
+ {
202
+ switch (size)
203
+ {
204
+ case < 0:
205
+ {
206
+ throw new ArgumentOutOfRangeException(
207
+ nameof(size),
208
+ size,
209
+ "Must be non-negative."
210
+ );
211
+ }
212
+ case 0:
213
+ {
214
+ return new PooledResource<T[]>(Array.Empty<T>(), _ => { });
215
+ }
216
+ }
217
+
218
+ ConcurrentStack<T[]> result = _pool.GetOrAdd(size, _ => new ConcurrentStack<T[]>());
219
+ if (!result.TryPop(out T[] array))
220
+ {
221
+ array = new T[size];
222
+ }
223
+
224
+ return new PooledResource<T[]>(array, _onRelease);
225
+ }
226
+
227
+ private static void Release(T[] resource)
228
+ {
229
+ int length = resource.Length;
230
+ Array.Clear(resource, 0, length);
231
+ ConcurrentStack<T[]> result = _pool.GetOrAdd(length, _ => new ConcurrentStack<T[]>());
232
+ result.Push(resource);
233
+ }
234
+ }
235
+ #endif
153
236
 
154
237
  #if SINGLE_THREADED
155
238
  public static class WallstopFastArrayPool<T>
156
239
  {
157
- private static readonly List<List<T[]>> _pool = new();
158
- private static readonly Action<T[]> _onDispose = Release;
240
+ private static readonly List<Stack<T[]>> _pool = new();
241
+ private static readonly Action<T[]> _onRelease = Release;
159
242
 
160
243
  public static PooledResource<T[]> Get(int size)
161
244
  {
@@ -180,36 +263,32 @@ namespace WallstopStudios.UnityHelpers.Utils
180
263
  _pool.Add(null);
181
264
  }
182
265
 
183
- List<T[]> pool = _pool[size];
266
+ Stack<T[]> pool = _pool[size];
184
267
  if (pool == null)
185
268
  {
186
- pool = new List<T[]>();
269
+ pool = new Stack<T[]>();
187
270
  _pool[size] = pool;
188
271
  }
189
272
 
190
- if (pool.Count == 0)
273
+ if (!pool.TryPop(out T[] instance))
191
274
  {
192
- return new PooledResource<T[]>(new T[size], _onDispose);
275
+ instance = new T[size];
193
276
  }
194
277
 
195
- int lastIndex = pool.Count - 1;
196
- T[] instance = pool[lastIndex];
197
- pool.RemoveAt(lastIndex);
198
- return new PooledResource<T[]>(instance, _onDispose);
278
+ return new PooledResource<T[]>(instance, _onRelease);
199
279
  }
200
280
 
201
281
  private static void Release(T[] resource)
202
282
  {
203
- _pool[resource.Length].Add(resource);
283
+ _pool[resource.Length].Push(resource);
204
284
  }
205
285
  }
206
286
  #else
207
-
208
287
  public static class WallstopFastArrayPool<T>
209
288
  {
210
289
  private static readonly ReaderWriterLockSlim _lock = new();
211
290
  private static readonly List<ConcurrentStack<T[]>> _pool = new();
212
- private static readonly Action<T[]> _onDispose = Release;
291
+ private static readonly Action<T[]> _onRelease = Release;
213
292
 
214
293
  public static PooledResource<T[]> Get(int size)
215
294
  {
@@ -331,11 +410,12 @@ namespace WallstopStudios.UnityHelpers.Utils
331
410
  }
332
411
  }
333
412
 
334
- if (pool.TryPop(out T[] instance))
413
+ if (!pool.TryPop(out T[] instance))
335
414
  {
336
- return new PooledResource<T[]>(instance, _onDispose);
415
+ instance = new T[size];
337
416
  }
338
- return new PooledResource<T[]>(new T[size], _onDispose);
417
+
418
+ return new PooledResource<T[]>(instance, _onRelease);
339
419
  }
340
420
 
341
421
  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 = WallstopGenericPool<List<int>>.Get();
24
- using PooledResource<List<int>> secondList = WallstopGenericPool<List<int>>.Get();
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 = WallstopGenericPool<List<int>>.Get();
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 = WallstopGenericPool<List<int>>.Get();
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 = WallstopGenericPool<List<int>>.Get())
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 = WallstopGenericPool<List<int>>.Get();
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 PooledResource<HashSet<string>> pooled = WallstopGenericPool<
684
- HashSet<string>
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 (PooledResource<List<int>> pooled = WallstopGenericPool<List<int>>.Get())
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(disposeCalled);
707
- }
708
- finally
709
- {
710
- WallstopGenericPool<List<int>>.clearAction -= Callback;
719
+ Assert.IsTrue(clearCalled);
711
720
  }
712
721
 
713
- return;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.wallstop-studios.unity-helpers",
3
- "version": "2.0.0-rc80.6",
3
+ "version": "2.0.0-rc80.8",
4
4
  "displayName": "Unity Helpers",
5
5
  "description": "Various Unity Helper Library",
6
6
  "dependencies": {},
@@ -35,28 +35,4 @@
35
35
  "scripts": {
36
36
  "test": "echo \"Error: no test specified\" && exit 1"
37
37
  }
38
- }
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
38
+ }