├── package.json.meta ├── Scellecs.Collections.asmdef.meta ├── Collections.cs.meta ├── Scellecs.Collections.asmdef ├── package.json ├── .gitignore ├── .gitattributes └── Collections.cs /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e8bb9def137424d709f01f6895691a13 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Scellecs.Collections.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e8e0c803a4e624f59b70e545ceb518a1 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Collections.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 933e30fab70b247589f76b206e96c19e 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Scellecs.Collections.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Scellecs.Collections", 3 | "references": [], 4 | "includePlatforms": [], 5 | "excludePlatforms": [], 6 | "allowUnsafeCode": true, 7 | "overrideReferences": false, 8 | "precompiledReferences": [], 9 | "autoReferenced": true, 10 | "defineConstraints": [], 11 | "versionDefines": [], 12 | "noEngineReferences": false 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.scellecs.collections", 3 | "author": "Oleg Morozov", 4 | "displayName": "Fast Collections", 5 | "description": "Fast and Simple Collection Types.", 6 | "unity": "2019.3", 7 | "version": "2021.1.0", 8 | "keywords": [ 9 | "collections" 10 | ], 11 | "dependencies": {}, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/scellecs/collections" 15 | } 16 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /[Ll]ibrary/ 2 | /[Tt]emp/ 3 | /[Oo]bj/ 4 | /[Bb]uild/ 5 | /[Bb]uilds/ 6 | /[Ll]ogs/ 7 | /[Ll]og/ 8 | /Assets/AssetStoreTools* 9 | /AssetBundles/ 10 | /Addressables/ 11 | /Assets/StreamingAssets/*.bundle* 12 | 13 | # Visual Studio 2015 cache directory 14 | /.vs/ 15 | 16 | # Autogenerated VS/MD/Consulo solution and project files 17 | ExportedObj/ 18 | .consulo/ 19 | .idea/ 20 | *.csproj 21 | *.unityproj 22 | *.sln 23 | *.suo 24 | *.tmp 25 | *.userprefs 26 | *.pidb 27 | *.booproj 28 | *.svd 29 | *.pdb 30 | 31 | # Unity3D generated meta files 32 | *.pidb.meta 33 | 34 | # Unity3D Generated File On Crash Reports 35 | sysinfo.txt 36 | 37 | # Builds 38 | *.apk 39 | 40 | # Rider 41 | /Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll 42 | /Assets/Plugins/Editor/JetBrains/JetBrains.Rider.Unity.Editor.Plugin.Repacked.dll.meta 43 | 44 | # Other 45 | /ServerData/ 46 | /Assets/StreamingAssets/ 47 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Git LFS tracking file types 2 | 3 | # IronSource 4 | *.a filter=lfs diff=lfs merge=lfs -text 5 | IS*Adapter filter=lfs diff=lfs merge=lfs -text 6 | GoogleMobileAds filter=lfs diff=lfs merge=lfs -text 7 | 8 | # Image 9 | *.jpg filter=lfs diff=lfs merge=lfs -text 10 | *.jpeg filter=lfs diff=lfs merge=lfs -text 11 | *.png filter=lfs diff=lfs merge=lfs -text 12 | *.gif filter=lfs diff=lfs merge=lfs -text 13 | *.psd filter=lfs diff=lfs merge=lfs -text 14 | *.ai filter=lfs diff=lfs merge=lfs -text 15 | *.tif filter=lfs diff=lfs merge=lfs -text 16 | *.tiff filter=lfs diff=lfs merge=lfs -text 17 | *.tga filter=lfs diff=lfs merge=lfs -text 18 | *.raw filter=lfs diff=lfs merge=lfs -text 19 | *.cubemap filter=lfs diff=lfs merge=lfs -text 20 | 21 | # Audio 22 | *.mp3 filter=lfs diff=lfs merge=lfs -text 23 | *.wav filter=lfs diff=lfs merge=lfs -text 24 | *.ogg filter=lfs diff=lfs merge=lfs -text 25 | *.flac filter=lfs diff=lfs merge=lfs -text 26 | 27 | # Video 28 | *.mp4 filter=lfs diff=lfs merge=lfs -text 29 | *.mov filter=lfs diff=lfs merge=lfs -text 30 | 31 | # 3D 32 | *.fbx filter=lfs diff=lfs merge=lfs -text 33 | *.FBX filter=lfs diff=lfs merge=lfs -text 34 | *.blend filter=lfs diff=lfs merge=lfs -text 35 | *.obj filter=lfs diff=lfs merge=lfs -text 36 | *.skp filter=lfs diff=lfs merge=lfs -text 37 | 38 | # Unity 39 | *.mat filter=lfs diff=lfs merge=lfs -text 40 | 41 | # etc 42 | *.a filter=lfs diff=lfs merge=lfs -text 43 | *.exr filter=lfs diff=lfs merge=lfs -text 44 | *.pdf filter=lfs diff=lfs merge=lfs -text 45 | *.zip filter=lfs diff=lfs merge=lfs -text 46 | *.rar filter=lfs diff=lfs merge=lfs -text 47 | *.7z filter=lfs diff=lfs merge=lfs -text 48 | *.tar.gz filter=lfs diff=lfs merge=lfs -text 49 | *.dll filter=lfs diff=lfs merge=lfs -text 50 | *.unitypackage filter=lfs diff=lfs merge=lfs -text 51 | *.aif filter=lfs diff=lfs merge=lfs -text 52 | *.ttf filter=lfs diff=lfs merge=lfs -text 53 | *.rns filter=lfs diff=lfs merge=lfs -text 54 | *.reason filter=lfs diff=lfs merge=lfs -text 55 | *.lxo filter=lfs diff=lfs merge=lfs -text 56 | *.bundle* filter=lfs diff=lfs merge=lfs -text 57 | LightingData.asset filter=lfs diff=lfs merge=lfs -text 58 | -------------------------------------------------------------------------------- /Collections.cs: -------------------------------------------------------------------------------- 1 | [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("Scellecs.Collections.TestSuite")] 2 | 3 | namespace Scellecs.Collections { 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Runtime.CompilerServices; 8 | using Unity.IL2CPP.CompilerServices; 9 | 10 | [Serializable] 11 | [Il2CppSetOption(Option.NullChecks, false)] 12 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 13 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 14 | public sealed class IntHashSet : IEnumerable { 15 | public int length; 16 | public int capacity; 17 | public int capacityMinusOne; 18 | public int lastIndex; 19 | public int freeIndex; 20 | 21 | public int[] buckets; 22 | public int[] slots; 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public IntHashSet() : this(0) { 26 | } 27 | 28 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 29 | public IntHashSet(int capacity) { 30 | this.lastIndex = 0; 31 | this.length = 0; 32 | this.freeIndex = -1; 33 | 34 | this.capacityMinusOne = HashHelpers.GetCapacity(capacity); 35 | this.capacity = this.capacityMinusOne + 1; 36 | this.buckets = new int[this.capacity]; 37 | this.slots = new int[this.capacity / 2]; 38 | } 39 | 40 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 41 | 42 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 43 | 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public Enumerator GetEnumerator() { 46 | Enumerator e; 47 | e.set = this; 48 | e.index = 0; 49 | e.current = default; 50 | return e; 51 | } 52 | 53 | [Il2CppSetOption(Option.NullChecks, false)] 54 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 55 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 56 | public unsafe struct Enumerator : IEnumerator { 57 | public IntHashSet set; 58 | 59 | public int index; 60 | public int current; 61 | 62 | public bool MoveNext() { 63 | fixed (int* slotsPtr = &this.set.slots[0]) { 64 | for (var len = this.set.lastIndex; this.index < len; ++this.index) { 65 | var v = *slotsPtr - 1; 66 | if (v < 0) { 67 | continue; 68 | } 69 | 70 | this.current = v; 71 | ++this.index; 72 | 73 | return true; 74 | } 75 | 76 | this.index = this.set.lastIndex + 1; 77 | this.current = default; 78 | return false; 79 | } 80 | } 81 | 82 | public int Current => this.current; 83 | 84 | object IEnumerator.Current => this.current; 85 | 86 | void IEnumerator.Reset() { 87 | this.index = 0; 88 | this.current = default; 89 | } 90 | 91 | public void Dispose() { 92 | } 93 | } 94 | } 95 | 96 | [Il2CppSetOption(Option.NullChecks, false)] 97 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 98 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 99 | public static unsafe class IntHashSetExtensions { 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public static bool Add(this IntHashSet hashSet, in int value) { 102 | var rem = value & hashSet.capacityMinusOne; 103 | 104 | fixed (int* slotsPtr = &hashSet.slots[0]) 105 | fixed (int* bucketsPtr = &hashSet.buckets[0]) { 106 | int* slot; 107 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = *(slot + 1)) { 108 | slot = slotsPtr + i; 109 | if (*slot - 1 == value) { 110 | return false; 111 | } 112 | } 113 | } 114 | 115 | int newIndex; 116 | if (hashSet.freeIndex >= 0) { 117 | newIndex = hashSet.freeIndex; 118 | fixed (int* s = &hashSet.slots[0]) { 119 | hashSet.freeIndex = *(s + newIndex + 1); 120 | } 121 | } 122 | else { 123 | if (hashSet.lastIndex == hashSet.capacity * 2) { 124 | var newCapacityMinusOne = HashHelpers.ExpandCapacity(hashSet.length); 125 | var newCapacity = newCapacityMinusOne + 1; 126 | 127 | ArrayHelpers.Grow(ref hashSet.slots, newCapacity * 2); 128 | 129 | var newBuckets = new int[newCapacity]; 130 | 131 | fixed (int* slotsPtr = &hashSet.slots[0]) 132 | fixed (int* bucketsPtr = &newBuckets[0]) { 133 | for (int i = 0, len = hashSet.lastIndex; i < len; i += 2) { 134 | var slotPtr = slotsPtr + i; 135 | 136 | var newResizeIndex = (*slotPtr - 1) & newCapacityMinusOne; 137 | var newCurrentBucket = bucketsPtr + newResizeIndex; 138 | 139 | *(slotPtr + 1) = *newCurrentBucket - 1; 140 | 141 | *newCurrentBucket = i + 1; 142 | } 143 | } 144 | 145 | hashSet.buckets = newBuckets; 146 | hashSet.capacityMinusOne = newCapacityMinusOne; 147 | hashSet.capacity = newCapacity; 148 | 149 | rem = value & newCapacityMinusOne; 150 | } 151 | 152 | newIndex = hashSet.lastIndex; 153 | hashSet.lastIndex += 2; 154 | } 155 | 156 | fixed (int* slotsPtr = &hashSet.slots[0]) 157 | fixed (int* bucketsPtr = &hashSet.buckets[0]) { 158 | var bucket = bucketsPtr + rem; 159 | var slot = slotsPtr + newIndex; 160 | 161 | *slot = value + 1; 162 | *(slot + 1) = *bucket - 1; 163 | 164 | *bucket = newIndex + 1; 165 | } 166 | 167 | ++hashSet.length; 168 | return true; 169 | } 170 | 171 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 172 | public static bool Remove(this IntHashSet hashSet, in int value) { 173 | fixed (int* slotsPtr = &hashSet.slots[0]) 174 | fixed (int* bucketsPtr = &hashSet.buckets[0]) { 175 | var rem = value & hashSet.capacityMinusOne; 176 | 177 | int next; 178 | var num = -1; 179 | 180 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = next) { 181 | var slot = slotsPtr + i; 182 | var slotNext = slot + 1; 183 | 184 | if (*slot - 1 == value) { 185 | if (num < 0) { 186 | *(bucketsPtr + rem) = *slotNext + 1; 187 | } 188 | else { 189 | *(slotsPtr + num + 1) = *slotNext; 190 | } 191 | 192 | *slot = -1; 193 | *slotNext = hashSet.freeIndex; 194 | 195 | if (--hashSet.length == 0) { 196 | hashSet.lastIndex = 0; 197 | hashSet.freeIndex = -1; 198 | } 199 | else { 200 | hashSet.freeIndex = i; 201 | } 202 | 203 | return true; 204 | } 205 | 206 | next = *slotNext; 207 | num = i; 208 | } 209 | 210 | return false; 211 | } 212 | } 213 | 214 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 215 | public static void CopyTo(this IntHashSet hashSet, int[] array) { 216 | fixed (int* slotsPtr = &hashSet.slots[0]) { 217 | var num = 0; 218 | for (int i = 0, li = hashSet.lastIndex, len = hashSet.length; i < li && num < len; ++i) { 219 | var v = *(slotsPtr + i) - 1; 220 | if (v < 0) { 221 | continue; 222 | } 223 | 224 | array[num] = v; 225 | ++num; 226 | } 227 | } 228 | } 229 | 230 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 231 | public static bool Has(this IntHashSet hashSet, in int key) { 232 | fixed (int* slotsPtr = &hashSet.slots[0]) 233 | fixed (int* bucketsPtr = &hashSet.buckets[0]) { 234 | var rem = key & hashSet.capacityMinusOne; 235 | 236 | int next; 237 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = next) { 238 | var slot = slotsPtr + i; 239 | if (*slot - 1 == key) { 240 | return true; 241 | } 242 | 243 | next = *(slot + 1); 244 | } 245 | 246 | return false; 247 | } 248 | } 249 | 250 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 251 | public static void Clear(this IntHashSet hashSet) { 252 | if (hashSet.lastIndex <= 0) { 253 | return; 254 | } 255 | 256 | Array.Clear(hashSet.slots, 0, hashSet.lastIndex); 257 | Array.Clear(hashSet.buckets, 0, hashSet.capacityMinusOne); 258 | hashSet.lastIndex = 0; 259 | hashSet.length = 0; 260 | hashSet.freeIndex = -1; 261 | } 262 | } 263 | 264 | [Serializable] 265 | [Il2CppSetOption(Option.NullChecks, false)] 266 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 267 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 268 | public sealed class IntHashMap : IEnumerable { 269 | public int length; 270 | public int capacity; 271 | public int capacityMinusOne; 272 | public int lastIndex; 273 | public int freeIndex; 274 | 275 | public int[] buckets; 276 | 277 | public T[] data; 278 | public Slot[] slots; 279 | 280 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 281 | public IntHashMap(in int capacity = 0) { 282 | this.lastIndex = 0; 283 | this.length = 0; 284 | this.freeIndex = -1; 285 | 286 | this.capacityMinusOne = HashHelpers.GetCapacity(capacity); 287 | this.capacity = this.capacityMinusOne + 1; 288 | 289 | this.buckets = new int[this.capacity]; 290 | this.slots = new Slot[this.capacity]; 291 | this.data = new T[this.capacity]; 292 | } 293 | 294 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 295 | 296 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 297 | 298 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 299 | public Enumerator GetEnumerator() { 300 | Enumerator e; 301 | e.hashMap = this; 302 | e.index = 0; 303 | e.current = default; 304 | return e; 305 | } 306 | 307 | [Serializable] 308 | [Il2CppSetOption(Option.NullChecks, false)] 309 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 310 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 311 | public struct Slot { 312 | public int key; 313 | public int next; 314 | } 315 | 316 | [Il2CppSetOption(Option.NullChecks, false)] 317 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 318 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 319 | public struct Enumerator : IEnumerator { 320 | public IntHashMap hashMap; 321 | 322 | public int index; 323 | public int current; 324 | 325 | public bool MoveNext() { 326 | for (; this.index < this.hashMap.lastIndex; ++this.index) { 327 | ref var slot = ref this.hashMap.slots[this.index]; 328 | if (slot.key - 1 < 0) { 329 | continue; 330 | } 331 | 332 | this.current = this.index; 333 | ++this.index; 334 | 335 | return true; 336 | } 337 | 338 | this.index = this.hashMap.lastIndex + 1; 339 | this.current = default; 340 | return false; 341 | } 342 | 343 | public int Current => this.current; 344 | 345 | object IEnumerator.Current => this.current; 346 | 347 | void IEnumerator.Reset() { 348 | this.index = 0; 349 | this.current = default; 350 | } 351 | 352 | public void Dispose() { 353 | } 354 | } 355 | } 356 | 357 | [Il2CppSetOption(Option.NullChecks, false)] 358 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 359 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 360 | public static class IntHashMapExtensions { 361 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 362 | public static bool Add(this IntHashMap hashMap, in int key, in T value, out int slotIndex) { 363 | var rem = key & hashMap.capacityMinusOne; 364 | 365 | for (var i = hashMap.buckets[rem] - 1; i >= 0; i = hashMap.slots[i].next) { 366 | if (hashMap.slots[i].key - 1 == key) { 367 | slotIndex = -1; 368 | return false; 369 | } 370 | } 371 | 372 | if (hashMap.freeIndex >= 0) { 373 | slotIndex = hashMap.freeIndex; 374 | hashMap.freeIndex = hashMap.slots[slotIndex].next; 375 | } 376 | else { 377 | if (hashMap.lastIndex == hashMap.capacity) { 378 | var newCapacityMinusOne = HashHelpers.ExpandCapacity(hashMap.length); 379 | var newCapacity = newCapacityMinusOne + 1; 380 | 381 | ArrayHelpers.Grow(ref hashMap.slots, newCapacity); 382 | ArrayHelpers.Grow(ref hashMap.data, newCapacity); 383 | 384 | var newBuckets = new int[newCapacity]; 385 | 386 | for (int i = 0, len = hashMap.lastIndex; i < len; ++i) { 387 | ref var slot = ref hashMap.slots[i]; 388 | 389 | var newResizeIndex = (slot.key - 1) & newCapacityMinusOne; 390 | slot.next = newBuckets[newResizeIndex] - 1; 391 | 392 | newBuckets[newResizeIndex] = i + 1; 393 | } 394 | 395 | hashMap.buckets = newBuckets; 396 | hashMap.capacity = newCapacity; 397 | hashMap.capacityMinusOne = newCapacityMinusOne; 398 | 399 | rem = key & hashMap.capacityMinusOne; 400 | } 401 | 402 | slotIndex = hashMap.lastIndex; 403 | ++hashMap.lastIndex; 404 | } 405 | 406 | ref var newSlot = ref hashMap.slots[slotIndex]; 407 | 408 | newSlot.key = key + 1; 409 | newSlot.next = hashMap.buckets[rem] - 1; 410 | 411 | hashMap.data[slotIndex] = value; 412 | 413 | hashMap.buckets[rem] = slotIndex + 1; 414 | 415 | ++hashMap.length; 416 | return true; 417 | } 418 | 419 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 420 | public static void Set(this IntHashMap hashMap, in int key, in T value, out int slotIndex) { 421 | var rem = key & hashMap.capacityMinusOne; 422 | 423 | for (var i = hashMap.buckets[rem] - 1; i >= 0; i = hashMap.slots[i].next) { 424 | if (hashMap.slots[i].key - 1 == key) { 425 | hashMap.data[i] = value; 426 | slotIndex = i; 427 | return; 428 | } 429 | } 430 | 431 | if (hashMap.freeIndex >= 0) { 432 | slotIndex = hashMap.freeIndex; 433 | hashMap.freeIndex = hashMap.slots[slotIndex].next; 434 | } 435 | else { 436 | if (hashMap.lastIndex == hashMap.capacity) { 437 | var newCapacityMinusOne = HashHelpers.ExpandCapacity(hashMap.length); 438 | var newCapacity = newCapacityMinusOne + 1; 439 | 440 | ArrayHelpers.Grow(ref hashMap.slots, newCapacity); 441 | ArrayHelpers.Grow(ref hashMap.data, newCapacity); 442 | 443 | var newBuckets = new int[newCapacity]; 444 | 445 | for (int i = 0, len = hashMap.lastIndex; i < len; ++i) { 446 | ref var slot = ref hashMap.slots[i]; 447 | var newResizeIndex = (slot.key - 1) & newCapacityMinusOne; 448 | slot.next = newBuckets[newResizeIndex] - 1; 449 | 450 | newBuckets[newResizeIndex] = i + 1; 451 | } 452 | 453 | hashMap.buckets = newBuckets; 454 | hashMap.capacity = newCapacity; 455 | hashMap.capacityMinusOne = newCapacityMinusOne; 456 | 457 | rem = key & hashMap.capacityMinusOne; 458 | } 459 | 460 | slotIndex = hashMap.lastIndex; 461 | ++hashMap.lastIndex; 462 | } 463 | 464 | ref var newSlot = ref hashMap.slots[slotIndex]; 465 | 466 | newSlot.key = key + 1; 467 | newSlot.next = hashMap.buckets[rem] - 1; 468 | 469 | hashMap.data[slotIndex] = value; 470 | 471 | hashMap.buckets[rem] = slotIndex + 1; 472 | 473 | ++hashMap.length; 474 | } 475 | 476 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 477 | public static bool Remove(this IntHashMap hashMap, in int key, out T lastValue) { 478 | var rem = key & hashMap.capacityMinusOne; 479 | 480 | int next; 481 | var num = -1; 482 | for (var i = hashMap.buckets[rem] - 1; i >= 0; i = next) { 483 | ref var slot = ref hashMap.slots[i]; 484 | if (slot.key - 1 == key) { 485 | if (num < 0) { 486 | hashMap.buckets[rem] = slot.next + 1; 487 | } 488 | else { 489 | hashMap.slots[num].next = slot.next; 490 | } 491 | 492 | lastValue = hashMap.data[i]; 493 | 494 | slot.key = -1; 495 | slot.next = hashMap.freeIndex; 496 | 497 | --hashMap.length; 498 | if (hashMap.length == 0) { 499 | hashMap.lastIndex = 0; 500 | hashMap.freeIndex = -1; 501 | } 502 | else { 503 | hashMap.freeIndex = i; 504 | } 505 | 506 | return true; 507 | } 508 | 509 | next = slot.next; 510 | num = i; 511 | } 512 | 513 | lastValue = default; 514 | return false; 515 | } 516 | 517 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 518 | public static bool Has(this IntHashMap hashMap, in int key) { 519 | var rem = key & hashMap.capacityMinusOne; 520 | 521 | int next; 522 | for (var i = hashMap.buckets[rem] - 1; i >= 0; i = next) { 523 | ref var slot = ref hashMap.slots[i]; 524 | if (slot.key - 1 == key) { 525 | return true; 526 | } 527 | 528 | next = slot.next; 529 | } 530 | 531 | return false; 532 | } 533 | 534 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 535 | public static bool TryGetValue(this IntHashMap hashMap, in int key, out T value) { 536 | var rem = key & hashMap.capacityMinusOne; 537 | 538 | int next; 539 | for (var i = hashMap.buckets[rem] - 1; i >= 0; i = next) { 540 | ref var slot = ref hashMap.slots[i]; 541 | if (slot.key - 1 == key) { 542 | value = hashMap.data[i]; 543 | return true; 544 | } 545 | 546 | next = slot.next; 547 | } 548 | 549 | value = default; 550 | return false; 551 | } 552 | 553 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 554 | public static T GetValueByKey(this IntHashMap hashMap, in int key) { 555 | var rem = key & hashMap.capacityMinusOne; 556 | 557 | int next; 558 | for (var i = hashMap.buckets[rem] - 1; i >= 0; i = next) { 559 | ref var slot = ref hashMap.slots[i]; 560 | if (slot.key - 1 == key) { 561 | return hashMap.data[i]; 562 | } 563 | 564 | next = slot.next; 565 | } 566 | 567 | return default; 568 | } 569 | 570 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 571 | public static T GetValueByIndex(this IntHashMap hashMap, in int index) => hashMap.data[index]; 572 | 573 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 574 | public static int GetKeyByIndex(this IntHashMap hashMap, in int index) => hashMap.slots[index].key - 1; 575 | 576 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 577 | public static int TryGetIndex(this IntHashMap hashMap, in int key) { 578 | var rem = key & hashMap.capacityMinusOne; 579 | 580 | int next; 581 | for (var i = hashMap.buckets[rem] - 1; i >= 0; i = next) { 582 | ref var slot = ref hashMap.slots[i]; 583 | if (slot.key - 1 == key) { 584 | return i; 585 | } 586 | 587 | next = slot.next; 588 | } 589 | 590 | return -1; 591 | } 592 | 593 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 594 | public static void CopyTo(this IntHashMap hashMap, T[] array) { 595 | var num = 0; 596 | for (int i = 0, li = hashMap.lastIndex; i < li && num < hashMap.length; ++i) { 597 | if (hashMap.slots[i].key - 1 < 0) { 598 | continue; 599 | } 600 | 601 | array[num] = hashMap.data[i]; 602 | ++num; 603 | } 604 | } 605 | 606 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 607 | public static void Clear(this IntHashMap hashMap) { 608 | if (hashMap.lastIndex <= 0) { 609 | return; 610 | } 611 | 612 | Array.Clear(hashMap.slots, 0, hashMap.lastIndex); 613 | Array.Clear(hashMap.buckets, 0, hashMap.capacity); 614 | Array.Clear(hashMap.data, 0, hashMap.capacity); 615 | 616 | hashMap.lastIndex = 0; 617 | hashMap.length = 0; 618 | hashMap.freeIndex = -1; 619 | } 620 | } 621 | 622 | [Serializable] 623 | [Il2CppSetOption(Option.NullChecks, false)] 624 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 625 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 626 | public sealed class UnsafeIntHashMap : IEnumerable where T : unmanaged { 627 | public int length; 628 | public int capacity; 629 | public int capacityMinusOne; 630 | public int lastIndex; 631 | public int freeIndex; 632 | 633 | public int[] buckets; 634 | 635 | public T[] data; 636 | public int[] slots; 637 | 638 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 639 | public UnsafeIntHashMap(in int capacity = 0) { 640 | this.lastIndex = 0; 641 | this.length = 0; 642 | this.freeIndex = -1; 643 | 644 | this.capacityMinusOne = HashHelpers.GetCapacity(capacity); 645 | this.capacity = this.capacityMinusOne + 1; 646 | 647 | this.buckets = new int[this.capacity]; 648 | this.slots = new int[this.capacity * 2]; 649 | this.data = new T[this.capacity]; 650 | } 651 | 652 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 653 | 654 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 655 | 656 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 657 | public Enumerator GetEnumerator() { 658 | Enumerator e; 659 | e.hashMap = this; 660 | e.index = 0; 661 | e.current = default; 662 | return e; 663 | } 664 | 665 | [Il2CppSetOption(Option.NullChecks, false)] 666 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 667 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 668 | public unsafe struct Enumerator : IEnumerator { 669 | public UnsafeIntHashMap hashMap; 670 | 671 | public int index; 672 | public int current; 673 | 674 | public bool MoveNext() { 675 | fixed (int* slotsPtr = &this.hashMap.slots[0]) { 676 | for (; this.index < this.hashMap.lastIndex; this.index += 2) { 677 | if (*(slotsPtr + this.index) - 1 < 0) { 678 | continue; 679 | } 680 | 681 | this.current = this.index; 682 | this.index += 2; 683 | 684 | return true; 685 | } 686 | } 687 | 688 | this.index = this.hashMap.lastIndex + 1; 689 | this.current = default; 690 | return false; 691 | } 692 | 693 | public int Current => this.current; 694 | 695 | object IEnumerator.Current => this.current; 696 | 697 | void IEnumerator.Reset() { 698 | this.index = 0; 699 | this.current = default; 700 | } 701 | 702 | public void Dispose() { 703 | } 704 | } 705 | } 706 | 707 | [Il2CppSetOption(Option.NullChecks, false)] 708 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 709 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 710 | public static unsafe class UnsafeIntHashMapExtensions { 711 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 712 | public static bool Add(this UnsafeIntHashMap hashMap, in int key, in T value, out int slotIndex) where T : unmanaged { 713 | var rem = key & hashMap.capacityMinusOne; 714 | 715 | fixed (int* slotsPtr = &hashMap.slots[0]) 716 | fixed (int* bucketsPtr = &hashMap.buckets[0]) { 717 | int* slot; 718 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = *(slot + 1)) { 719 | slot = slotsPtr + i; 720 | if (*slot - 1 == key) { 721 | slotIndex = -1; 722 | return false; 723 | } 724 | } 725 | } 726 | 727 | if (hashMap.freeIndex >= 0) { 728 | slotIndex = hashMap.freeIndex; 729 | fixed (int* s = &hashMap.slots[0]) { 730 | hashMap.freeIndex = *(s + slotIndex + 1); 731 | } 732 | } 733 | else { 734 | if (hashMap.lastIndex == hashMap.capacity * 2) { 735 | var newCapacityMinusOne = HashHelpers.ExpandCapacity(hashMap.length); 736 | var newCapacity = newCapacityMinusOne + 1; 737 | 738 | ArrayHelpers.Grow(ref hashMap.slots, newCapacity * 2); 739 | ArrayHelpers.Grow(ref hashMap.data, newCapacity); 740 | 741 | var newBuckets = new int[newCapacity]; 742 | 743 | fixed (int* slotsPtr = &hashMap.slots[0]) 744 | fixed (int* bucketsPtr = &newBuckets[0]) { 745 | for (int i = 0, len = hashMap.lastIndex; i < len; i += 2) { 746 | var slotPtr = slotsPtr + i; 747 | 748 | var newResizeIndex = (*slotPtr - 1) & newCapacityMinusOne; 749 | var newCurrentBucket = bucketsPtr + newResizeIndex; 750 | 751 | *(slotPtr + 1) = *newCurrentBucket - 1; 752 | 753 | *newCurrentBucket = i + 1; 754 | } 755 | } 756 | 757 | hashMap.buckets = newBuckets; 758 | hashMap.capacity = newCapacity; 759 | hashMap.capacityMinusOne = newCapacityMinusOne; 760 | 761 | rem = key & hashMap.capacityMinusOne; 762 | } 763 | 764 | slotIndex = hashMap.lastIndex; 765 | hashMap.lastIndex += 2; 766 | } 767 | 768 | fixed (int* slotsPtr = &hashMap.slots[0]) 769 | fixed (int* bucketsPtr = &hashMap.buckets[0]) 770 | fixed (T* dataPtr = &hashMap.data[0]) { 771 | var bucket = bucketsPtr + rem; 772 | var slot = slotsPtr + slotIndex; 773 | 774 | *slot = key + 1; 775 | *(slot + 1) = *bucket - 1; 776 | 777 | *(dataPtr + slotIndex / 2) = value; 778 | 779 | *bucket = slotIndex + 1; 780 | } 781 | 782 | ++hashMap.length; 783 | return true; 784 | } 785 | 786 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 787 | public static bool Remove(this UnsafeIntHashMap hashMap, in int key, out T lastValue) where T : unmanaged { 788 | fixed (int* slotsPtr = &hashMap.slots[0]) 789 | fixed (int* bucketsPtr = &hashMap.buckets[0]) 790 | fixed (T* dataPtr = &hashMap.data[0]) { 791 | var rem = key & hashMap.capacityMinusOne; 792 | 793 | int next; 794 | var num = -1; 795 | 796 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = next) { 797 | var slot = slotsPtr + i; 798 | var slotNext = slot + 1; 799 | 800 | if (*slot - 1 == key) { 801 | if (num < 0) { 802 | *(bucketsPtr + rem) = *slotNext + 1; 803 | } 804 | else { 805 | *(slotsPtr + num + 1) = *slotNext; 806 | } 807 | 808 | lastValue = *(dataPtr + i / 2); 809 | 810 | *slot = -1; 811 | *slotNext = hashMap.freeIndex; 812 | 813 | if (--hashMap.length == 0) { 814 | hashMap.lastIndex = 0; 815 | hashMap.freeIndex = -1; 816 | } 817 | else { 818 | hashMap.freeIndex = i; 819 | } 820 | 821 | return true; 822 | } 823 | 824 | next = *slotNext; 825 | num = i; 826 | } 827 | 828 | lastValue = default; 829 | return false; 830 | } 831 | } 832 | 833 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 834 | public static bool TryGetValue(this UnsafeIntHashMap hashMap, in int key, out T value) where T : unmanaged { 835 | var rem = key & hashMap.capacityMinusOne; 836 | 837 | fixed (int* slotsPtr = &hashMap.slots[0]) 838 | fixed (int* bucketsPtr = &hashMap.buckets[0]) 839 | fixed (T* dataPtr = &hashMap.data[0]) { 840 | int* slot; 841 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = *(slot + 1)) { 842 | slot = slotsPtr + i; 843 | if (*slot - 1 == key) { 844 | value = *(dataPtr + i / 2); 845 | return true; 846 | } 847 | } 848 | } 849 | 850 | value = default; 851 | return false; 852 | } 853 | 854 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 855 | public static T GetValueByKey(this UnsafeIntHashMap hashMap, in int key) where T : unmanaged { 856 | var rem = key & hashMap.capacityMinusOne; 857 | 858 | fixed (int* slotsPtr = &hashMap.slots[0]) 859 | fixed (int* bucketsPtr = &hashMap.buckets[0]) 860 | fixed (T* dataPtr = &hashMap.data[0]) { 861 | int next; 862 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = next) { 863 | if (*(slotsPtr + i) - 1 == key) { 864 | return *(dataPtr + i / 2); 865 | } 866 | 867 | next = *(slotsPtr + i + 1); 868 | } 869 | } 870 | 871 | return default; 872 | } 873 | 874 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 875 | public static T GetValueByIndex(this UnsafeIntHashMap hashMap, in int index) where T : unmanaged { 876 | fixed (T* d = &hashMap.data[0]) { 877 | return *(d + index / 2); 878 | } 879 | } 880 | 881 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 882 | public static int GetKeyByIndex(this UnsafeIntHashMap hashMap, in int index) where T : unmanaged { 883 | fixed (int* d = &hashMap.slots[0]) { 884 | return (*(d + index)) - 1; 885 | } 886 | } 887 | 888 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 889 | public static int TryGetIndex(this UnsafeIntHashMap hashMap, in int key) where T : unmanaged { 890 | var rem = key & hashMap.capacityMinusOne; 891 | 892 | fixed (int* slotsPtr = &hashMap.slots[0]) 893 | fixed (int* bucketsPtr = &hashMap.buckets[0]) { 894 | int* slot; 895 | for (var i = *(bucketsPtr + rem) - 1; i >= 0; i = *(slot + 1)) { 896 | slot = slotsPtr + i; 897 | if (*slot - 1 == key) { 898 | return i; 899 | } 900 | } 901 | } 902 | 903 | return -1; 904 | } 905 | 906 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 907 | public static void Clear(this UnsafeIntHashMap hashMap) where T : unmanaged { 908 | if (hashMap.lastIndex <= 0) { 909 | return; 910 | } 911 | 912 | Array.Clear(hashMap.slots, 0, hashMap.lastIndex); 913 | Array.Clear(hashMap.buckets, 0, hashMap.capacity); 914 | Array.Clear(hashMap.data, 0, hashMap.capacity); 915 | 916 | hashMap.lastIndex = 0; 917 | hashMap.length = 0; 918 | hashMap.freeIndex = -1; 919 | } 920 | } 921 | 922 | [Serializable] 923 | [Il2CppSetOption(Option.NullChecks, false)] 924 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 925 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 926 | public sealed class IntStack { 927 | public int length; 928 | public int capacity; 929 | 930 | public int[] data; 931 | 932 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 933 | public IntStack() { 934 | this.capacity = 4; 935 | this.data = new int[this.capacity]; 936 | this.length = 0; 937 | } 938 | } 939 | 940 | [Il2CppSetOption(Option.NullChecks, false)] 941 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 942 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 943 | public static unsafe class IntStackExtensions { 944 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 945 | public static void Push(this IntStack stack, in int value) { 946 | if (stack.length == stack.capacity) { 947 | ArrayHelpers.Grow(ref stack.data, stack.capacity <<= 1); 948 | } 949 | 950 | fixed (int* d = &stack.data[0]) { 951 | *(d + stack.length++) = value; 952 | } 953 | } 954 | 955 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 956 | public static int Pop(this IntStack stack) { 957 | fixed (int* d = &stack.data[0]) { 958 | return *(d + --stack.length); 959 | } 960 | } 961 | 962 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 963 | public static void Clear(this IntStack stack) { 964 | stack.data = null; 965 | stack.length = stack.capacity = 0; 966 | } 967 | } 968 | 969 | [Serializable] 970 | [Il2CppSetOption(Option.NullChecks, false)] 971 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 972 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 973 | public sealed class FastList : IEnumerable { 974 | public T[] data; 975 | public int length; 976 | public int capacity; 977 | 978 | public EqualityComparer comparer; 979 | 980 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 981 | public FastList() { 982 | this.capacity = 3; 983 | this.data = new T[this.capacity]; 984 | this.length = 0; 985 | 986 | this.comparer = EqualityComparer.Default; 987 | } 988 | 989 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 990 | public FastList(int capacity) { 991 | this.capacity = HashHelpers.GetCapacity(capacity); 992 | this.data = new T[this.capacity]; 993 | this.length = 0; 994 | 995 | this.comparer = EqualityComparer.Default; 996 | } 997 | 998 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 999 | public FastList(FastList other) { 1000 | this.capacity = other.capacity; 1001 | this.data = new T[this.capacity]; 1002 | this.length = other.length; 1003 | Array.Copy(other.data, 0, this.data, 0, this.length); 1004 | 1005 | this.comparer = other.comparer; 1006 | } 1007 | 1008 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 1009 | 1010 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 1011 | 1012 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1013 | public Enumerator GetEnumerator() { 1014 | Enumerator e; 1015 | e.list = this; 1016 | e.current = default; 1017 | e.index = 0; 1018 | return e; 1019 | } 1020 | 1021 | [Il2CppSetOption(Option.NullChecks, false)] 1022 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1023 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1024 | public struct ResultSwap { 1025 | public int oldIndex; 1026 | public int newIndex; 1027 | } 1028 | 1029 | [Il2CppSetOption(Option.NullChecks, false)] 1030 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1031 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1032 | public struct Enumerator : IEnumerator { 1033 | public FastList list; 1034 | 1035 | public T current; 1036 | public int index; 1037 | 1038 | public bool MoveNext() { 1039 | if (this.index >= this.list.length) { 1040 | return false; 1041 | } 1042 | 1043 | this.current = this.list.data[this.index++]; 1044 | return true; 1045 | } 1046 | 1047 | public void Reset() { 1048 | this.index = 0; 1049 | this.current = default; 1050 | } 1051 | 1052 | public T Current => this.current; 1053 | object IEnumerator.Current => this.current; 1054 | 1055 | public void Dispose() { 1056 | } 1057 | } 1058 | } 1059 | 1060 | [Il2CppSetOption(Option.NullChecks, false)] 1061 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1062 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1063 | public static class FastListExtensions { 1064 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1065 | public static int Add(this FastList list) { 1066 | var index = list.length; 1067 | if (++list.length == list.capacity) { 1068 | ArrayHelpers.Grow(ref list.data, list.capacity <<= 1); 1069 | } 1070 | 1071 | return index; 1072 | } 1073 | 1074 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1075 | public static int Add(this FastList list, T value) { 1076 | var index = list.length; 1077 | if (++list.length == list.capacity) { 1078 | ArrayHelpers.Grow(ref list.data, list.capacity <<= 1); 1079 | } 1080 | 1081 | list.data[index] = value; 1082 | return index; 1083 | } 1084 | 1085 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1086 | public static void AddListRange(this FastList list, FastList other) { 1087 | if (other.length > 0) { 1088 | var newSize = list.length + other.length; 1089 | if (newSize > list.capacity) { 1090 | while (newSize > list.capacity) { 1091 | list.capacity <<= 1; 1092 | } 1093 | 1094 | ArrayHelpers.Grow(ref list.data, list.capacity); 1095 | } 1096 | 1097 | if (list == other) { 1098 | Array.Copy(list.data, 0, list.data, list.length, list.length); 1099 | } 1100 | else { 1101 | Array.Copy(other.data, 0, list.data, list.length, other.length); 1102 | } 1103 | 1104 | list.length += other.length; 1105 | } 1106 | } 1107 | 1108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1109 | public static void Swap(this FastList list, int source, int destination) => list.data[destination] = list.data[source]; 1110 | 1111 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1112 | public static int IndexOf(this FastList list, T value) => ArrayHelpers.IndexOf(list.data, value, list.comparer); 1113 | 1114 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1115 | public static void Remove(this FastList list, T value) => list.RemoveAt(list.IndexOf(value)); 1116 | 1117 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1118 | public static void RemoveSwap(this FastList list, T value, out FastList.ResultSwap swap) => list.RemoveAtSwap(list.IndexOf(value), out swap); 1119 | 1120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1121 | public static void RemoveAt(this FastList list, int index) { 1122 | --list.length; 1123 | if (index < list.length) { 1124 | Array.Copy(list.data, index + 1, list.data, index, list.length - index); 1125 | } 1126 | 1127 | list.data[list.length] = default; 1128 | } 1129 | 1130 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1131 | public static bool RemoveAtSwap(this FastList list, int index, out FastList.ResultSwap swap) { 1132 | if (list.length-- > 1) { 1133 | swap.oldIndex = list.length; 1134 | swap.newIndex = index; 1135 | 1136 | list.data[swap.newIndex] = list.data[swap.oldIndex]; 1137 | return true; 1138 | } 1139 | 1140 | swap = default; 1141 | return false; 1142 | } 1143 | 1144 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1145 | public static void Clear(this FastList list) { 1146 | if (list.length <= 0) { 1147 | return; 1148 | } 1149 | 1150 | Array.Clear(list.data, 0, list.length); 1151 | list.length = 0; 1152 | } 1153 | 1154 | //todo rework 1155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1156 | public static void Sort(this FastList list) => Array.Sort(list.data, 0, list.length, null); 1157 | 1158 | //todo rework 1159 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1160 | public static void Sort(this FastList list, int index, int len) => Array.Sort(list.data, index, len, null); 1161 | 1162 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1163 | public static T[] ToArray(this FastList list) { 1164 | var newArray = new T[list.length]; 1165 | Array.Copy(list.data, 0, newArray, 0, list.length); 1166 | return newArray; 1167 | } 1168 | } 1169 | 1170 | [Serializable] 1171 | [Il2CppSetOption(Option.NullChecks, false)] 1172 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1173 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1174 | public sealed unsafe class IntFastList : IEnumerable { 1175 | public int length; 1176 | public int capacity; 1177 | 1178 | public int[] data; 1179 | 1180 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1181 | public IntFastList() { 1182 | this.capacity = 3; 1183 | this.data = new int[this.capacity]; 1184 | this.length = 0; 1185 | } 1186 | 1187 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1188 | public IntFastList(int capacity) { 1189 | this.capacity = HashHelpers.GetCapacity(capacity); 1190 | this.data = new int[this.capacity]; 1191 | this.length = 0; 1192 | } 1193 | 1194 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1195 | public IntFastList(IntFastList other) { 1196 | this.capacity = other.capacity; 1197 | this.data = new int[this.capacity]; 1198 | this.length = other.length; 1199 | Array.Copy(other.data, 0, this.data, 0, this.length); 1200 | } 1201 | 1202 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 1203 | 1204 | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); 1205 | 1206 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1207 | public Enumerator GetEnumerator() { 1208 | Enumerator e; 1209 | e.intFastList = this; 1210 | e.current = default; 1211 | e.index = 0; 1212 | return e; 1213 | } 1214 | 1215 | [Il2CppSetOption(Option.NullChecks, false)] 1216 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1217 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1218 | public struct ResultSwap { 1219 | public int oldIndex; 1220 | public int newIndex; 1221 | } 1222 | 1223 | [Il2CppSetOption(Option.NullChecks, false)] 1224 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1225 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1226 | public struct Enumerator : IEnumerator { 1227 | public IntFastList intFastList; 1228 | 1229 | public int current; 1230 | public int index; 1231 | 1232 | public bool MoveNext() { 1233 | if (this.index >= this.intFastList.length) { 1234 | return false; 1235 | } 1236 | 1237 | fixed (int* d = &this.intFastList.data[0]) { 1238 | this.current = *(d + this.index++); 1239 | } 1240 | 1241 | return true; 1242 | } 1243 | 1244 | public void Reset() { 1245 | this.index = 0; 1246 | this.current = default; 1247 | } 1248 | 1249 | public int Current => this.current; 1250 | object IEnumerator.Current => this.current; 1251 | 1252 | public void Dispose() { 1253 | } 1254 | } 1255 | } 1256 | 1257 | [Il2CppSetOption(Option.NullChecks, false)] 1258 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1259 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1260 | public static unsafe class IntFastListExtensions { 1261 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1262 | public static int Add(this IntFastList list) { 1263 | var index = list.length; 1264 | if (++list.length == list.capacity) { 1265 | ArrayHelpers.Grow(ref list.data, list.capacity <<= 1); 1266 | } 1267 | 1268 | return index; 1269 | } 1270 | 1271 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1272 | public static int Get(this IntFastList list, in int index) { 1273 | fixed (int* d = &list.data[0]) { 1274 | return *(d + index); 1275 | } 1276 | } 1277 | 1278 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1279 | public static void Set(this IntFastList list, in int index, in int value) { 1280 | fixed (int* d = &list.data[0]) { 1281 | *(d + index) = value; 1282 | } 1283 | } 1284 | 1285 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1286 | public static int Add(this IntFastList list, in int value) { 1287 | var index = list.length; 1288 | if (++list.length == list.capacity) { 1289 | ArrayHelpers.Grow(ref list.data, list.capacity <<= 1); 1290 | } 1291 | 1292 | fixed (int* p = &list.data[0]) { 1293 | *(p + index) = value; 1294 | } 1295 | 1296 | return index; 1297 | } 1298 | 1299 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1300 | public static void AddListRange(this IntFastList list, IntFastList other) { 1301 | if (other.length > 0) { 1302 | var newSize = list.length + other.length; 1303 | if (newSize > list.capacity) { 1304 | while (newSize > list.capacity) { 1305 | list.capacity <<= 1; 1306 | } 1307 | 1308 | ArrayHelpers.Grow(ref list.data, list.capacity); 1309 | } 1310 | 1311 | if (list == other) { 1312 | Array.Copy(list.data, 0, list.data, list.length, list.length); 1313 | } 1314 | else { 1315 | Array.Copy(other.data, 0, list.data, list.length, other.length); 1316 | } 1317 | 1318 | list.length += other.length; 1319 | } 1320 | } 1321 | 1322 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1323 | public static void Swap(this IntFastList list, int source, int destination) => list.data[destination] = list.data[source]; 1324 | 1325 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1326 | public static int IndexOf(this IntFastList list, int value) => ArrayHelpers.IndexOfUnsafeInt(list.data, value); 1327 | 1328 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1329 | public static void Remove(this IntFastList list, int value) => list.RemoveAt(list.IndexOf(value)); 1330 | 1331 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1332 | public static void RemoveSwap(this IntFastList list, int value, out IntFastList.ResultSwap swap) => list.RemoveAtSwap(list.IndexOf(value), out swap); 1333 | 1334 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1335 | public static void RemoveAt(this IntFastList list, int index) { 1336 | --list.length; 1337 | if (index < list.length) { 1338 | Array.Copy(list.data, index + 1, list.data, index, list.length - index); 1339 | } 1340 | } 1341 | 1342 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1343 | public static bool RemoveAtSwap(this IntFastList list, int index, out IntFastList.ResultSwap swap) { 1344 | if (list.length-- > 1) { 1345 | swap.oldIndex = list.length; 1346 | swap.newIndex = index; 1347 | fixed (int* d = &list.data[0]) { 1348 | *(d + swap.newIndex) = *(d + swap.oldIndex); 1349 | } 1350 | 1351 | return true; 1352 | } 1353 | 1354 | swap = default; 1355 | return false; 1356 | } 1357 | 1358 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1359 | public static void Clear(this IntFastList list) { 1360 | if (list.length <= 0) { 1361 | return; 1362 | } 1363 | 1364 | Array.Clear(list.data, 0, list.length); 1365 | list.length = 0; 1366 | } 1367 | 1368 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1369 | public static void Sort(this IntFastList list) => Array.Sort(list.data, 0, list.length, null); 1370 | 1371 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1372 | public static void Sort(this IntFastList list, int index, int len) => Array.Sort(list.data, index, len, null); 1373 | 1374 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1375 | public static int[] ToArray(this IntFastList list) { 1376 | var newArray = new int[list.length]; 1377 | Array.Copy(list.data, 0, newArray, 0, list.length); 1378 | return newArray; 1379 | } 1380 | } 1381 | 1382 | [Il2CppSetOption(Option.NullChecks, false)] 1383 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1384 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1385 | internal static class ArrayHelpers { 1386 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1387 | public static void Grow(ref T[] array, int newSize) { 1388 | var newArray = new T[newSize]; 1389 | Array.Copy(array, 0, newArray, 0, array.Length); 1390 | array = newArray; 1391 | } 1392 | 1393 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1394 | public static int IndexOf(T[] array, T value, EqualityComparer comparer) { 1395 | for (int i = 0, length = array.Length; i < length; ++i) { 1396 | if (comparer.Equals(array[i], value)) { 1397 | return i; 1398 | } 1399 | } 1400 | 1401 | return -1; 1402 | } 1403 | 1404 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 1405 | public static unsafe int IndexOfUnsafeInt(int[] array, int value) { 1406 | fixed (int* arr = &array[0]) { 1407 | var i = 0; 1408 | for (int* current = arr, length = arr + array.Length; current < length; ++current) { 1409 | if (*current == value) { 1410 | return i; 1411 | } 1412 | 1413 | ++i; 1414 | } 1415 | } 1416 | 1417 | 1418 | return -1; 1419 | } 1420 | } 1421 | 1422 | [Il2CppSetOption(Option.NullChecks, false)] 1423 | [Il2CppSetOption(Option.ArrayBoundsChecks, false)] 1424 | [Il2CppSetOption(Option.DivideByZeroChecks, false)] 1425 | internal static class HashHelpers { 1426 | //https://github.com/dotnet/runtime/blob/master/src/libraries/System.Private.CoreLib/src/System/Collections/HashHelpers.cs#L32 1427 | //different primes to fit n^2 - 1 1428 | //todo expand to maxInt 1429 | internal static readonly int[] capacities = { 1430 | 3, 1431 | 15, 1432 | 63, 1433 | 255, 1434 | 1023, 1435 | 4095, 1436 | 16383, 1437 | 65535, 1438 | 262143, 1439 | 1048575, 1440 | 4194303, 1441 | }; 1442 | 1443 | public static int ExpandCapacity(int oldSize) { 1444 | var min = oldSize << 1; 1445 | return min > 2146435069U && 2146435069 > oldSize ? 2146435069 : GetCapacity(min); 1446 | } 1447 | 1448 | public static int GetCapacity(int min) { 1449 | for (int index = 0, length = capacities.Length; index < length; ++index) { 1450 | var prime = capacities[index]; 1451 | if (prime >= min) { 1452 | return prime; 1453 | } 1454 | } 1455 | 1456 | throw new Exception("Prime is too big"); 1457 | } 1458 | } 1459 | } 1460 | 1461 | namespace Unity.IL2CPP.CompilerServices { 1462 | using System; 1463 | 1464 | internal enum Option { 1465 | NullChecks = 1, 1466 | ArrayBoundsChecks = 2, 1467 | DivideByZeroChecks = 3 1468 | } 1469 | 1470 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] 1471 | internal class Il2CppSetOptionAttribute : Attribute { 1472 | public Il2CppSetOptionAttribute(Option option, object value) { 1473 | this.Option = option; 1474 | this.Value = value; 1475 | } 1476 | 1477 | public Option Option { get; } 1478 | public object Value { get; } 1479 | } 1480 | } --------------------------------------------------------------------------------