com.wallstop-studios.unity-helpers 2.1.0 → 2.1.2
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/AGENTS.md +1 -0
- package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
- package/{package-lock.json.meta → Docs/ILIST_SORTING_PERFORMANCE.md.meta} +1 -1
- package/Docs/INDEX.md +11 -1
- package/Docs/Images/random_generators.svg +7 -7
- package/Docs/RANDOM_PERFORMANCE.md +17 -14
- package/Docs/REFLECTION_HELPERS.md +84 -1
- package/Docs/REFLECTION_PERFORMANCE.md +169 -0
- package/Docs/REFLECTION_PERFORMANCE.md.meta +7 -0
- package/Docs/RELATIONAL_COMPONENTS.md +6 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
- package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
- package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
- package/Editor/Sprites/AnimationCopier.cs +1 -1
- package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
- package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
- package/Editor/Sprites/TextureResizerWizard.cs +4 -3
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
- package/README.md +33 -18
- package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
- package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
- package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
- package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
- package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
- package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
- package/Runtime/Core/CodeGen.meta +8 -0
- package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
- package/Runtime/Core/Extension/IListExtensions.cs +720 -12
- package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
- package/Runtime/Core/Helper/Objects.cs +1 -1
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
- package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
- package/Runtime/Core/Math/Line2D.cs +2 -4
- package/Runtime/Core/Math/Line3D.cs +2 -4
- package/Runtime/Core/Random/AbstractRandom.cs +52 -5
- package/Runtime/Core/Random/DotNetRandom.cs +3 -3
- package/Runtime/Core/Random/FlurryBurstRandom.cs +279 -0
- package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
- package/Runtime/Core/Random/IllusionFlow.cs +3 -3
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
- package/Runtime/Core/Random/PcgRandom.cs +6 -6
- package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
- package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
- package/Runtime/Core/Random/RomuDuo.cs +3 -3
- package/Runtime/Core/Random/SplitMix64.cs +3 -3
- package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
- package/Runtime/Core/Random/StormDropRandom.cs +271 -0
- package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
- package/Runtime/Core/Random/UnityRandom.cs +3 -3
- package/Runtime/Core/Random/WyRandom.cs +6 -4
- package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
- package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
- package/Runtime/Tags/AttributeMetadataCache.cs +316 -9
- package/Runtime/Tags/CosmeticEffectData.cs +1 -1
- package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
- package/Tests/Editor/Helper/HelpersTests.cs +2 -2
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
- package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
- package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
- package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
- package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
- package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
- package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
- package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
- package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
- package/Tests/Editor/Tags.meta +8 -0
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +4 -4
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
- package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
- package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
- package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
- package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
- package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
- package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +2 -2
- package/package.json +1 -1
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
|
@@ -22,6 +22,18 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
|
|
|
22
22
|
|
|
23
23
|
/// <summary>Insertion sort algorithm - efficient for small or nearly-sorted lists.</summary>
|
|
24
24
|
Insertion = 2,
|
|
25
|
+
|
|
26
|
+
/// <summary>Meteor sort algorithm - adaptive gap-based sorting variant.</summary>
|
|
27
|
+
Meteor = 3,
|
|
28
|
+
|
|
29
|
+
/// <summary>Pattern-defeating quicksort - adaptive quicksort with pattern detection.</summary>
|
|
30
|
+
PatternDefeatingQuickSort = 4,
|
|
31
|
+
|
|
32
|
+
/// <summary>Grail sort algorithm - stable mergesort leveraging pooled buffers.</summary>
|
|
33
|
+
Grail = 5,
|
|
34
|
+
|
|
35
|
+
/// <summary>Power sort algorithm - adaptive mergesort that exploits natural runs.</summary>
|
|
36
|
+
Power = 6,
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
/// <summary>
|
|
@@ -175,13 +187,22 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
|
|
|
175
187
|
/// <typeparam name="TComparer">The type of comparer.</typeparam>
|
|
176
188
|
/// <param name="array">The list to sort.</param>
|
|
177
189
|
/// <param name="comparer">The comparer to use for element comparisons.</param>
|
|
178
|
-
/// <param name="sortAlgorithm">
|
|
190
|
+
/// <param name="sortAlgorithm">
|
|
191
|
+
/// The sorting algorithm to use (Ghost, Meteor, PatternDefeatingQuickSort, Grail, Power, or Insertion).
|
|
192
|
+
/// Defaults to Ghost.
|
|
193
|
+
/// </param>
|
|
179
194
|
/// <remarks>
|
|
180
195
|
/// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
|
|
181
196
|
/// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
|
|
182
|
-
/// <para>
|
|
197
|
+
/// <para>
|
|
198
|
+
/// Performance: Ghost, Meteor, PatternDefeatingQuickSort, Grail, and Power sorts are O(n log n) on average.
|
|
199
|
+
/// Insertion sort is O(n^2) worst/average case.
|
|
200
|
+
/// </para>
|
|
183
201
|
/// <para>Allocations: No allocations.</para>
|
|
184
|
-
/// <para>
|
|
202
|
+
/// <para>
|
|
203
|
+
/// Edge cases: Empty or single element lists require no sorting. Ghost, Meteor, and PatternDefeatingQuickSort
|
|
204
|
+
/// are currently not stable. Grail and Power sorts are stable.
|
|
205
|
+
/// </para>
|
|
185
206
|
/// </remarks>
|
|
186
207
|
/// <exception cref="InvalidEnumArgumentException">Thrown when sortAlgorithm is not a valid SortAlgorithm value.</exception>
|
|
187
208
|
public static void Sort<T, TComparer>(
|
|
@@ -203,6 +224,26 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
|
|
|
203
224
|
InsertionSort(array, comparer);
|
|
204
225
|
return;
|
|
205
226
|
}
|
|
227
|
+
case SortAlgorithm.Meteor:
|
|
228
|
+
{
|
|
229
|
+
MeteorSort(array, comparer);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
case SortAlgorithm.PatternDefeatingQuickSort:
|
|
233
|
+
{
|
|
234
|
+
PatternDefeatingQuickSort(array, comparer);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
case SortAlgorithm.Grail:
|
|
238
|
+
{
|
|
239
|
+
GrailSort(array, comparer);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
case SortAlgorithm.Power:
|
|
243
|
+
{
|
|
244
|
+
PowerSort(array, comparer);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
206
247
|
default:
|
|
207
248
|
{
|
|
208
249
|
throw new InvalidEnumArgumentException(
|
|
@@ -232,22 +273,244 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
|
|
|
232
273
|
where TComparer : IComparer<T>
|
|
233
274
|
{
|
|
234
275
|
int arrayCount = array.Count;
|
|
235
|
-
|
|
276
|
+
if (arrayCount < 2)
|
|
236
277
|
{
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
InsertionSortRange(array, 0, arrayCount - 1, comparer);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/*
|
|
285
|
+
Implementation reference: Meteor Sort by Wiley Looper,
|
|
286
|
+
https://github.com/wileylooper/meteorsort/blob/master/meteorsort.cs
|
|
287
|
+
|
|
288
|
+
Note: Meteor Sort is currently not stable.
|
|
289
|
+
*/
|
|
290
|
+
/// <summary>
|
|
291
|
+
/// Sorts the elements in the list using the Meteor Sort algorithm, a gap-sequence-based hybrid sort.
|
|
292
|
+
/// </summary>
|
|
293
|
+
/// <typeparam name="T">The type of elements in the list.</typeparam>
|
|
294
|
+
/// <typeparam name="TComparer">The type of comparer.</typeparam>
|
|
295
|
+
/// <param name="array">The list to sort.</param>
|
|
296
|
+
/// <param name="comparer">The comparer to use for element comparisons.</param>
|
|
297
|
+
/// <remarks>
|
|
298
|
+
/// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
|
|
299
|
+
/// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
|
|
300
|
+
/// <para>Performance: O(n log n) average case using adaptive gap reductions.</para>
|
|
301
|
+
/// <para>Allocations: No allocations.</para>
|
|
302
|
+
/// <para>Edge cases: Not a stable sort - equal elements may be reordered.</para>
|
|
303
|
+
/// </remarks>
|
|
304
|
+
public static void MeteorSort<T, TComparer>(this IList<T> array, TComparer comparer)
|
|
305
|
+
where TComparer : IComparer<T>
|
|
306
|
+
{
|
|
307
|
+
int length = array.Count;
|
|
308
|
+
int gap = length;
|
|
309
|
+
|
|
310
|
+
int i;
|
|
311
|
+
int j;
|
|
312
|
+
while (gap > 15)
|
|
313
|
+
{
|
|
314
|
+
gap = ((gap >> 2) - (gap >> 4)) + (gap >> 3);
|
|
315
|
+
i = gap;
|
|
316
|
+
|
|
317
|
+
while (i < length)
|
|
240
318
|
{
|
|
241
|
-
|
|
242
|
-
j
|
|
319
|
+
T element = array[i];
|
|
320
|
+
j = i;
|
|
321
|
+
|
|
322
|
+
while (j >= gap && 0 < comparer.Compare(array[j - gap], element))
|
|
323
|
+
{
|
|
324
|
+
array[j] = array[j - gap];
|
|
325
|
+
j -= gap;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
array[j] = element;
|
|
329
|
+
i++;
|
|
243
330
|
}
|
|
244
|
-
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
i = 1;
|
|
334
|
+
gap = 0;
|
|
335
|
+
|
|
336
|
+
while (i < length)
|
|
337
|
+
{
|
|
338
|
+
T element = array[i];
|
|
339
|
+
j = i;
|
|
340
|
+
|
|
341
|
+
while (j > 0 && 0 < comparer.Compare(array[gap], element))
|
|
342
|
+
{
|
|
343
|
+
array[j] = array[gap];
|
|
344
|
+
j = gap;
|
|
345
|
+
gap--;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
array[j] = element;
|
|
349
|
+
gap = i;
|
|
350
|
+
i++;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/*
|
|
355
|
+
Implementation reference: Pattern-Defeating Quicksort by Orson Peters,
|
|
356
|
+
https://github.com/orlp/pdqsort (zlib License)
|
|
357
|
+
|
|
358
|
+
This is a C# adaptation that retains the pattern-detection heuristics while operating on IList<T>.
|
|
359
|
+
Note: PatternDefeatingQuickSort is not stable.
|
|
360
|
+
*/
|
|
361
|
+
/// <summary>
|
|
362
|
+
/// Sorts the elements in the list using pattern-defeating quicksort, an adaptive quicksort variant with
|
|
363
|
+
/// introspective fallbacks and pattern detection.
|
|
364
|
+
/// </summary>
|
|
365
|
+
/// <typeparam name="T">The type of elements in the list.</typeparam>
|
|
366
|
+
/// <typeparam name="TComparer">The type of comparer.</typeparam>
|
|
367
|
+
/// <param name="array">The list to sort.</param>
|
|
368
|
+
/// <param name="comparer">The comparer to use for element comparisons.</param>
|
|
369
|
+
/// <remarks>
|
|
370
|
+
/// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
|
|
371
|
+
/// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
|
|
372
|
+
/// <para>Performance: O(n log n) on average with protection against quadratic worst cases via heapsort fallback.</para>
|
|
373
|
+
/// <para>Allocations: No allocations.</para>
|
|
374
|
+
/// <para>Edge cases: Not a stable sort - equal elements may be reordered.</para>
|
|
375
|
+
/// </remarks>
|
|
376
|
+
public static void PatternDefeatingQuickSort<T, TComparer>(
|
|
377
|
+
this IList<T> array,
|
|
378
|
+
TComparer comparer
|
|
379
|
+
)
|
|
380
|
+
where TComparer : IComparer<T>
|
|
381
|
+
{
|
|
382
|
+
int count = array.Count;
|
|
383
|
+
if (count < 2)
|
|
384
|
+
{
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
int depthLimit = 2 * FloorLog2(count);
|
|
389
|
+
PatternDefeatingQuickSortRange(array, 0, count - 1, comparer, depthLimit);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/*
|
|
393
|
+
Implementation reference: Grail Sort by Mrrl (MIT License),
|
|
394
|
+
https://github.com/Mrrl/GrailSort
|
|
395
|
+
|
|
396
|
+
This adaptation uses pooled buffers instead of manual block buffers while keeping stability.
|
|
397
|
+
*/
|
|
398
|
+
/// <summary>
|
|
399
|
+
/// Sorts the elements in the list using the Grail Sort algorithm, a stable mergesort that adapts buffer usage.
|
|
400
|
+
/// </summary>
|
|
401
|
+
/// <typeparam name="T">The type of elements in the list.</typeparam>
|
|
402
|
+
/// <typeparam name="TComparer">The type of comparer.</typeparam>
|
|
403
|
+
/// <param name="array">The list to sort.</param>
|
|
404
|
+
/// <param name="comparer">The comparer to use for element comparisons.</param>
|
|
405
|
+
/// <remarks>
|
|
406
|
+
/// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
|
|
407
|
+
/// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
|
|
408
|
+
/// <para>Performance: O(n log n) worst/average case. Stable sort.</para>
|
|
409
|
+
/// <para>Allocations: Uses pooled temporary buffers sized to half of the list.</para>
|
|
410
|
+
/// <para>Edge cases: Empty or single element lists require no sorting.</para>
|
|
411
|
+
/// </remarks>
|
|
412
|
+
public static void GrailSort<T, TComparer>(this IList<T> array, TComparer comparer)
|
|
413
|
+
where TComparer : IComparer<T>
|
|
414
|
+
{
|
|
415
|
+
int count = array.Count;
|
|
416
|
+
if (count < 2)
|
|
417
|
+
{
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
int bufferLength = count / 2 + 1;
|
|
422
|
+
using PooledResource<T[]> bufferLease = WallstopFastArrayPool<T>.Get(
|
|
423
|
+
bufferLength,
|
|
424
|
+
out T[] buffer
|
|
425
|
+
);
|
|
426
|
+
GrailSortRange(array, buffer, 0, count - 1, comparer);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/*
|
|
430
|
+
Implementation reference: Powersort (Munro, Wild) - adaptive mergesort leveraging natural runs.
|
|
431
|
+
https://arxiv.org/abs/1805.04154 (Creative Commons Attribution 4.0)
|
|
432
|
+
|
|
433
|
+
This adaptation detects natural runs and merges them using pooled buffers, providing a stable adaptive sort.
|
|
434
|
+
*/
|
|
435
|
+
/// <summary>
|
|
436
|
+
/// Sorts the elements in the list using the Power Sort algorithm, which exploits existing runs and merges them
|
|
437
|
+
/// in near-optimal order.
|
|
438
|
+
/// </summary>
|
|
439
|
+
/// <typeparam name="T">The type of elements in the list.</typeparam>
|
|
440
|
+
/// <typeparam name="TComparer">The type of comparer.</typeparam>
|
|
441
|
+
/// <param name="array">The list to sort.</param>
|
|
442
|
+
/// <param name="comparer">The comparer to use for element comparisons.</param>
|
|
443
|
+
/// <remarks>
|
|
444
|
+
/// <para>Null handling: Throws NullReferenceException if array is null. Comparer behavior depends on implementation.</para>
|
|
445
|
+
/// <para>Thread safety: Not thread-safe. Modifies the list in place. No Unity main thread requirement.</para>
|
|
446
|
+
/// <para>Performance: O(n log n) worst/average case, approaching O(n) on partially sorted data. Stable sort.</para>
|
|
447
|
+
/// <para>Allocations: Uses pooled lists and buffers for run management and merging.</para>
|
|
448
|
+
/// <para>Edge cases: Empty or single element lists require no sorting.</para>
|
|
449
|
+
/// </remarks>
|
|
450
|
+
public static void PowerSort<T, TComparer>(this IList<T> array, TComparer comparer)
|
|
451
|
+
where TComparer : IComparer<T>
|
|
452
|
+
{
|
|
453
|
+
int count = array.Count;
|
|
454
|
+
if (count < 2)
|
|
455
|
+
{
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
using PooledResource<List<(int start, int length)>> runBuffer = Buffers<(
|
|
460
|
+
int start,
|
|
461
|
+
int length
|
|
462
|
+
)>.List.Get(out List<(int start, int length)> runs);
|
|
463
|
+
using PooledResource<List<(int start, int length)>> mergeBuffer = Buffers<(
|
|
464
|
+
int start,
|
|
465
|
+
int length
|
|
466
|
+
)>.List.Get(out List<(int start, int length)> mergedRuns);
|
|
467
|
+
|
|
468
|
+
CollectNaturalRuns(array, comparer, runs);
|
|
469
|
+
if (runs.Count <= 1)
|
|
470
|
+
{
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
int bufferLength = count / 2 + 1;
|
|
475
|
+
using PooledResource<T[]> tempLease = WallstopFastArrayPool<T>.Get(
|
|
476
|
+
bufferLength,
|
|
477
|
+
out T[] buffer
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
while (runs.Count > 1)
|
|
481
|
+
{
|
|
482
|
+
mergedRuns.Clear();
|
|
483
|
+
int runCount = runs.Count;
|
|
484
|
+
for (int i = 0; i < runCount; i += 2)
|
|
485
|
+
{
|
|
486
|
+
if (i + 1 >= runCount)
|
|
487
|
+
{
|
|
488
|
+
mergedRuns.Add(runs[i]);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
(int start, int length) leftRun = runs[i];
|
|
493
|
+
(int start, int length) rightRun = runs[i + 1];
|
|
494
|
+
MergeRuns(
|
|
495
|
+
array,
|
|
496
|
+
buffer,
|
|
497
|
+
leftRun.start,
|
|
498
|
+
leftRun.length,
|
|
499
|
+
rightRun.start,
|
|
500
|
+
rightRun.length,
|
|
501
|
+
comparer
|
|
502
|
+
);
|
|
503
|
+
mergedRuns.Add((leftRun.start, leftRun.length + rightRun.length));
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
(runs, mergedRuns) = (mergedRuns, runs);
|
|
245
507
|
}
|
|
246
508
|
}
|
|
247
509
|
|
|
248
510
|
/*
|
|
249
|
-
Implementation copyright Will Stafford Parsons
|
|
250
|
-
|
|
511
|
+
Implementation copyright Will Stafford Parsons.
|
|
512
|
+
Original reference: ghostsort by Will Stafford Parsons (formerly hosted on GitHub).
|
|
513
|
+
Repository is currently unavailable; preserved locally until an official mirror returns.
|
|
251
514
|
|
|
252
515
|
Note: Ghost Sort is currently not stable.
|
|
253
516
|
|
|
@@ -316,6 +579,451 @@ namespace WallstopStudios.UnityHelpers.Core.Extension
|
|
|
316
579
|
}
|
|
317
580
|
}
|
|
318
581
|
|
|
582
|
+
private static void PatternDefeatingQuickSortRange<T, TComparer>(
|
|
583
|
+
IList<T> array,
|
|
584
|
+
int left,
|
|
585
|
+
int right,
|
|
586
|
+
TComparer comparer,
|
|
587
|
+
int depthLimit
|
|
588
|
+
)
|
|
589
|
+
where TComparer : IComparer<T>
|
|
590
|
+
{
|
|
591
|
+
const int insertionThreshold = 16;
|
|
592
|
+
while (right - left > insertionThreshold)
|
|
593
|
+
{
|
|
594
|
+
if (depthLimit == 0)
|
|
595
|
+
{
|
|
596
|
+
HeapSortRange(array, left, right, comparer);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
int pivotIndex = SelectPivotIndex(array, left, right, comparer);
|
|
601
|
+
(int pivotStart, int pivotEnd, bool swapped) = PartitionRange(
|
|
602
|
+
array,
|
|
603
|
+
left,
|
|
604
|
+
right,
|
|
605
|
+
pivotIndex,
|
|
606
|
+
comparer
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
if (!swapped && IsRangeSorted(array, left, right, comparer))
|
|
610
|
+
{
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
depthLimit--;
|
|
615
|
+
|
|
616
|
+
int leftSize = pivotStart - left;
|
|
617
|
+
int rightSize = right - pivotEnd;
|
|
618
|
+
|
|
619
|
+
if (leftSize < rightSize)
|
|
620
|
+
{
|
|
621
|
+
if (leftSize > 0)
|
|
622
|
+
{
|
|
623
|
+
PatternDefeatingQuickSortRange(
|
|
624
|
+
array,
|
|
625
|
+
left,
|
|
626
|
+
pivotStart - 1,
|
|
627
|
+
comparer,
|
|
628
|
+
depthLimit
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
left = pivotEnd + 1;
|
|
632
|
+
}
|
|
633
|
+
else
|
|
634
|
+
{
|
|
635
|
+
if (rightSize > 0)
|
|
636
|
+
{
|
|
637
|
+
PatternDefeatingQuickSortRange(
|
|
638
|
+
array,
|
|
639
|
+
pivotEnd + 1,
|
|
640
|
+
right,
|
|
641
|
+
comparer,
|
|
642
|
+
depthLimit
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
right = pivotStart - 1;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
InsertionSortRange(array, left, right, comparer);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
private static int SelectPivotIndex<T, TComparer>(
|
|
653
|
+
IList<T> array,
|
|
654
|
+
int left,
|
|
655
|
+
int right,
|
|
656
|
+
TComparer comparer
|
|
657
|
+
)
|
|
658
|
+
where TComparer : IComparer<T>
|
|
659
|
+
{
|
|
660
|
+
int mid = left + ((right - left) >> 1);
|
|
661
|
+
if (0 < comparer.Compare(array[left], array[mid]))
|
|
662
|
+
{
|
|
663
|
+
array.Swap(left, mid);
|
|
664
|
+
}
|
|
665
|
+
if (0 < comparer.Compare(array[left], array[right]))
|
|
666
|
+
{
|
|
667
|
+
array.Swap(left, right);
|
|
668
|
+
}
|
|
669
|
+
if (0 < comparer.Compare(array[mid], array[right]))
|
|
670
|
+
{
|
|
671
|
+
array.Swap(mid, right);
|
|
672
|
+
}
|
|
673
|
+
return mid;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
private static (int pivotStart, int pivotEnd, bool swapped) PartitionRange<T, TComparer>(
|
|
677
|
+
IList<T> array,
|
|
678
|
+
int left,
|
|
679
|
+
int right,
|
|
680
|
+
int pivotIndex,
|
|
681
|
+
TComparer comparer
|
|
682
|
+
)
|
|
683
|
+
where TComparer : IComparer<T>
|
|
684
|
+
{
|
|
685
|
+
array.Swap(left, pivotIndex);
|
|
686
|
+
T pivot = array[left];
|
|
687
|
+
int i = left + 1;
|
|
688
|
+
int j = right;
|
|
689
|
+
bool swapped = false;
|
|
690
|
+
|
|
691
|
+
while (i <= j)
|
|
692
|
+
{
|
|
693
|
+
while (i <= j && comparer.Compare(array[i], pivot) < 0)
|
|
694
|
+
{
|
|
695
|
+
i++;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
while (i <= j && comparer.Compare(array[j], pivot) > 0)
|
|
699
|
+
{
|
|
700
|
+
j--;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
if (i > j)
|
|
704
|
+
{
|
|
705
|
+
break;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
if (i < j)
|
|
709
|
+
{
|
|
710
|
+
array.Swap(i, j);
|
|
711
|
+
swapped = true;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
i++;
|
|
715
|
+
j--;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
int pivotPosition = j;
|
|
719
|
+
array.Swap(left, pivotPosition);
|
|
720
|
+
|
|
721
|
+
int pivotStart = pivotPosition;
|
|
722
|
+
int pivotEnd = pivotPosition;
|
|
723
|
+
|
|
724
|
+
while (pivotStart > left && comparer.Compare(array[pivotStart - 1], pivot) == 0)
|
|
725
|
+
{
|
|
726
|
+
pivotStart--;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
while (pivotEnd < right && comparer.Compare(array[pivotEnd + 1], pivot) == 0)
|
|
730
|
+
{
|
|
731
|
+
pivotEnd++;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
return (pivotStart, pivotEnd, swapped || pivotIndex != pivotPosition);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
private static void GrailSortRange<T, TComparer>(
|
|
738
|
+
IList<T> array,
|
|
739
|
+
T[] buffer,
|
|
740
|
+
int left,
|
|
741
|
+
int right,
|
|
742
|
+
TComparer comparer
|
|
743
|
+
)
|
|
744
|
+
where TComparer : IComparer<T>
|
|
745
|
+
{
|
|
746
|
+
if (left >= right)
|
|
747
|
+
{
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
int mid = left + ((right - left) >> 1);
|
|
752
|
+
GrailSortRange(array, buffer, left, mid, comparer);
|
|
753
|
+
GrailSortRange(array, buffer, mid + 1, right, comparer);
|
|
754
|
+
|
|
755
|
+
if (comparer.Compare(array[mid], array[mid + 1]) <= 0)
|
|
756
|
+
{
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
MergeRuns(array, buffer, left, mid - left + 1, mid + 1, right - mid, comparer);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
private static void MergeRuns<T, TComparer>(
|
|
764
|
+
IList<T> array,
|
|
765
|
+
T[] buffer,
|
|
766
|
+
int leftStart,
|
|
767
|
+
int leftLength,
|
|
768
|
+
int rightStart,
|
|
769
|
+
int rightLength,
|
|
770
|
+
TComparer comparer
|
|
771
|
+
)
|
|
772
|
+
where TComparer : IComparer<T>
|
|
773
|
+
{
|
|
774
|
+
if (leftLength == 0 || rightLength == 0)
|
|
775
|
+
{
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
int leftEnd = leftStart + leftLength - 1;
|
|
780
|
+
int rightEnd = rightStart + rightLength - 1;
|
|
781
|
+
if (comparer.Compare(array[leftEnd], array[rightStart]) <= 0)
|
|
782
|
+
{
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (leftLength <= rightLength)
|
|
787
|
+
{
|
|
788
|
+
for (int i = 0; i < leftLength; ++i)
|
|
789
|
+
{
|
|
790
|
+
buffer[i] = array[leftStart + i];
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
int leftIndex = 0;
|
|
794
|
+
int rightIndex = rightStart;
|
|
795
|
+
int dest = leftStart;
|
|
796
|
+
int leftLimit = leftLength;
|
|
797
|
+
|
|
798
|
+
while (leftIndex < leftLimit && rightIndex <= rightEnd)
|
|
799
|
+
{
|
|
800
|
+
if (0 < comparer.Compare(buffer[leftIndex], array[rightIndex]))
|
|
801
|
+
{
|
|
802
|
+
array[dest] = array[rightIndex];
|
|
803
|
+
rightIndex++;
|
|
804
|
+
}
|
|
805
|
+
else
|
|
806
|
+
{
|
|
807
|
+
array[dest] = buffer[leftIndex];
|
|
808
|
+
leftIndex++;
|
|
809
|
+
}
|
|
810
|
+
dest++;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
while (leftIndex < leftLimit)
|
|
814
|
+
{
|
|
815
|
+
array[dest] = buffer[leftIndex];
|
|
816
|
+
leftIndex++;
|
|
817
|
+
dest++;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
else
|
|
821
|
+
{
|
|
822
|
+
for (int i = 0; i < rightLength; ++i)
|
|
823
|
+
{
|
|
824
|
+
buffer[i] = array[rightStart + i];
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
int leftIndex = leftEnd;
|
|
828
|
+
int rightIndex = rightLength - 1;
|
|
829
|
+
int dest = rightEnd;
|
|
830
|
+
|
|
831
|
+
while (leftIndex >= leftStart && rightIndex >= 0)
|
|
832
|
+
{
|
|
833
|
+
if (0 < comparer.Compare(array[leftIndex], buffer[rightIndex]))
|
|
834
|
+
{
|
|
835
|
+
array[dest] = array[leftIndex];
|
|
836
|
+
leftIndex--;
|
|
837
|
+
}
|
|
838
|
+
else
|
|
839
|
+
{
|
|
840
|
+
array[dest] = buffer[rightIndex];
|
|
841
|
+
rightIndex--;
|
|
842
|
+
}
|
|
843
|
+
dest--;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
while (rightIndex >= 0)
|
|
847
|
+
{
|
|
848
|
+
array[dest] = buffer[rightIndex];
|
|
849
|
+
rightIndex--;
|
|
850
|
+
dest--;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
private static void CollectNaturalRuns<T, TComparer>(
|
|
856
|
+
IList<T> array,
|
|
857
|
+
TComparer comparer,
|
|
858
|
+
List<(int start, int length)> runs
|
|
859
|
+
)
|
|
860
|
+
where TComparer : IComparer<T>
|
|
861
|
+
{
|
|
862
|
+
runs.Clear();
|
|
863
|
+
int count = array.Count;
|
|
864
|
+
int index = 0;
|
|
865
|
+
while (index < count)
|
|
866
|
+
{
|
|
867
|
+
int start = index;
|
|
868
|
+
index++;
|
|
869
|
+
if (index == count)
|
|
870
|
+
{
|
|
871
|
+
runs.Add((start, 1));
|
|
872
|
+
break;
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
int compare = comparer.Compare(array[index - 1], array[index]);
|
|
876
|
+
bool ascending = compare <= 0;
|
|
877
|
+
|
|
878
|
+
while (index < count)
|
|
879
|
+
{
|
|
880
|
+
int nextCompare = comparer.Compare(array[index - 1], array[index]);
|
|
881
|
+
if (ascending)
|
|
882
|
+
{
|
|
883
|
+
if (nextCompare <= 0)
|
|
884
|
+
{
|
|
885
|
+
index++;
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
else
|
|
890
|
+
{
|
|
891
|
+
if (nextCompare >= 0)
|
|
892
|
+
{
|
|
893
|
+
index++;
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
int end = index - 1;
|
|
901
|
+
if (!ascending && start < end)
|
|
902
|
+
{
|
|
903
|
+
Reverse(array, start, end);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
runs.Add((start, end - start + 1));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
private static void InsertionSortRange<T, TComparer>(
|
|
911
|
+
IList<T> array,
|
|
912
|
+
int left,
|
|
913
|
+
int right,
|
|
914
|
+
TComparer comparer
|
|
915
|
+
)
|
|
916
|
+
where TComparer : IComparer<T>
|
|
917
|
+
{
|
|
918
|
+
if (left >= right)
|
|
919
|
+
{
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
for (int i = left + 1; i <= right; ++i)
|
|
924
|
+
{
|
|
925
|
+
T key = array[i];
|
|
926
|
+
int j = i - 1;
|
|
927
|
+
while (j >= left && 0 < comparer.Compare(array[j], key))
|
|
928
|
+
{
|
|
929
|
+
array[j + 1] = array[j];
|
|
930
|
+
j--;
|
|
931
|
+
}
|
|
932
|
+
array[j + 1] = key;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
private static void HeapSortRange<T, TComparer>(
|
|
937
|
+
IList<T> array,
|
|
938
|
+
int start,
|
|
939
|
+
int end,
|
|
940
|
+
TComparer comparer
|
|
941
|
+
)
|
|
942
|
+
where TComparer : IComparer<T>
|
|
943
|
+
{
|
|
944
|
+
int length = end - start + 1;
|
|
945
|
+
if (length <= 1)
|
|
946
|
+
{
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
for (int i = (length >> 1) - 1; i >= 0; --i)
|
|
951
|
+
{
|
|
952
|
+
SiftDown(array, start, length, i, comparer);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
for (int i = length - 1; i > 0; --i)
|
|
956
|
+
{
|
|
957
|
+
array.Swap(start, start + i);
|
|
958
|
+
SiftDown(array, start, i, 0, comparer);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
private static void SiftDown<T, TComparer>(
|
|
963
|
+
IList<T> array,
|
|
964
|
+
int start,
|
|
965
|
+
int length,
|
|
966
|
+
int root,
|
|
967
|
+
TComparer comparer
|
|
968
|
+
)
|
|
969
|
+
where TComparer : IComparer<T>
|
|
970
|
+
{
|
|
971
|
+
while (true)
|
|
972
|
+
{
|
|
973
|
+
int child = (root << 1) + 1;
|
|
974
|
+
if (child >= length)
|
|
975
|
+
{
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
int rightChild = child + 1;
|
|
980
|
+
if (
|
|
981
|
+
rightChild < length
|
|
982
|
+
&& comparer.Compare(array[start + child], array[start + rightChild]) < 0
|
|
983
|
+
)
|
|
984
|
+
{
|
|
985
|
+
child = rightChild;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
if (comparer.Compare(array[start + root], array[start + child]) >= 0)
|
|
989
|
+
{
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
array.Swap(start + root, start + child);
|
|
994
|
+
root = child;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
private static bool IsRangeSorted<T, TComparer>(
|
|
999
|
+
IList<T> array,
|
|
1000
|
+
int left,
|
|
1001
|
+
int right,
|
|
1002
|
+
TComparer comparer
|
|
1003
|
+
)
|
|
1004
|
+
where TComparer : IComparer<T>
|
|
1005
|
+
{
|
|
1006
|
+
for (int i = left + 1; i <= right; ++i)
|
|
1007
|
+
{
|
|
1008
|
+
if (0 < comparer.Compare(array[i - 1], array[i]))
|
|
1009
|
+
{
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
return true;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
private static int FloorLog2(int value)
|
|
1017
|
+
{
|
|
1018
|
+
int result = 0;
|
|
1019
|
+
while (value > 1)
|
|
1020
|
+
{
|
|
1021
|
+
value >>= 1;
|
|
1022
|
+
result++;
|
|
1023
|
+
}
|
|
1024
|
+
return result;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
319
1027
|
/// <summary>
|
|
320
1028
|
/// Sorts a list of Unity Objects by their name property in ascending alphabetical order.
|
|
321
1029
|
/// </summary>
|