├── LICENSE ├── MemoryAllocator ├── Runtime.meta ├── Runtime │ ├── Collections.meta │ ├── Collections │ │ ├── Dictionary.cs │ │ ├── Dictionary.cs.meta │ │ ├── EquatableDictionary.cs │ │ ├── EquatableDictionary.cs.meta │ │ ├── EquatableHashSet.cs │ │ ├── EquatableHashSet.cs.meta │ │ ├── HashSet.cs │ │ ├── HashSet.cs.meta │ │ ├── Helpers.cs │ │ ├── Helpers.cs.meta │ │ ├── List.cs │ │ ├── List.cs.meta │ │ ├── NativeHashSet.cs │ │ ├── NativeHashSet.cs.meta │ │ ├── Queue.cs │ │ ├── Queue.cs.meta │ │ ├── SparseSet.cs │ │ ├── SparseSet.cs.meta │ │ ├── Stack.cs │ │ ├── Stack.cs.meta │ │ ├── ThrowHelper.cs │ │ └── ThrowHelper.cs.meta │ ├── Core.meta │ ├── Core │ │ ├── MemArray.cs │ │ ├── MemArray.cs.meta │ │ ├── MemArraySliced.cs │ │ ├── MemArraySliced.cs.meta │ │ ├── MemoryAllocator.Core.cs │ │ ├── MemoryAllocator.Core.cs.meta │ │ ├── MemoryAllocator.cs │ │ ├── MemoryAllocator.cs.meta │ │ ├── NativeArrayUtils.cs │ │ ├── NativeArrayUtils.cs.meta │ │ ├── StaticAllocators.cs │ │ └── StaticAllocators.cs.meta │ ├── Debug.meta │ ├── Debug │ │ ├── Proxy.cs │ │ └── Proxy.cs.meta │ ├── Exceptions.cs │ ├── Exceptions.cs.meta │ ├── Flags.cs │ ├── Flags.cs.meta │ ├── ME.MemoryAllocator.asmdef │ └── ME.MemoryAllocator.asmdef.meta ├── Tests.meta └── Tests │ ├── Core.cs │ ├── Core.cs.meta │ ├── Dictionary.cs │ ├── Dictionary.cs.meta │ ├── EquatableDictionary.cs │ ├── EquatableDictionary.cs.meta │ ├── EquatableHashSet.cs │ ├── EquatableHashSet.cs.meta │ ├── HashSet.cs │ ├── HashSet.cs.meta │ ├── List.cs │ ├── List.cs.meta │ ├── ME.MemoryAllocator.Tests.asmdef │ ├── ME.MemoryAllocator.Tests.asmdef.meta │ ├── NativeHashSet.cs │ ├── NativeHashSet.cs.meta │ ├── Queue.cs │ ├── Queue.cs.meta │ ├── Stack.cs │ └── Stack.cs.meta ├── README.md └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alex Silaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7517ea17177264b89a768a7957f7c5c9 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ed66460106fd456ab5eacc48c1edea05 3 | timeCreated: 1658395973 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/Dictionary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: faabe12f09ad455a8110f3a5cec2bffc 3 | timeCreated: 1658476679 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/EquatableDictionary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b736987db29c4a5bb08853564ae9232c 3 | timeCreated: 1659022102 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/EquatableHashSet.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Collections { 2 | 3 | using MemPtr = System.Int64; 4 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 5 | 6 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(HashSetProxy<>))] 7 | public struct EquatableHashSet where T : unmanaged, System.IEquatable { 8 | 9 | public struct EnumeratorNoState : System.Collections.Generic.IEnumerator { 10 | 11 | private readonly MemoryAllocator allocator; 12 | private readonly EquatableHashSet set; 13 | private int index; 14 | private T current; 15 | 16 | internal EnumeratorNoState(in MemoryAllocator allocator, EquatableHashSet set) { 17 | this.allocator = allocator; 18 | this.set = set; 19 | this.index = 0; 20 | this.current = default(T); 21 | } 22 | 23 | public void Dispose() { 24 | } 25 | 26 | public bool MoveNext() { 27 | while (this.index < this.set.lastIndex) { 28 | if (this.set.slots[in this.allocator, this.index].hashCode >= 0) { 29 | this.current = this.set.slots[in this.allocator, this.index].value; 30 | this.index++; 31 | return true; 32 | } 33 | 34 | this.index++; 35 | } 36 | 37 | this.index = this.set.lastIndex + 1; 38 | this.current = default(T); 39 | return false; 40 | } 41 | 42 | public T Current => this.current; 43 | public int Index => this.index - 1; 44 | 45 | object System.Collections.IEnumerator.Current => this.Current; 46 | 47 | void System.Collections.IEnumerator.Reset() { 48 | this.index = 0; 49 | this.current = default(T); 50 | } 51 | 52 | } 53 | 54 | public struct Slot { 55 | internal int hashCode; // Lower 31 bits of hash code, -1 if unused 56 | internal int next; // Index of next entry, -1 if last 57 | internal T value; 58 | } 59 | 60 | private const int LOWER31_BIT_MASK = 0x7FFFFFFF; 61 | 62 | internal MemArrayAllocator buckets; 63 | internal MemArrayAllocator slots; 64 | internal int count; 65 | internal int lastIndex; 66 | internal int freeList; 67 | internal int version; 68 | 69 | public bool isCreated { 70 | [INLINE(256)] 71 | get => this.buckets.isCreated; 72 | } 73 | 74 | public int Count { 75 | [INLINE(256)] 76 | get => this.count; 77 | } 78 | 79 | [INLINE(256)] 80 | public EquatableHashSet(ref MemoryAllocator allocator, int capacity) { 81 | 82 | this = default; 83 | this.Initialize(ref allocator, capacity); 84 | 85 | } 86 | 87 | [INLINE(256)] 88 | public void Dispose(ref MemoryAllocator allocator) { 89 | 90 | this.buckets.Dispose(ref allocator); 91 | this.slots.Dispose(ref allocator); 92 | this = default; 93 | 94 | } 95 | 96 | [INLINE(256)] 97 | public readonly EnumeratorNoState GetEnumerator(in MemoryAllocator allocator) { 98 | 99 | return new EnumeratorNoState(in allocator, this); 100 | 101 | } 102 | 103 | [INLINE(256)] 104 | public ref T GetByIndex(in MemoryAllocator allocator, int index) { 105 | return ref this.slots[in allocator, index].value; 106 | } 107 | 108 | /// 109 | /// Remove all items from this set. This clears the elements but not the underlying 110 | /// buckets and slots array. Follow this call by TrimExcess to release these. 111 | /// 112 | /// 113 | [INLINE(256)] 114 | public void Clear(in MemoryAllocator allocator) { 115 | if (this.lastIndex > 0) { 116 | // clear the elements so that the gc can reclaim the references. 117 | // clear only up to m_lastIndex for m_slots 118 | this.slots.Clear(in allocator, 0, this.lastIndex); 119 | this.buckets.Clear(in allocator, 0, this.buckets.Length); 120 | this.lastIndex = 0; 121 | this.count = 0; 122 | this.freeList = -1; 123 | } 124 | this.version++; 125 | } 126 | 127 | /// 128 | /// Checks if this hashset contains the item 129 | /// 130 | /// 131 | /// item to check for containment 132 | /// true if item contained; false if not 133 | [INLINE(256)] 134 | public readonly bool Contains(in MemoryAllocator allocator, T item) { 135 | if (this.buckets.isCreated == true) { 136 | int hashCode = this.InternalGetHashCode(item); 137 | // see note at "HashSet" level describing why "- 1" appears in for loop 138 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 139 | if (this.slots[in allocator, i].hashCode == hashCode && 140 | this.slots[in allocator, i].value.Equals(item) == true) { 141 | return true; 142 | } 143 | } 144 | } 145 | // either m_buckets is null or wasn't found 146 | return false; 147 | } 148 | 149 | /// 150 | /// Remove item from this hashset 151 | /// 152 | /// 153 | /// item to remove 154 | /// true if removed; false if not (i.e. if the item wasn't in the HashSet) 155 | [INLINE(256)] 156 | public bool Remove(ref MemoryAllocator allocator, T item) { 157 | if (this.buckets.isCreated == true) { 158 | int hashCode = this.InternalGetHashCode(item); 159 | int bucket = hashCode % this.buckets.Length; 160 | int last = -1; 161 | for (int i = this.buckets[in allocator, bucket] - 1; i >= 0; last = i, i = this.slots[in allocator, i].next) { 162 | if (this.slots[in allocator, i].hashCode == hashCode && 163 | this.slots[in allocator, i].value.Equals(item) == true) { 164 | if (last < 0) { 165 | // first iteration; update buckets 166 | this.buckets[in allocator, bucket] = this.slots[in allocator, i].next + 1; 167 | } 168 | else { 169 | // subsequent iterations; update 'next' pointers 170 | this.slots[in allocator, last].next = this.slots[in allocator, i].next; 171 | } 172 | this.slots[in allocator, i].hashCode = -1; 173 | this.slots[in allocator, i].value = default(T); 174 | this.slots[in allocator, i].next = this.freeList; 175 | 176 | this.count--; 177 | this.version++; 178 | if (this.count == 0) { 179 | this.lastIndex = 0; 180 | this.freeList = -1; 181 | } 182 | else { 183 | this.freeList = i; 184 | } 185 | return true; 186 | } 187 | } 188 | } 189 | // either m_buckets is null or wasn't found 190 | return false; 191 | } 192 | 193 | /// 194 | /// Add item to this HashSet. Returns bool indicating whether item was added (won't be 195 | /// added if already present) 196 | /// 197 | /// 198 | /// 199 | /// true if added, false if already present 200 | [INLINE(256)] 201 | public bool Add(ref MemoryAllocator allocator, T item) { 202 | return this.AddIfNotPresent(ref allocator, item); 203 | } 204 | 205 | /// 206 | /// Searches the set for a given value and returns the equal value it finds, if any. 207 | /// 208 | /// 209 | /// The value to search for. 210 | /// The value from the set that the search found, or the default value of when the search yielded no match. 211 | /// A value indicating whether the search was successful. 212 | /// 213 | /// This can be useful when you want to reuse a previously stored reference instead of 214 | /// a newly constructed one (so that more sharing of references can occur) or to look up 215 | /// a value that has more complete data than the value you currently have, although their 216 | /// comparer functions indicate they are equal. 217 | /// 218 | [INLINE(256)] 219 | public readonly bool TryGetValue(ref MemoryAllocator allocator, T equalValue, out T actualValue) { 220 | if (this.buckets.isCreated == true) { 221 | int i = this.InternalIndexOf(in allocator, equalValue); 222 | if (i >= 0) { 223 | actualValue = this.slots[in allocator, i].value; 224 | return true; 225 | } 226 | } 227 | actualValue = default(T); 228 | return false; 229 | } 230 | 231 | [INLINE(256)] 232 | public ref T GetValue(in MemoryAllocator allocator, T equalValue) { 233 | 234 | int i = this.InternalIndexOf(in allocator, equalValue); 235 | if (i >= 0) { 236 | return ref this.slots[in allocator, i].value; 237 | } 238 | 239 | throw new System.Collections.Generic.KeyNotFoundException(); 240 | 241 | } 242 | 243 | #region Helper 244 | /// 245 | /// Initializes buckets and slots arrays. Uses suggested capacity by finding next prime 246 | /// greater than or equal to capacity. 247 | /// 248 | /// 249 | /// 250 | [INLINE(256)] 251 | private void Initialize(ref MemoryAllocator allocator, int capacity) { 252 | int size = System.Collections.Generic.HashHelpers.GetPrime(capacity); 253 | this.buckets = new MemArrayAllocator(ref allocator, size); 254 | this.slots = new MemArrayAllocator(ref allocator, size); 255 | this.freeList = -1; 256 | } 257 | 258 | /// 259 | /// Expand to new capacity. New capacity is next prime greater than or equal to suggested 260 | /// size. This is called when the underlying array is filled. This performs no 261 | /// defragmentation, allowing faster execution; note that this is reasonable since 262 | /// AddIfNotPresent attempts to insert new elements in re-opened spots. 263 | /// 264 | /// 265 | [INLINE(256)] 266 | private void IncreaseCapacity(ref MemoryAllocator allocator) { 267 | int newSize = System.Collections.Generic.HashHelpers.ExpandPrime(this.count); 268 | if (newSize <= this.count) { 269 | throw new System.ArgumentException(); 270 | } 271 | 272 | // Able to increase capacity; copy elements to larger array and rehash 273 | this.SetCapacity(ref allocator, newSize, false); 274 | } 275 | 276 | /// 277 | /// Set the underlying buckets array to size newSize and rehash. Note that newSize 278 | /// *must* be a prime. It is very likely that you want to call IncreaseCapacity() 279 | /// instead of this method. 280 | /// 281 | [INLINE(256)] 282 | private void SetCapacity(ref MemoryAllocator allocator, int newSize, bool forceNewHashCodes) { 283 | //System.Diagnostics.Contracts.Contract.Assert(HashHelpers.IsPrime(newSize), "New size is not prime!"); 284 | 285 | //System.Diagnostics.Contracts.Contract.Assert(this.buckets.isCreated, "SetCapacity called on a set with no elements"); 286 | 287 | var newSlots = new MemArrayAllocator(ref allocator, newSize); 288 | if (this.slots.isCreated == true) { 289 | NativeArrayUtils.CopyNoChecks(ref allocator, in this.slots, 0, ref newSlots, 0, this.lastIndex); 290 | } 291 | 292 | if (forceNewHashCodes == true) { 293 | for(int i = 0; i < this.lastIndex; i++) { 294 | if(newSlots[in allocator, i].hashCode != -1) { 295 | newSlots[in allocator, i].hashCode = this.InternalGetHashCode(newSlots[in allocator, i].value); 296 | } 297 | } 298 | } 299 | 300 | var newBuckets = new MemArrayAllocator(ref allocator, newSize); 301 | for (int i = 0; i < this.lastIndex; i++) { 302 | int bucket = newSlots[in allocator, i].hashCode % newSize; 303 | newSlots[in allocator, i].next = newBuckets[in allocator, bucket] - 1; 304 | newBuckets[in allocator, bucket] = i + 1; 305 | } 306 | if (this.slots.isCreated == true) this.slots.Dispose(ref allocator); 307 | if (this.buckets.isCreated == true) this.buckets.Dispose(ref allocator); 308 | this.slots = newSlots; 309 | this.buckets = newBuckets; 310 | } 311 | 312 | /// 313 | /// Adds value to HashSet if not contained already 314 | /// Returns true if added and false if already present 315 | /// 316 | /// 317 | /// value to find 318 | /// 319 | [INLINE(256)] 320 | private bool AddIfNotPresent(ref MemoryAllocator allocator, T value) { 321 | if (this.buckets.isCreated == false) { 322 | this.Initialize(ref allocator, 0); 323 | } 324 | 325 | int hashCode = this.InternalGetHashCode(value); 326 | int bucket = hashCode % this.buckets.Length; 327 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 328 | if (this.slots[in allocator, i].hashCode == hashCode && 329 | this.slots[in allocator, i].value.Equals(value) == true) { 330 | return false; 331 | } 332 | } 333 | 334 | int index; 335 | if (this.freeList >= 0) { 336 | index = this.freeList; 337 | this.freeList = this.slots[in allocator, index].next; 338 | } 339 | else { 340 | if (this.lastIndex == this.slots.Length) { 341 | this.IncreaseCapacity(ref allocator); 342 | // this will change during resize 343 | bucket = hashCode % this.buckets.Length; 344 | } 345 | index = this.lastIndex; 346 | this.lastIndex++; 347 | } 348 | this.slots[in allocator, index].hashCode = hashCode; 349 | this.slots[in allocator, index].value = value; 350 | this.slots[in allocator, index].next = this.buckets[in allocator, bucket] - 1; 351 | this.buckets[in allocator, bucket] = index + 1; 352 | this.count++; 353 | this.version++; 354 | 355 | return true; 356 | } 357 | 358 | // Add value at known index with known hash code. Used only 359 | // when constructing from another HashSet. 360 | [INLINE(256)] 361 | private void AddValue(ref MemoryAllocator allocator, int index, int hashCode, T value) { 362 | int bucket = hashCode % this.buckets.Length; 363 | this.slots[in allocator, index].hashCode = hashCode; 364 | this.slots[in allocator, index].value = value; 365 | this.slots[in allocator, index].next = this.buckets[in allocator, bucket] - 1; 366 | this.buckets[in allocator, bucket] = index + 1; 367 | } 368 | 369 | /// 370 | /// Used internally by set operations which have to rely on bit array marking. This is like 371 | /// Contains but returns index in slots array. 372 | /// 373 | /// 374 | /// 375 | /// 376 | [INLINE(256)] 377 | private readonly int InternalIndexOf(in MemoryAllocator allocator, T item) { 378 | int hashCode = this.InternalGetHashCode(item); 379 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 380 | if ((this.slots[in allocator, i].hashCode) == hashCode && 381 | this.slots[in allocator, i].value.Equals(item) == true) { 382 | return i; 383 | } 384 | } 385 | // wasn't found 386 | return -1; 387 | } 388 | 389 | /// 390 | /// Workaround Comparers that throw ArgumentNullException for GetHashCode(null). 391 | /// 392 | /// 393 | /// hash code 394 | [INLINE(256)] 395 | private readonly int InternalGetHashCode(T item) { 396 | return item.GetHashCode() & EquatableHashSet.LOWER31_BIT_MASK; 397 | } 398 | #endregion 399 | 400 | } 401 | 402 | } 403 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/EquatableHashSet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 50c9f3c9558243e1a55c713c36247db7 3 | timeCreated: 1659016591 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/HashSet.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Collections { 2 | 3 | using MemPtr = System.Int64; 4 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 5 | 6 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(HashSetProxy<>))] 7 | public struct HashSet : IIsCreated where T : unmanaged { 8 | 9 | public struct EnumeratorNoState : System.Collections.Generic.IEnumerator { 10 | 11 | private readonly MemoryAllocator allocator; 12 | private readonly HashSet set; 13 | private int index; 14 | private T current; 15 | 16 | internal EnumeratorNoState(in MemoryAllocator allocator, HashSet set) { 17 | this.allocator = allocator; 18 | this.set = set; 19 | this.index = 0; 20 | this.current = default(T); 21 | } 22 | 23 | public void Dispose() { 24 | } 25 | 26 | public bool MoveNext() { 27 | while (this.index < this.set.lastIndex) { 28 | if (this.set.slots[in this.allocator, this.index].hashCode >= 0) { 29 | this.current = this.set.slots[in this.allocator, this.index].value; 30 | this.index++; 31 | return true; 32 | } 33 | 34 | this.index++; 35 | } 36 | 37 | this.index = this.set.lastIndex + 1; 38 | this.current = default(T); 39 | return false; 40 | } 41 | 42 | public T Current => this.current; 43 | public int Index => this.index - 1; 44 | 45 | object System.Collections.IEnumerator.Current => this.Current; 46 | 47 | void System.Collections.IEnumerator.Reset() { 48 | this.index = 0; 49 | this.current = default(T); 50 | } 51 | 52 | } 53 | 54 | public struct Slot { 55 | internal int hashCode; // Lower 31 bits of hash code, -1 if unused 56 | internal int next; // Index of next entry, -1 if last 57 | internal T value; 58 | } 59 | 60 | private const int LOWER31_BIT_MASK = 0x7FFFFFFF; 61 | 62 | internal MemArrayAllocator buckets; 63 | internal MemArrayAllocator slots; 64 | internal int count; 65 | internal int lastIndex; 66 | internal int freeList; 67 | internal int version; 68 | 69 | public bool isCreated { 70 | [INLINE(256)] 71 | get => this.buckets.isCreated; 72 | } 73 | 74 | public int Count { 75 | [INLINE(256)] 76 | get => this.count; 77 | } 78 | 79 | [INLINE(256)] 80 | public HashSet(ref MemoryAllocator allocator, int capacity) { 81 | 82 | this = default; 83 | this.Initialize(ref allocator, capacity); 84 | 85 | } 86 | 87 | [INLINE(256)] 88 | public void Dispose(ref MemoryAllocator allocator) { 89 | 90 | this.buckets.Dispose(ref allocator); 91 | this.slots.Dispose(ref allocator); 92 | this = default; 93 | 94 | } 95 | 96 | [INLINE(256)] 97 | public readonly MemPtr GetMemPtr(in MemoryAllocator allocator) { 98 | 99 | E.IS_CREATED(this); 100 | return this.buckets.arrPtr; 101 | 102 | } 103 | 104 | [INLINE(256)] 105 | public void ReplaceWith(ref MemoryAllocator allocator, in HashSet other) { 106 | 107 | if (this.GetMemPtr(in allocator) == other.GetMemPtr(in allocator)) { 108 | return; 109 | } 110 | 111 | this.Dispose(ref allocator); 112 | this = other; 113 | 114 | } 115 | 116 | [INLINE(256)] 117 | public readonly EnumeratorNoState GetEnumerator(in MemoryAllocator allocator) { 118 | 119 | return new EnumeratorNoState(in allocator, this); 120 | 121 | } 122 | 123 | [INLINE(256)] 124 | public ref T GetByIndex(in MemoryAllocator allocator, int index) { 125 | return ref this.slots[in allocator, index].value; 126 | } 127 | 128 | /// 129 | /// Remove all items from this set. This clears the elements but not the underlying 130 | /// buckets and slots array. Follow this call by TrimExcess to release these. 131 | /// 132 | /// 133 | [INLINE(256)] 134 | public void Clear(in MemoryAllocator allocator) { 135 | if (this.lastIndex > 0) { 136 | // clear the elements so that the gc can reclaim the references. 137 | // clear only up to m_lastIndex for m_slots 138 | this.slots.Clear(in allocator, 0, this.lastIndex); 139 | this.buckets.Clear(in allocator, 0, this.buckets.Length); 140 | this.lastIndex = 0; 141 | this.count = 0; 142 | this.freeList = -1; 143 | } 144 | this.version++; 145 | } 146 | 147 | /// 148 | /// Checks if this hashset contains the item 149 | /// 150 | /// 151 | /// item to check for containment 152 | /// true if item contained; false if not 153 | [INLINE(256)] 154 | public readonly bool Contains(in MemoryAllocator allocator, T item) { 155 | if (this.buckets.isCreated == true) { 156 | var c = System.Collections.Generic.EqualityComparer.Default; 157 | int hashCode = this.InternalGetHashCode(item); 158 | // see note at "HashSet" level describing why "- 1" appears in for loop 159 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 160 | if (this.slots[in allocator, i].hashCode == hashCode && 161 | c.Equals(this.slots[in allocator, i].value, item) == true) { 162 | return true; 163 | } 164 | } 165 | } 166 | // either m_buckets is null or wasn't found 167 | return false; 168 | } 169 | 170 | /// 171 | /// Remove item from this hashset 172 | /// 173 | /// 174 | /// item to remove 175 | /// true if removed; false if not (i.e. if the item wasn't in the HashSet) 176 | [INLINE(256)] 177 | public bool Remove(ref MemoryAllocator allocator, T item) { 178 | if (this.buckets.isCreated == true) { 179 | var c = System.Collections.Generic.EqualityComparer.Default; 180 | int hashCode = this.InternalGetHashCode(item); 181 | int bucket = hashCode % this.buckets.Length; 182 | int last = -1; 183 | for (int i = this.buckets[in allocator, bucket] - 1; i >= 0; last = i, i = this.slots[in allocator, i].next) { 184 | if (this.slots[in allocator, i].hashCode == hashCode && 185 | c.Equals(this.slots[in allocator, i].value, item) == true) { 186 | if (last < 0) { 187 | // first iteration; update buckets 188 | this.buckets[in allocator, bucket] = this.slots[in allocator, i].next + 1; 189 | } 190 | else { 191 | // subsequent iterations; update 'next' pointers 192 | this.slots[in allocator, last].next = this.slots[in allocator, i].next; 193 | } 194 | this.slots[in allocator, i].hashCode = -1; 195 | this.slots[in allocator, i].value = default(T); 196 | this.slots[in allocator, i].next = this.freeList; 197 | 198 | this.count--; 199 | this.version++; 200 | if (this.count == 0) { 201 | this.lastIndex = 0; 202 | this.freeList = -1; 203 | } 204 | else { 205 | this.freeList = i; 206 | } 207 | return true; 208 | } 209 | } 210 | } 211 | // either m_buckets is null or wasn't found 212 | return false; 213 | } 214 | 215 | /// 216 | /// Add item to this HashSet. Returns bool indicating whether item was added (won't be 217 | /// added if already present) 218 | /// 219 | /// 220 | /// 221 | /// true if added, false if already present 222 | [INLINE(256)] 223 | public bool Add(ref MemoryAllocator allocator, T item) { 224 | return this.AddIfNotPresent(ref allocator, item); 225 | } 226 | 227 | /// 228 | /// Searches the set for a given value and returns the equal value it finds, if any. 229 | /// 230 | /// 231 | /// The value to search for. 232 | /// The value from the set that the search found, or the default value of when the search yielded no match. 233 | /// A value indicating whether the search was successful. 234 | /// 235 | /// This can be useful when you want to reuse a previously stored reference instead of 236 | /// a newly constructed one (so that more sharing of references can occur) or to look up 237 | /// a value that has more complete data than the value you currently have, although their 238 | /// comparer functions indicate they are equal. 239 | /// 240 | [INLINE(256)] 241 | public readonly bool TryGetValue(ref MemoryAllocator allocator, T equalValue, out T actualValue) { 242 | if (this.buckets.isCreated == true) { 243 | int i = this.InternalIndexOf(in allocator, equalValue); 244 | if (i >= 0) { 245 | actualValue = this.slots[in allocator, i].value; 246 | return true; 247 | } 248 | } 249 | actualValue = default(T); 250 | return false; 251 | } 252 | 253 | [INLINE(256)] 254 | public ref T GetValue(in MemoryAllocator allocator, T equalValue) { 255 | 256 | int i = this.InternalIndexOf(in allocator, equalValue); 257 | if (i >= 0) { 258 | return ref this.slots[in allocator, i].value; 259 | } 260 | 261 | throw new System.Collections.Generic.KeyNotFoundException(); 262 | 263 | } 264 | 265 | #region Helper 266 | /// 267 | /// Initializes buckets and slots arrays. Uses suggested capacity by finding next prime 268 | /// greater than or equal to capacity. 269 | /// 270 | /// 271 | /// 272 | [INLINE(256)] 273 | private void Initialize(ref MemoryAllocator allocator, int capacity) { 274 | int size = System.Collections.Generic.HashHelpers.GetPrime(capacity); 275 | this.buckets = new MemArrayAllocator(ref allocator, size); 276 | this.slots = new MemArrayAllocator(ref allocator, size); 277 | this.freeList = -1; 278 | } 279 | 280 | /// 281 | /// Expand to new capacity. New capacity is next prime greater than or equal to suggested 282 | /// size. This is called when the underlying array is filled. This performs no 283 | /// defragmentation, allowing faster execution; note that this is reasonable since 284 | /// AddIfNotPresent attempts to insert new elements in re-opened spots. 285 | /// 286 | /// 287 | [INLINE(256)] 288 | private void IncreaseCapacity(ref MemoryAllocator allocator) { 289 | int newSize = System.Collections.Generic.HashHelpers.ExpandPrime(this.count); 290 | if (newSize <= this.count) { 291 | throw new System.ArgumentException(); 292 | } 293 | 294 | // Able to increase capacity; copy elements to larger array and rehash 295 | this.SetCapacity(ref allocator, newSize, false); 296 | } 297 | 298 | /// 299 | /// Set the underlying buckets array to size newSize and rehash. Note that newSize 300 | /// *must* be a prime. It is very likely that you want to call IncreaseCapacity() 301 | /// instead of this method. 302 | /// 303 | [INLINE(256)] 304 | private void SetCapacity(ref MemoryAllocator allocator, int newSize, bool forceNewHashCodes) { 305 | System.Diagnostics.Contracts.Contract.Assert(System.Collections.Generic.HashHelpers.IsPrime(newSize), "New size is not prime!"); 306 | 307 | System.Diagnostics.Contracts.Contract.Assert(this.buckets.isCreated, "SetCapacity called on a set with no elements"); 308 | 309 | var newSlots = new MemArrayAllocator(ref allocator, newSize); 310 | if (this.slots.isCreated == true) { 311 | NativeArrayUtils.CopyNoChecks(ref allocator, in this.slots, 0, ref newSlots, 0, this.lastIndex); 312 | } 313 | 314 | if (forceNewHashCodes == true) { 315 | for(int i = 0; i < this.lastIndex; i++) { 316 | if(newSlots[in allocator, i].hashCode != -1) { 317 | newSlots[in allocator, i].hashCode = this.InternalGetHashCode(newSlots[in allocator, i].value); 318 | } 319 | } 320 | } 321 | 322 | var newBuckets = new MemArrayAllocator(ref allocator, newSize); 323 | for (int i = 0; i < this.lastIndex; i++) { 324 | int bucket = newSlots[in allocator, i].hashCode % newSize; 325 | newSlots[in allocator, i].next = newBuckets[in allocator, bucket] - 1; 326 | newBuckets[in allocator, bucket] = i + 1; 327 | } 328 | if (this.slots.isCreated == true) this.slots.Dispose(ref allocator); 329 | if (this.buckets.isCreated == true) this.buckets.Dispose(ref allocator); 330 | this.slots = newSlots; 331 | this.buckets = newBuckets; 332 | } 333 | 334 | /// 335 | /// Adds value to HashSet if not contained already 336 | /// Returns true if added and false if already present 337 | /// 338 | /// 339 | /// value to find 340 | /// 341 | [INLINE(256)] 342 | private bool AddIfNotPresent(ref MemoryAllocator allocator, T value) { 343 | if (this.buckets.isCreated == false) { 344 | this.Initialize(ref allocator, 0); 345 | } 346 | 347 | int hashCode = this.InternalGetHashCode(value); 348 | int bucket = hashCode % this.buckets.Length; 349 | var c = System.Collections.Generic.EqualityComparer.Default; 350 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 351 | if (this.slots[in allocator, i].hashCode == hashCode && 352 | c.Equals(this.slots[in allocator, i].value, value) == true) { 353 | return false; 354 | } 355 | } 356 | 357 | int index; 358 | if (this.freeList >= 0) { 359 | index = this.freeList; 360 | this.freeList = this.slots[in allocator, index].next; 361 | } 362 | else { 363 | if (this.lastIndex == this.slots.Length) { 364 | this.IncreaseCapacity(ref allocator); 365 | // this will change during resize 366 | bucket = hashCode % this.buckets.Length; 367 | } 368 | index = this.lastIndex; 369 | this.lastIndex++; 370 | } 371 | this.slots[in allocator, index].hashCode = hashCode; 372 | this.slots[in allocator, index].value = value; 373 | this.slots[in allocator, index].next = this.buckets[in allocator, bucket] - 1; 374 | this.buckets[in allocator, bucket] = index + 1; 375 | this.count++; 376 | this.version++; 377 | 378 | return true; 379 | } 380 | 381 | // Add value at known index with known hash code. Used only 382 | // when constructing from another HashSet. 383 | [INLINE(256)] 384 | private void AddValue(ref MemoryAllocator allocator, int index, int hashCode, T value) { 385 | int bucket = hashCode % this.buckets.Length; 386 | this.slots[in allocator, index].hashCode = hashCode; 387 | this.slots[in allocator, index].value = value; 388 | this.slots[in allocator, index].next = this.buckets[in allocator, bucket] - 1; 389 | this.buckets[in allocator, bucket] = index + 1; 390 | } 391 | 392 | /// 393 | /// Used internally by set operations which have to rely on bit array marking. This is like 394 | /// Contains but returns index in slots array. 395 | /// 396 | /// 397 | /// 398 | /// 399 | [INLINE(256)] 400 | private readonly int InternalIndexOf(in MemoryAllocator allocator, T item) { 401 | int hashCode = this.InternalGetHashCode(item); 402 | var c = System.Collections.Generic.EqualityComparer.Default; 403 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 404 | if ((this.slots[in allocator, i].hashCode) == hashCode && 405 | c.Equals(this.slots[in allocator, i].value, item) == true) { 406 | return i; 407 | } 408 | } 409 | // wasn't found 410 | return -1; 411 | } 412 | 413 | /// 414 | /// Workaround Comparers that throw ArgumentNullException for GetHashCode(null). 415 | /// 416 | /// 417 | /// hash code 418 | [INLINE(256)] 419 | private readonly int InternalGetHashCode(T item) { 420 | return System.Collections.Generic.EqualityComparer.Default.GetHashCode(item) & HashSet.LOWER31_BIT_MASK; 421 | } 422 | #endregion 423 | 424 | } 425 | 426 | } 427 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/HashSet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9407f05ba5fe412ea42daa7fe9cb1fab 3 | timeCreated: 1658401478 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/Helpers.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | public interface IIsCreated { 4 | 5 | bool isCreated { get; } 6 | 7 | } 8 | 9 | public interface IEquatableAllocator { 10 | 11 | bool Equals(in MemoryAllocator allocator, T obj); 12 | int GetHash(in MemoryAllocator allocator); 13 | 14 | } 15 | 16 | public enum InsertionBehavior { 17 | 18 | None, 19 | OverwriteExisting, 20 | ThrowOnExisting, 21 | 22 | } 23 | 24 | public static class Helpers { 25 | 26 | public static int NextPot(int n) { 27 | 28 | --n; 29 | n |= n >> 1; 30 | n |= n >> 2; 31 | n |= n >> 4; 32 | n |= n >> 8; 33 | n |= n >> 16; 34 | ++n; 35 | return n; 36 | 37 | } 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/Helpers.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d17eca5e0fe14c20838bcd45b28ab2ba 3 | timeCreated: 1658401601 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/List.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Collections { 2 | 3 | using MemPtr = System.Int64; 4 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 5 | 6 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(ListProxy<>))] 7 | public struct List : IIsCreated where T : unmanaged { 8 | 9 | public struct EnumeratorNoState : System.Collections.Generic.IEnumerator { 10 | 11 | private readonly MemoryAllocator allocator; 12 | private readonly List list; 13 | private int index; 14 | 15 | internal EnumeratorNoState(in MemoryAllocator allocator, List list) { 16 | this.allocator = allocator; 17 | this.list = list; 18 | this.index = -1; 19 | } 20 | 21 | public void Dispose() { 22 | } 23 | 24 | public bool MoveNext() { 25 | ++this.index; 26 | return this.index < this.list.Count; 27 | } 28 | 29 | public T Current => this.list[in this.allocator, this.index]; 30 | 31 | object System.Collections.IEnumerator.Current => this.Current; 32 | 33 | void System.Collections.IEnumerator.Reset() { 34 | this.index = -1; 35 | } 36 | 37 | } 38 | 39 | private MemArrayAllocator arr; 40 | public int Count; 41 | 42 | public readonly bool isCreated { 43 | [INLINE(256)] 44 | get => this.arr.isCreated; 45 | } 46 | 47 | [INLINE(256)] 48 | public readonly int Capacity(in MemoryAllocator allocator) { 49 | E.IS_CREATED(this); 50 | return this.arr.Length; 51 | } 52 | 53 | [INLINE(256)] 54 | public List(ref MemoryAllocator allocator, int capacity) { 55 | 56 | if (capacity <= 0) capacity = 1; 57 | this.arr = new MemArrayAllocator(ref allocator, capacity); 58 | this.Count = 0; 59 | this.EnsureCapacity(ref allocator, capacity); 60 | 61 | } 62 | 63 | [INLINE(256)] 64 | public void ReplaceWith(ref MemoryAllocator allocator, in List other) { 65 | 66 | if (other.GetMemPtr(in allocator) == this.GetMemPtr(in allocator)) { 67 | return; 68 | } 69 | 70 | this.Dispose(ref allocator); 71 | this = other; 72 | 73 | } 74 | 75 | [INLINE(256)] 76 | public void CopyFrom(ref MemoryAllocator allocator, in List other) { 77 | 78 | if (other.GetMemPtr(in allocator) == this.GetMemPtr(in allocator)) return; 79 | if (this.arr.arrPtr == 0L && other.arr.arrPtr == 0L) return; 80 | if (this.arr.arrPtr != 0L && other.arr.arrPtr == 0L) { 81 | this.Dispose(ref allocator); 82 | return; 83 | } 84 | if (this.arr.arrPtr == 0L) this = new List(ref allocator, other.Capacity(in allocator)); 85 | 86 | NativeArrayUtils.Copy(ref allocator, in other.arr, ref this.arr); 87 | this.Count = other.Count; 88 | 89 | } 90 | 91 | [INLINE(256)] 92 | public readonly MemPtr GetMemPtr(in MemoryAllocator allocator) { 93 | 94 | E.IS_CREATED(this); 95 | return this.arr.arrPtr; 96 | 97 | } 98 | 99 | [INLINE(256)] 100 | public readonly unsafe void* GetUnsafePtr(in MemoryAllocator allocator) { 101 | 102 | E.IS_CREATED(this); 103 | return this.arr.GetUnsafePtr(in allocator); 104 | 105 | } 106 | 107 | [INLINE(256)] 108 | public void Dispose(ref MemoryAllocator allocator) { 109 | 110 | E.IS_CREATED(this); 111 | this.arr.Dispose(ref allocator); 112 | this = default; 113 | 114 | } 115 | 116 | [INLINE(256)] 117 | public readonly EnumeratorNoState GetEnumerator(in MemoryAllocator allocator) { 118 | 119 | E.IS_CREATED(this); 120 | return new EnumeratorNoState(in allocator, this); 121 | 122 | } 123 | 124 | [INLINE(256)] 125 | public void Clear(in MemoryAllocator allocator) { 126 | 127 | E.IS_CREATED(this); 128 | this.Count = 0; 129 | 130 | } 131 | 132 | public readonly ref T this[in MemoryAllocator allocator, int index] { 133 | [INLINE(256)] 134 | get { 135 | E.IS_CREATED(this); 136 | E.RANGE(index, 0, this.Count); 137 | return ref this.arr[in allocator, index]; 138 | } 139 | } 140 | 141 | [INLINE(256)] 142 | public bool EnsureCapacity(ref MemoryAllocator allocator, int capacity) { 143 | 144 | E.IS_CREATED(this); 145 | capacity = Helpers.NextPot(capacity); 146 | return this.arr.Resize(ref allocator, capacity, ClearOptions.UninitializedMemory); 147 | 148 | } 149 | 150 | [INLINE(256)] 151 | public void Add(ref MemoryAllocator allocator, T obj) { 152 | 153 | E.IS_CREATED(this); 154 | ++this.Count; 155 | this.EnsureCapacity(ref allocator, this.Count); 156 | 157 | this.arr[in allocator, this.Count - 1] = obj; 158 | 159 | } 160 | 161 | [INLINE(256)] 162 | public readonly bool Contains(in MemoryAllocator allocator, U obj) where U : unmanaged, System.IEquatable { 163 | 164 | E.IS_CREATED(this); 165 | for (int i = 0, cnt = this.Count; i < cnt; ++i) { 166 | 167 | if (obj.Equals(this.arr[in allocator, i]) == true) { 168 | 169 | return true; 170 | 171 | } 172 | 173 | } 174 | 175 | return false; 176 | 177 | } 178 | 179 | [INLINE(256)] 180 | public bool Remove(ref MemoryAllocator allocator, U obj) where U : unmanaged, System.IEquatable { 181 | 182 | E.IS_CREATED(this); 183 | for (int i = 0, cnt = this.Count; i < cnt; ++i) { 184 | 185 | if (obj.Equals(this.arr[in allocator, i]) == true) { 186 | 187 | this.RemoveAt(ref allocator, i); 188 | return true; 189 | 190 | } 191 | 192 | } 193 | 194 | return false; 195 | 196 | } 197 | 198 | [INLINE(256)] 199 | public unsafe bool RemoveAt(ref MemoryAllocator allocator, int index) { 200 | 201 | E.IS_CREATED(this); 202 | if (index < 0 || index >= this.Count) return false; 203 | 204 | if (index == this.Count - 1) { 205 | 206 | --this.Count; 207 | this.arr[in allocator, this.Count] = default; 208 | return true; 209 | 210 | } 211 | 212 | var ptr = this.arr.arrPtr; 213 | var size = sizeof(T); 214 | allocator.MemMove(ptr, size * index, ptr, size * (index + 1), (this.Count - index - 1) * size); 215 | 216 | --this.Count; 217 | this.arr[in allocator, this.Count] = default; 218 | 219 | return true; 220 | 221 | } 222 | 223 | [INLINE(256)] 224 | public bool RemoveAtFast(ref MemoryAllocator allocator, int index) { 225 | 226 | E.IS_CREATED(this); 227 | if (index < 0 || index >= this.Count) return false; 228 | 229 | --this.Count; 230 | var last = this.arr[in allocator, this.Count]; 231 | this.arr[in allocator, index] = last; 232 | 233 | return true; 234 | 235 | } 236 | 237 | [INLINE(256)] 238 | public bool Resize(ref MemoryAllocator allocator, int newLength, ClearOptions options = ClearOptions.ClearMemory) { 239 | 240 | E.IS_CREATED(this); 241 | if (this.isCreated == false) { 242 | 243 | this = new List(ref allocator, newLength); 244 | return true; 245 | 246 | } 247 | 248 | if (newLength <= this.Count) { 249 | 250 | return false; 251 | 252 | } 253 | 254 | this.arr.Resize(ref allocator, newLength, options); 255 | this.Count = newLength; 256 | return true; 257 | 258 | } 259 | 260 | [INLINE(256)] 261 | public void AddRange(ref MemoryAllocator allocator, System.Collections.Generic.List list) { 262 | 263 | E.IS_CREATED(this); 264 | foreach (var item in list) { 265 | 266 | this.Add(ref allocator, item); 267 | 268 | } 269 | 270 | } 271 | 272 | [INLINE(256)] 273 | public unsafe void AddRange(ref MemoryAllocator allocator, List collection) { 274 | 275 | E.IS_CREATED(this); 276 | var index = this.Count; 277 | if (collection.isCreated == false) 278 | ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection); 279 | if ((uint) index > (uint)this.Count) 280 | throw new System.IndexOutOfRangeException(); 281 | int count = collection.Count; 282 | if (count > 0) { 283 | this.EnsureCapacity(ref allocator, this.Count + count); 284 | var size = sizeof(T); 285 | if (index < this.Count) { 286 | allocator.MemMove(this.arr.arrPtr, (index + count) * size, this.arr.arrPtr, index * size, (this.Count - index) * size); 287 | } 288 | 289 | if (this.arr.arrPtr == collection.arr.arrPtr) { 290 | allocator.MemMove(this.arr.arrPtr, index * size, this.arr.arrPtr, 0, index * size); 291 | allocator.MemMove(this.arr.arrPtr, (index * 2) * size, this.arr.arrPtr, (index + count) * size, (this.Count - index) * size); 292 | } else { 293 | collection.CopyTo(ref allocator, this.arr, index); 294 | } 295 | 296 | this.Count += count; 297 | } 298 | 299 | } 300 | 301 | [INLINE(256)] 302 | public readonly unsafe void CopyTo(ref MemoryAllocator allocator, MemArrayAllocator arr, int index) { 303 | 304 | E.IS_CREATED(this); 305 | if (arr.isCreated == false) { 306 | ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported); 307 | } 308 | 309 | var size = sizeof(T); 310 | allocator.MemCopy(arr.arrPtr, index * size, this.arr.arrPtr, 0, this.Count * size); 311 | 312 | } 313 | 314 | } 315 | 316 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/List.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 92c2937608e64d6283a8569f1f305280 3 | timeCreated: 1658395980 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/NativeHashSet.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Collections { 2 | 3 | using MemPtr = System.Int64; 4 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 5 | 6 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(HashSetProxy<>))] 7 | public struct NativeHashSet where T : unmanaged, IEquatableAllocator { 8 | 9 | public struct EnumeratorNoState : System.Collections.Generic.IEnumerator { 10 | 11 | private readonly MemoryAllocator allocator; 12 | private readonly NativeHashSet set; 13 | private int index; 14 | private T current; 15 | 16 | internal EnumeratorNoState(in MemoryAllocator allocator, NativeHashSet set) { 17 | this.allocator = allocator; 18 | this.set = set; 19 | this.index = 0; 20 | this.current = default(T); 21 | } 22 | 23 | public void Dispose() { 24 | } 25 | 26 | public bool MoveNext() { 27 | while (this.index < this.set.lastIndex) { 28 | if (this.set.slots[in this.allocator, this.index].hashCode >= 0) { 29 | this.current = this.set.slots[in this.allocator, this.index].value; 30 | this.index++; 31 | return true; 32 | } 33 | 34 | this.index++; 35 | } 36 | 37 | this.index = this.set.lastIndex + 1; 38 | this.current = default(T); 39 | return false; 40 | } 41 | 42 | public T Current => this.current; 43 | public int Index => this.index - 1; 44 | 45 | object System.Collections.IEnumerator.Current => this.Current; 46 | 47 | void System.Collections.IEnumerator.Reset() { 48 | this.index = 0; 49 | this.current = default(T); 50 | } 51 | 52 | } 53 | 54 | public struct Slot { 55 | internal int hashCode; // Lower 31 bits of hash code, -1 if unused 56 | internal int next; // Index of next entry, -1 if last 57 | internal T value; 58 | } 59 | 60 | private const int LOWER31_BIT_MASK = 0x7FFFFFFF; 61 | 62 | internal MemArrayAllocator buckets; 63 | internal MemArrayAllocator slots; 64 | internal int count; 65 | internal int lastIndex; 66 | internal int freeList; 67 | internal int version; 68 | 69 | public bool isCreated { 70 | [INLINE(256)] 71 | get => this.buckets.isCreated; 72 | } 73 | 74 | public int Count { 75 | [INLINE(256)] 76 | get => this.count; 77 | } 78 | 79 | [INLINE(256)] 80 | public NativeHashSet(ref MemoryAllocator allocator, int capacity) { 81 | 82 | this = default; 83 | this.Initialize(ref allocator, capacity); 84 | 85 | } 86 | 87 | [INLINE(256)] 88 | public void Dispose(ref MemoryAllocator allocator) { 89 | 90 | this.buckets.Dispose(ref allocator); 91 | this.slots.Dispose(ref allocator); 92 | this = default; 93 | 94 | } 95 | 96 | [INLINE(256)] 97 | public readonly EnumeratorNoState GetEnumerator(in MemoryAllocator allocator) { 98 | 99 | return new EnumeratorNoState(in allocator, this); 100 | 101 | } 102 | 103 | [INLINE(256)] 104 | public ref T GetByIndex(in MemoryAllocator allocator, int index) { 105 | return ref this.slots[in allocator, index].value; 106 | } 107 | 108 | /// 109 | /// Remove all items from this set. This clears the elements but not the underlying 110 | /// buckets and slots array. Follow this call by TrimExcess to release these. 111 | /// 112 | /// 113 | [INLINE(256)] 114 | public void Clear(in MemoryAllocator allocator) { 115 | if (this.lastIndex > 0) { 116 | // clear the elements so that the gc can reclaim the references. 117 | // clear only up to m_lastIndex for m_slots 118 | this.slots.Clear(in allocator, 0, this.lastIndex); 119 | this.buckets.Clear(in allocator, 0, this.buckets.Length); 120 | this.lastIndex = 0; 121 | this.count = 0; 122 | this.freeList = -1; 123 | } 124 | this.version++; 125 | } 126 | 127 | /// 128 | /// Checks if this hashset contains the item 129 | /// 130 | /// 131 | /// item to check for containment 132 | /// true if item contained; false if not 133 | [INLINE(256)] 134 | public readonly bool Contains(in MemoryAllocator allocator, T item) { 135 | if (this.buckets.isCreated == true) { 136 | int hashCode = this.InternalGetHashCode(in allocator, item); 137 | // see note at "HashSet" level describing why "- 1" appears in for loop 138 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 139 | if (this.slots[in allocator, i].hashCode == hashCode && 140 | this.slots[in allocator, i].value.Equals(in allocator, item) == true) { 141 | return true; 142 | } 143 | } 144 | } 145 | // either m_buckets is null or wasn't found 146 | return false; 147 | } 148 | 149 | /// 150 | /// Remove item from this hashset 151 | /// 152 | /// 153 | /// item to remove 154 | /// true if removed; false if not (i.e. if the item wasn't in the HashSet) 155 | [INLINE(256)] 156 | public bool Remove(ref MemoryAllocator allocator, T item) { 157 | if (this.buckets.isCreated == true) { 158 | int hashCode = this.InternalGetHashCode(in allocator, item); 159 | int bucket = hashCode % this.buckets.Length; 160 | int last = -1; 161 | for (int i = this.buckets[in allocator, bucket] - 1; i >= 0; last = i, i = this.slots[in allocator, i].next) { 162 | if (this.slots[in allocator, i].hashCode == hashCode && 163 | this.slots[in allocator, i].value.Equals(in allocator, item) == true) { 164 | if (last < 0) { 165 | // first iteration; update buckets 166 | this.buckets[in allocator, bucket] = this.slots[in allocator, i].next + 1; 167 | } 168 | else { 169 | // subsequent iterations; update 'next' pointers 170 | this.slots[in allocator, last].next = this.slots[in allocator, i].next; 171 | } 172 | this.slots[in allocator, i].hashCode = -1; 173 | this.slots[in allocator, i].value = default(T); 174 | this.slots[in allocator, i].next = this.freeList; 175 | 176 | this.count--; 177 | this.version++; 178 | if (this.count == 0) { 179 | this.lastIndex = 0; 180 | this.freeList = -1; 181 | } 182 | else { 183 | this.freeList = i; 184 | } 185 | return true; 186 | } 187 | } 188 | } 189 | // either m_buckets is null or wasn't found 190 | return false; 191 | } 192 | 193 | /// 194 | /// Add item to this HashSet. Returns bool indicating whether item was added (won't be 195 | /// added if already present) 196 | /// 197 | /// 198 | /// 199 | /// true if added, false if already present 200 | [INLINE(256)] 201 | public bool Add(ref MemoryAllocator allocator, T item) { 202 | return this.AddIfNotPresent(ref allocator, item); 203 | } 204 | 205 | /// 206 | /// Searches the set for a given value and returns the equal value it finds, if any. 207 | /// 208 | /// 209 | /// The value to search for. 210 | /// The value from the set that the search found, or the default value of when the search yielded no match. 211 | /// A value indicating whether the search was successful. 212 | /// 213 | /// This can be useful when you want to reuse a previously stored reference instead of 214 | /// a newly constructed one (so that more sharing of references can occur) or to look up 215 | /// a value that has more complete data than the value you currently have, although their 216 | /// comparer functions indicate they are equal. 217 | /// 218 | [INLINE(256)] 219 | public readonly bool TryGetValue(ref MemoryAllocator allocator, T equalValue, out T actualValue) { 220 | if (this.buckets.isCreated == true) { 221 | int i = this.InternalIndexOf(in allocator, equalValue); 222 | if (i >= 0) { 223 | actualValue = this.slots[in allocator, i].value; 224 | return true; 225 | } 226 | } 227 | actualValue = default(T); 228 | return false; 229 | } 230 | 231 | [INLINE(256)] 232 | public ref T GetValue(in MemoryAllocator allocator, T equalValue) { 233 | 234 | int i = this.InternalIndexOf(in allocator, equalValue); 235 | if (i >= 0) { 236 | return ref this.slots[in allocator, i].value; 237 | } 238 | 239 | throw new System.Collections.Generic.KeyNotFoundException(); 240 | 241 | } 242 | 243 | #region Helper 244 | /// 245 | /// Initializes buckets and slots arrays. Uses suggested capacity by finding next prime 246 | /// greater than or equal to capacity. 247 | /// 248 | /// 249 | /// 250 | [INLINE(256)] 251 | private void Initialize(ref MemoryAllocator allocator, int capacity) { 252 | int size = System.Collections.Generic.HashHelpers.GetPrime(capacity); 253 | this.buckets = new MemArrayAllocator(ref allocator, size); 254 | this.slots = new MemArrayAllocator(ref allocator, size); 255 | this.freeList = -1; 256 | } 257 | 258 | /// 259 | /// Expand to new capacity. New capacity is next prime greater than or equal to suggested 260 | /// size. This is called when the underlying array is filled. This performs no 261 | /// defragmentation, allowing faster execution; note that this is reasonable since 262 | /// AddIfNotPresent attempts to insert new elements in re-opened spots. 263 | /// 264 | /// 265 | [INLINE(256)] 266 | private void IncreaseCapacity(ref MemoryAllocator allocator) { 267 | int newSize = System.Collections.Generic.HashHelpers.ExpandPrime(this.count); 268 | if (newSize <= this.count) { 269 | throw new System.ArgumentException(); 270 | } 271 | 272 | // Able to increase capacity; copy elements to larger array and rehash 273 | this.SetCapacity(ref allocator, newSize, false); 274 | } 275 | 276 | /// 277 | /// Set the underlying buckets array to size newSize and rehash. Note that newSize 278 | /// *must* be a prime. It is very likely that you want to call IncreaseCapacity() 279 | /// instead of this method. 280 | /// 281 | [INLINE(256)] 282 | private void SetCapacity(ref MemoryAllocator allocator, int newSize, bool forceNewHashCodes) { 283 | System.Diagnostics.Contracts.Contract.Assert(System.Collections.Generic.HashHelpers.IsPrime(newSize), "New size is not prime!"); 284 | 285 | System.Diagnostics.Contracts.Contract.Assert(this.buckets.isCreated, "SetCapacity called on a set with no elements"); 286 | 287 | var newSlots = new MemArrayAllocator(ref allocator, newSize); 288 | if (this.slots.isCreated == true) { 289 | NativeArrayUtils.CopyNoChecks(ref allocator, in this.slots, 0, ref newSlots, 0, this.lastIndex); 290 | } 291 | 292 | if (forceNewHashCodes == true) { 293 | for(int i = 0; i < this.lastIndex; i++) { 294 | if(newSlots[in allocator, i].hashCode != -1) { 295 | newSlots[in allocator, i].hashCode = this.InternalGetHashCode(in allocator, newSlots[in allocator, i].value); 296 | } 297 | } 298 | } 299 | 300 | var newBuckets = new MemArrayAllocator(ref allocator, newSize); 301 | for (int i = 0; i < this.lastIndex; i++) { 302 | int bucket = newSlots[in allocator, i].hashCode % newSize; 303 | newSlots[in allocator, i].next = newBuckets[in allocator, bucket] - 1; 304 | newBuckets[in allocator, bucket] = i + 1; 305 | } 306 | if (this.slots.isCreated == true) this.slots.Dispose(ref allocator); 307 | if (this.buckets.isCreated == true) this.buckets.Dispose(ref allocator); 308 | this.slots = newSlots; 309 | this.buckets = newBuckets; 310 | } 311 | 312 | /// 313 | /// Adds value to HashSet if not contained already 314 | /// Returns true if added and false if already present 315 | /// 316 | /// 317 | /// value to find 318 | /// 319 | [INLINE(256)] 320 | private bool AddIfNotPresent(ref MemoryAllocator allocator, T value) { 321 | if (this.buckets.isCreated == false) { 322 | this.Initialize(ref allocator, 0); 323 | } 324 | 325 | int hashCode = this.InternalGetHashCode(in allocator, value); 326 | int bucket = hashCode % this.buckets.Length; 327 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 328 | if (this.slots[in allocator, i].hashCode == hashCode && 329 | this.slots[in allocator, i].value.Equals(in allocator, value) == true) { 330 | return false; 331 | } 332 | } 333 | 334 | int index; 335 | if (this.freeList >= 0) { 336 | index = this.freeList; 337 | this.freeList = this.slots[in allocator, index].next; 338 | } 339 | else { 340 | if (this.lastIndex == this.slots.Length) { 341 | this.IncreaseCapacity(ref allocator); 342 | // this will change during resize 343 | bucket = hashCode % this.buckets.Length; 344 | } 345 | index = this.lastIndex; 346 | this.lastIndex++; 347 | } 348 | this.slots[in allocator, index].hashCode = hashCode; 349 | this.slots[in allocator, index].value = value; 350 | this.slots[in allocator, index].next = this.buckets[in allocator, bucket] - 1; 351 | this.buckets[in allocator, bucket] = index + 1; 352 | this.count++; 353 | this.version++; 354 | 355 | return true; 356 | } 357 | 358 | // Add value at known index with known hash code. Used only 359 | // when constructing from another HashSet. 360 | [INLINE(256)] 361 | private void AddValue(ref MemoryAllocator allocator, int index, int hashCode, T value) { 362 | int bucket = hashCode % this.buckets.Length; 363 | this.slots[in allocator, index].hashCode = hashCode; 364 | this.slots[in allocator, index].value = value; 365 | this.slots[in allocator, index].next = this.buckets[in allocator, bucket] - 1; 366 | this.buckets[in allocator, bucket] = index + 1; 367 | } 368 | 369 | /// 370 | /// Used internally by set operations which have to rely on bit array marking. This is like 371 | /// Contains but returns index in slots array. 372 | /// 373 | /// 374 | /// 375 | /// 376 | [INLINE(256)] 377 | private readonly int InternalIndexOf(in MemoryAllocator allocator, T item) { 378 | int hashCode = this.InternalGetHashCode(in allocator, item); 379 | for (int i = this.buckets[in allocator, hashCode % this.buckets.Length] - 1; i >= 0; i = this.slots[in allocator, i].next) { 380 | if ((this.slots[in allocator, i].hashCode) == hashCode && 381 | this.slots[in allocator, i].value.Equals(in allocator, item) == true) { 382 | return i; 383 | } 384 | } 385 | // wasn't found 386 | return -1; 387 | } 388 | 389 | /// 390 | /// Workaround Comparers that throw ArgumentNullException for GetHashCode(null). 391 | /// 392 | /// 393 | /// hash code 394 | [INLINE(256)] 395 | private readonly int InternalGetHashCode(in MemoryAllocator allocator, T item) { 396 | return item.GetHash(in allocator) & NativeHashSet.LOWER31_BIT_MASK; 397 | } 398 | #endregion 399 | 400 | } 401 | 402 | } 403 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/NativeHashSet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9a6c646072b741328ae371266b1113c0 3 | timeCreated: 1659015974 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/Queue.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Collections { 2 | 3 | using MemPtr = System.Int64; 4 | 5 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(QueueProxy<>))] 6 | public struct Queue where T : unmanaged { 7 | 8 | private const int MINIMUM_GROW = 4; 9 | private const int GROW_FACTOR = 200; 10 | 11 | private MemArrayAllocator array; 12 | private int head; 13 | private int tail; 14 | private int size; 15 | private int version; 16 | public readonly bool isCreated => this.array.isCreated; 17 | 18 | public readonly int Count => this.size; 19 | 20 | public Queue(ref MemoryAllocator allocator, int capacity) { 21 | this = default; 22 | this.array = new MemArrayAllocator(ref allocator, capacity); 23 | } 24 | 25 | public void Dispose(ref MemoryAllocator allocator) { 26 | 27 | this.array.Dispose(ref allocator); 28 | this = default; 29 | 30 | } 31 | 32 | public void Clear(in MemoryAllocator allocator) { 33 | this.head = 0; 34 | this.tail = 0; 35 | this.size = 0; 36 | this.version++; 37 | } 38 | 39 | public void Enqueue(ref MemoryAllocator allocator, T item) { 40 | if (this.size == this.array.Length) { 41 | var newCapacity = (int)((long)this.array.Length * (long)Queue.GROW_FACTOR / 100); 42 | if (newCapacity < this.array.Length + Queue.MINIMUM_GROW) { 43 | newCapacity = this.array.Length + Queue.MINIMUM_GROW; 44 | } 45 | 46 | this.SetCapacity(ref allocator, newCapacity); 47 | } 48 | 49 | this.array[in allocator, this.tail] = item; 50 | this.tail = (this.tail + 1) % this.array.Length; 51 | this.size++; 52 | this.version++; 53 | } 54 | 55 | public EnumeratorNoState GetEnumerator(in MemoryAllocator allocator) { 56 | return new EnumeratorNoState(this, in allocator); 57 | } 58 | 59 | public T Dequeue(in MemoryAllocator allocator) { 60 | if (this.size == 0) { 61 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue); 62 | } 63 | 64 | var removed = this.array[in allocator, this.head]; 65 | this.array[in allocator, this.head] = default(T); 66 | this.head = (this.head + 1) % this.array.Length; 67 | this.size--; 68 | this.version++; 69 | return removed; 70 | } 71 | 72 | public T Peek(in MemoryAllocator allocator) { 73 | if (this.size == 0) { 74 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue); 75 | } 76 | 77 | return this.array[in allocator, this.head]; 78 | } 79 | 80 | public bool Contains(in MemoryAllocator allocator, U item) where U : System.IEquatable { 81 | var index = this.head; 82 | var count = this.size; 83 | 84 | while (count-- > 0) { 85 | if (item.Equals(this.array[in allocator, index])) { 86 | return true; 87 | } 88 | 89 | index = (index + 1) % this.array.Length; 90 | } 91 | 92 | return false; 93 | } 94 | 95 | private T GetElement(in MemoryAllocator allocator, int i) { 96 | return this.array[in allocator, (this.head + i) % this.array.Length]; 97 | } 98 | 99 | private void SetCapacity(ref MemoryAllocator allocator, int capacity) { 100 | this.array.Resize(ref allocator, capacity); 101 | this.head = 0; 102 | this.tail = this.size == capacity ? 0 : this.size; 103 | this.version++; 104 | } 105 | 106 | public struct EnumeratorNoState : System.Collections.Generic.IEnumerator { 107 | 108 | private readonly MemoryAllocator allocator; 109 | private Queue q; 110 | private int index; // -1 = not started, -2 = ended/disposed 111 | private readonly int version; 112 | private T currentElement; 113 | 114 | internal EnumeratorNoState(Queue q, in MemoryAllocator allocator) { 115 | this.q = q; 116 | this.version = this.q.version; 117 | this.index = -1; 118 | this.currentElement = default(T); 119 | this.allocator = allocator; 120 | } 121 | 122 | public void Dispose() { 123 | this.index = -2; 124 | this.currentElement = default(T); 125 | } 126 | 127 | public bool MoveNext() { 128 | if (this.version != this.q.version) { 129 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); 130 | } 131 | 132 | if (this.index == -2) { 133 | return false; 134 | } 135 | 136 | this.index++; 137 | 138 | if (this.index == this.q.size) { 139 | this.index = -2; 140 | this.currentElement = default(T); 141 | return false; 142 | } 143 | 144 | this.currentElement = this.q.GetElement(in this.allocator, this.index); 145 | return true; 146 | } 147 | 148 | public T Current { 149 | get { 150 | if (this.index < 0) { 151 | if (this.index == -1) { 152 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted); 153 | } else { 154 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded); 155 | } 156 | } 157 | 158 | return this.currentElement; 159 | } 160 | } 161 | 162 | object System.Collections.IEnumerator.Current { 163 | get { 164 | if (this.index < 0) { 165 | if (this.index == -1) { 166 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted); 167 | } else { 168 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded); 169 | } 170 | } 171 | 172 | return this.currentElement; 173 | } 174 | } 175 | 176 | void System.Collections.IEnumerator.Reset() { 177 | if (this.version != this.q.version) { 178 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); 179 | } 180 | 181 | this.index = -1; 182 | this.currentElement = default(T); 183 | } 184 | 185 | } 186 | 187 | } 188 | 189 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/Queue.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a10bd35d97424892bbdef7f5caf81fe0 3 | timeCreated: 1659027661 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/SparseSet.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Collections { 2 | 3 | using Unity.Collections.LowLevel.Unsafe; 4 | 5 | public struct SparseSet where T : unmanaged { 6 | 7 | private MemArraySlicedAllocator dense; 8 | private MemArrayAllocator sparse; 9 | private Stack freeIndexes; 10 | 11 | public bool isCreated => this.sparse.isCreated; 12 | public int Length => this.sparse.Length; 13 | 14 | public SparseSet(ref MemoryAllocator allocator, int length) { 15 | 16 | this.dense = default; 17 | this.sparse = default; 18 | this.freeIndexes = default; 19 | this.Validate(ref allocator, length); 20 | 21 | } 22 | 23 | public T ReadDense(in MemoryAllocator allocator, int sparseIndex) { 24 | 25 | return this.dense[in allocator, sparseIndex]; 26 | 27 | } 28 | 29 | public ref T GetDense(in MemoryAllocator allocator, int sparseIndex) { 30 | 31 | return ref this.dense[in allocator, sparseIndex]; 32 | 33 | } 34 | 35 | public MemArrayAllocator GetSparse() { 36 | 37 | return this.sparse; 38 | 39 | } 40 | 41 | public SparseSet Merge(ref MemoryAllocator allocator) { 42 | 43 | this.dense = this.dense.Merge(ref allocator); 44 | return this; 45 | 46 | } 47 | 48 | public void Validate(ref MemoryAllocator allocator, int capacity) { 49 | 50 | if (this.freeIndexes.isCreated == false) this.freeIndexes = new Stack(ref allocator, 10); 51 | this.sparse.Resize(ref allocator, capacity); 52 | 53 | } 54 | 55 | public SparseSet Dispose(ref MemoryAllocator allocator) { 56 | 57 | this.freeIndexes.Dispose(ref allocator); 58 | this.sparse.Dispose(ref allocator); 59 | this.dense.Dispose(ref allocator); 60 | return this; 61 | 62 | } 63 | 64 | public void Set(ref MemoryAllocator allocator, int fromEntityId, int toEntityId, in T data) { 65 | 66 | for (int i = fromEntityId; i <= toEntityId; ++i) { 67 | this.Set(ref allocator, i, in data); 68 | } 69 | 70 | } 71 | 72 | public int Set(ref MemoryAllocator allocator, int entityId, in T data) { 73 | 74 | ref var idx = ref this.sparse[in allocator, entityId]; 75 | if (idx == 0) { 76 | if (this.freeIndexes.Count > 0) { 77 | idx = this.freeIndexes.Pop(in allocator); 78 | } else { 79 | idx = this.dense.Length + 1; 80 | } 81 | } 82 | 83 | this.dense.Resize(ref allocator, idx + 1, out _); 84 | this.dense[in allocator, idx] = data; 85 | return idx; 86 | 87 | } 88 | 89 | public ref T Get(ref MemoryAllocator allocator, int entityId) { 90 | 91 | var idx = this.sparse[in allocator, entityId]; 92 | if (idx == 0) idx = this.Set(ref allocator, entityId, default); 93 | return ref this.dense[in allocator, idx]; 94 | 95 | } 96 | 97 | public T Read(ref MemoryAllocator allocator, int entityId) { 98 | 99 | var idx = this.sparse[in allocator, entityId]; 100 | if (idx == 0) return default; 101 | return this.dense[in allocator, idx]; 102 | 103 | } 104 | 105 | public void Remove(ref MemoryAllocator allocator, int entityId) { 106 | 107 | ref var idx = ref this.sparse[in allocator, entityId]; 108 | this.dense[in allocator, idx] = default; 109 | this.freeIndexes.Push(ref allocator, idx); 110 | idx = 0; 111 | 112 | } 113 | 114 | public void Remove(ref MemoryAllocator allocator, int entityId, int length) { 115 | 116 | for (int i = entityId; i < length; ++i) { 117 | this.Remove(ref allocator, i); 118 | } 119 | 120 | } 121 | 122 | public T Has(ref MemoryAllocator allocator, int entityId) { 123 | 124 | return this.Get(ref allocator, entityId); 125 | 126 | } 127 | 128 | public unsafe int SetPtr(MemoryAllocator* allocator, int entityId, in T data) { 129 | 130 | ref var alloc = ref UnsafeUtility.AsRef(allocator); 131 | return this.Set(ref alloc, entityId, in data); 132 | 133 | } 134 | 135 | public unsafe T ReadPtr(MemoryAllocator* allocator, int entityId) { 136 | 137 | ref var alloc = ref UnsafeUtility.AsRef(allocator); 138 | var idx = this.sparse[in alloc, entityId]; 139 | if (idx == 0) return default; 140 | return this.Get(ref alloc, entityId); 141 | 142 | } 143 | 144 | public unsafe bool HasDataPtr(MemoryAllocator* allocator, int entityId) { 145 | 146 | ref var alloc = ref UnsafeUtility.AsRef(allocator); 147 | var idx = this.sparse[in alloc, entityId]; 148 | if (idx == 0) return false; 149 | return true; 150 | 151 | } 152 | 153 | public unsafe T HasPtr(MemoryAllocator* allocator, int entityId) { 154 | 155 | return this.ReadPtr(allocator, entityId); 156 | 157 | } 158 | 159 | public unsafe ref T GetPtr(MemoryAllocator* allocator, int entityId) { 160 | 161 | ref var alloc = ref UnsafeUtility.AsRef(allocator); 162 | return ref this.Get(ref alloc, entityId); 163 | 164 | } 165 | 166 | public unsafe void RemovePtr(MemoryAllocator* allocator, int entityId, int length) { 167 | 168 | ref var alloc = ref UnsafeUtility.AsRef(allocator); 169 | this.Remove(ref alloc, entityId, length); 170 | 171 | } 172 | 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/SparseSet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ce4159228c8a4b809dbe3fb2085103e3 3 | timeCreated: 1659355101 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/Stack.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Collections { 2 | 3 | using MemPtr = System.Int64; 4 | 5 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(StackProxy<>))] 6 | public struct Stack where T : unmanaged { 7 | 8 | private const int DEFAULT_CAPACITY = 4; 9 | 10 | private MemArrayAllocator array; 11 | private int size; 12 | private int version; 13 | public bool isCreated => this.array.isCreated; 14 | 15 | public readonly int Count => this.size; 16 | 17 | public Stack(ref MemoryAllocator allocator, int capacity) { 18 | this = default; 19 | this.array = new MemArrayAllocator(ref allocator, capacity); 20 | } 21 | 22 | public void Dispose(ref MemoryAllocator allocator) { 23 | 24 | this.array.Dispose(ref allocator); 25 | this = default; 26 | 27 | } 28 | 29 | public void Clear(in MemoryAllocator allocator) { 30 | this.size = 0; 31 | this.version++; 32 | } 33 | 34 | public bool Contains(in MemoryAllocator allocator, U item) where U : System.IEquatable { 35 | 36 | var count = this.size; 37 | while (count-- > 0) { 38 | if (item.Equals(this.array[in allocator, count])) { 39 | return true; 40 | } 41 | } 42 | 43 | return false; 44 | 45 | } 46 | 47 | public readonly EnumeratorNoState GetEnumerator(in MemoryAllocator allocator) { 48 | return new EnumeratorNoState(this, in allocator); 49 | } 50 | 51 | public readonly T Peek(in MemoryAllocator allocator) { 52 | if (this.size == 0) { 53 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack); 54 | } 55 | 56 | return this.array[in allocator, this.size - 1]; 57 | } 58 | 59 | public T Pop(in MemoryAllocator allocator) { 60 | if (this.size == 0) { 61 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack); 62 | } 63 | 64 | this.version++; 65 | var item = this.array[in allocator, --this.size]; 66 | this.array[in allocator, this.size] = default; 67 | return item; 68 | } 69 | 70 | public void Push(ref MemoryAllocator allocator, T item) { 71 | if (this.size == this.array.Length) { 72 | this.array.Resize(ref allocator, this.array.Length == 0 ? Stack.DEFAULT_CAPACITY : 2 * this.array.Length); 73 | } 74 | 75 | this.array[in allocator, this.size++] = item; 76 | this.version++; 77 | } 78 | 79 | public struct EnumeratorNoState : System.Collections.Generic.IEnumerator { 80 | 81 | private readonly Stack stack; 82 | private readonly MemoryAllocator allocator; 83 | private int index; 84 | private readonly int version; 85 | private T currentElement; 86 | 87 | internal EnumeratorNoState(Stack stack, in MemoryAllocator allocator) { 88 | this.stack = stack; 89 | this.allocator = allocator; 90 | this.version = this.stack.version; 91 | this.index = -2; 92 | this.currentElement = default(T); 93 | } 94 | 95 | public void Dispose() { 96 | this.index = -1; 97 | } 98 | 99 | public bool MoveNext() { 100 | bool retval; 101 | if (this.version != this.stack.version) { 102 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); 103 | } 104 | 105 | if (this.index == -2) { // First call to enumerator. 106 | this.index = this.stack.size - 1; 107 | retval = this.index >= 0; 108 | if (retval) { 109 | this.currentElement = this.stack.array[in this.allocator, this.index]; 110 | } 111 | 112 | return retval; 113 | } 114 | 115 | if (this.index == -1) { // End of enumeration. 116 | return false; 117 | } 118 | 119 | retval = --this.index >= 0; 120 | if (retval) { 121 | this.currentElement = this.stack.array[in this.allocator, this.index]; 122 | } else { 123 | this.currentElement = default(T); 124 | } 125 | 126 | return retval; 127 | } 128 | 129 | public T Current { 130 | get { 131 | if (this.index == -2) { 132 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted); 133 | } 134 | 135 | if (this.index == -1) { 136 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded); 137 | } 138 | 139 | return this.currentElement; 140 | } 141 | } 142 | 143 | object System.Collections.IEnumerator.Current { 144 | get { 145 | if (this.index == -2) { 146 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted); 147 | } 148 | 149 | if (this.index == -1) { 150 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded); 151 | } 152 | 153 | return this.currentElement; 154 | } 155 | } 156 | 157 | void System.Collections.IEnumerator.Reset() { 158 | if (this.version != this.stack.version) { 159 | ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); 160 | } 161 | 162 | this.index = -2; 163 | this.currentElement = default; 164 | } 165 | 166 | } 167 | 168 | } 169 | 170 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/Stack.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a059f03784a149ad9888881391ad66a8 3 | timeCreated: 1659025274 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Collections/ThrowHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f3918c2a78d245a6ac3c94d3f1be3bb1 3 | timeCreated: 1667980008 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1d6ebf65d672ebc40a565928e3fe0431 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemArray.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | using MemPtr = System.Int64; 4 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 5 | 6 | public struct UnsafeMemArrayAllocator { 7 | 8 | public MemPtr arrPtr; 9 | public int Length; 10 | public int growFactor; 11 | 12 | public readonly bool isCreated { 13 | [INLINE(256)] 14 | get => this.arrPtr != 0L; 15 | } 16 | 17 | [INLINE(256)] 18 | public UnsafeMemArrayAllocator(int sizeOf, ref MemoryAllocator allocator, int length, ClearOptions clearOptions = ClearOptions.ClearMemory, int growFactor = 1) { 19 | 20 | this.arrPtr = length > 0 ? allocator.AllocArray(length, sizeOf) : 0; 21 | this.Length = length; 22 | this.growFactor = growFactor; 23 | 24 | if (clearOptions == ClearOptions.ClearMemory) { 25 | this.Clear(sizeOf, in allocator); 26 | } 27 | 28 | } 29 | 30 | [INLINE(256)] 31 | public ref T Get(in MemoryAllocator allocator, int index) where T : unmanaged { 32 | E.RANGE(index, 0, this.Length); 33 | return ref allocator.RefArray(this.arrPtr, index); 34 | } 35 | 36 | [INLINE(256)] 37 | public void Dispose(ref MemoryAllocator allocator) { 38 | 39 | if (this.arrPtr != 0) { 40 | allocator.Free(this.arrPtr); 41 | } 42 | this = default; 43 | 44 | } 45 | 46 | [INLINE(256)] 47 | public readonly unsafe void* GetUnsafePtr(in MemoryAllocator allocator) { 48 | 49 | return allocator.GetUnsafePtr(this.arrPtr); 50 | 51 | } 52 | 53 | [INLINE(256)] 54 | public void Clear(int sizeOf, in MemoryAllocator allocator) { 55 | 56 | this.Clear(sizeOf, in allocator, 0, this.Length); 57 | 58 | } 59 | 60 | [INLINE(256)] 61 | public void Clear(int sizeOf, in MemoryAllocator allocator, int index, int length) { 62 | 63 | var size = sizeOf; 64 | allocator.MemClear(this.arrPtr, index * size, length * size); 65 | 66 | } 67 | 68 | [INLINE(256)] 69 | public bool Resize(int sizeOf, ref MemoryAllocator allocator, int newLength, ClearOptions options = ClearOptions.ClearMemory, int growFactor = 1) { 70 | 71 | if (this.isCreated == false) { 72 | 73 | this = new UnsafeMemArrayAllocator(sizeOf, ref allocator, newLength, options, growFactor); 74 | return true; 75 | 76 | } 77 | 78 | if (newLength <= this.Length) { 79 | 80 | return false; 81 | 82 | } 83 | 84 | newLength *= this.growFactor; 85 | 86 | var prevLength = this.Length; 87 | this.arrPtr = allocator.ReAllocArray(sizeOf, this.arrPtr, newLength); 88 | if (options == ClearOptions.ClearMemory) { 89 | this.Clear(sizeOf, in allocator, prevLength, newLength - prevLength); 90 | } 91 | this.Length = newLength; 92 | return true; 93 | 94 | } 95 | 96 | } 97 | 98 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(MemArrayAllocatorProxy<>))] 99 | public struct MemArrayAllocator where T : unmanaged { 100 | 101 | public struct EnumeratorNoState : System.Collections.Generic.IEnumerator { 102 | 103 | private readonly MemoryAllocator allocator; 104 | private readonly MemArrayAllocator list; 105 | private int index; 106 | 107 | internal EnumeratorNoState(in MemoryAllocator allocator, MemArrayAllocator list) { 108 | this.allocator = allocator; 109 | this.list = list; 110 | this.index = -1; 111 | } 112 | 113 | public void Dispose() { 114 | } 115 | 116 | public bool MoveNext() { 117 | ++this.index; 118 | return this.index < this.list.Length; 119 | } 120 | 121 | public T Current => this.list[in this.allocator, this.index]; 122 | 123 | object System.Collections.IEnumerator.Current => this.Current; 124 | 125 | void System.Collections.IEnumerator.Reset() { 126 | this.index = -1; 127 | } 128 | 129 | } 130 | 131 | public MemPtr arrPtr; 132 | public int Length; 133 | public int growFactor; 134 | 135 | public readonly bool isCreated { 136 | [INLINE(256)] 137 | get => this.arrPtr != 0L; 138 | } 139 | 140 | [INLINE(256)] 141 | public MemArrayAllocator(ref MemoryAllocator allocator, int length, ClearOptions clearOptions = ClearOptions.ClearMemory, int growFactor = 1) { 142 | 143 | this.arrPtr = length > 0 ? allocator.AllocArray(length) : 0; 144 | this.Length = length; 145 | this.growFactor = growFactor; 146 | 147 | if (clearOptions == ClearOptions.ClearMemory) { 148 | this.Clear(in allocator); 149 | } 150 | 151 | } 152 | 153 | [INLINE(256)] 154 | public MemArrayAllocator(ref MemoryAllocator allocator, MemArrayAllocator arr) { 155 | 156 | this.arrPtr = arr.arrPtr; 157 | this.Length = arr.Length; 158 | this.growFactor = arr.growFactor; 159 | 160 | } 161 | 162 | [INLINE(256)] 163 | public MemArrayAllocator(ref MemoryAllocator allocator, MemPtr ptr, int length, int growFactor) { 164 | 165 | this.arrPtr = ptr; 166 | this.Length = length; 167 | this.growFactor = growFactor; 168 | 169 | } 170 | 171 | [INLINE(256)] 172 | public MemArrayAllocator(ref MemoryAllocator allocator, System.Collections.Generic.List arr) { 173 | 174 | this = new MemArrayAllocator(ref allocator, arr.Count, ClearOptions.UninitializedMemory); 175 | NativeArrayUtils.Copy(in allocator, arr.ToArray(), 0, ref this, 0, arr.Count); 176 | 177 | } 178 | 179 | [INLINE(256)] 180 | public MemArrayAllocator(ref MemoryAllocator allocator, T[] arr) { 181 | 182 | this = new MemArrayAllocator(ref allocator, arr.Length, ClearOptions.UninitializedMemory); 183 | NativeArrayUtils.Copy(in allocator, arr, 0, ref this, 0, arr.Length); 184 | 185 | } 186 | 187 | [INLINE(256)] 188 | public readonly ref U As(in MemoryAllocator allocator, int index) where U : struct { 189 | E.RANGE(index, 0, this.Length); 190 | return ref allocator.RefArray(this.arrPtr, index); 191 | } 192 | 193 | [INLINE(256)] 194 | public void ReplaceWith(ref MemoryAllocator allocator, in MemArrayAllocator other) { 195 | 196 | if (other.arrPtr == this.arrPtr) { 197 | return; 198 | } 199 | 200 | this.Dispose(ref allocator); 201 | this = other; 202 | 203 | } 204 | 205 | [INLINE(256)] 206 | public void CopyFrom(ref MemoryAllocator allocator, in MemArrayAllocator other) { 207 | 208 | if (other.arrPtr == this.arrPtr) return; 209 | if (this.arrPtr == 0L && other.arrPtr == 0L) return; 210 | if (this.arrPtr != 0L && other.arrPtr == 0L) { 211 | this.Dispose(ref allocator); 212 | return; 213 | } 214 | if (this.arrPtr == 0L) this = new MemArrayAllocator(ref allocator, other.Length); 215 | 216 | NativeArrayUtils.Copy(ref allocator, in other, ref this); 217 | 218 | } 219 | 220 | [INLINE(256)] 221 | public void Dispose(ref MemoryAllocator allocator) { 222 | 223 | if (this.arrPtr != 0) { 224 | allocator.Free(this.arrPtr); 225 | } 226 | this = default; 227 | 228 | } 229 | 230 | [INLINE(256)] 231 | public readonly unsafe void* GetUnsafePtr(in MemoryAllocator allocator) { 232 | 233 | return allocator.GetUnsafePtr(this.arrPtr); 234 | 235 | } 236 | 237 | [INLINE(256)] 238 | public readonly EnumeratorNoState GetEnumerator(in MemoryAllocator allocator) { 239 | 240 | return new EnumeratorNoState(in allocator, this); 241 | 242 | } 243 | 244 | public ref T this[in MemoryAllocator allocator, int index] { 245 | [INLINE(256)] 246 | get { 247 | E.RANGE(index, 0, this.Length); 248 | return ref allocator.RefArray(this.arrPtr, index); 249 | } 250 | } 251 | 252 | public ref T this[MemoryAllocator allocator, int index] { 253 | [INLINE(256)] 254 | get { 255 | E.RANGE(index, 0, this.Length); 256 | return ref allocator.RefArray(this.arrPtr, index); 257 | } 258 | } 259 | 260 | [INLINE(256)] 261 | public bool Resize(ref MemoryAllocator allocator, int newLength, ClearOptions options = ClearOptions.ClearMemory, int growFactor = 1) { 262 | 263 | if (this.isCreated == false) { 264 | 265 | this = new MemArrayAllocator(ref allocator, newLength, options, growFactor); 266 | return true; 267 | 268 | } 269 | 270 | if (newLength <= this.Length) { 271 | 272 | return false; 273 | 274 | } 275 | 276 | newLength *= this.growFactor; 277 | 278 | var prevLength = this.Length; 279 | this.arrPtr = allocator.ReAllocArray(this.arrPtr, newLength); 280 | if (options == ClearOptions.ClearMemory) { 281 | this.Clear(in allocator, prevLength, newLength - prevLength); 282 | } 283 | this.Length = newLength; 284 | return true; 285 | 286 | } 287 | 288 | [INLINE(256)] 289 | public void Clear(in MemoryAllocator allocator) { 290 | 291 | this.Clear(in allocator, 0, this.Length); 292 | 293 | } 294 | 295 | [INLINE(256)] 296 | public void Clear(in MemoryAllocator allocator, int index, int length) { 297 | 298 | var size = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf(); 299 | allocator.MemClear(this.arrPtr, index * size, length * size); 300 | 301 | } 302 | 303 | } 304 | 305 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemArray.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 05c788748d2d4d8478c44845a230c6fc 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemArraySliced.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | using MemPtr = System.Int64; 4 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 5 | 6 | public unsafe struct UnsafeMemArraySlicedAllocator { 7 | 8 | internal const int BUCKET_SIZE = 4; 9 | 10 | public UnsafeMemArrayAllocator data; 11 | public MemArrayAllocator tails; 12 | public int tailsLength; 13 | 14 | public bool isCreated { 15 | [INLINE(256)] 16 | get => this.data.isCreated; 17 | } 18 | 19 | public int Length { 20 | [INLINE(256)] 21 | get => this.data.Length + this.tailsLength; 22 | } 23 | 24 | [INLINE(256)] 25 | public UnsafeMemArraySlicedAllocator(int sizeOf, ref MemoryAllocator allocator, int length, ClearOptions options = ClearOptions.ClearMemory) { 26 | 27 | this.data = new UnsafeMemArrayAllocator(sizeOf, ref allocator, length, options); 28 | this.tails = default; 29 | this.tailsLength = 0; 30 | 31 | } 32 | 33 | [INLINE(256)] 34 | public void Dispose(ref MemoryAllocator allocator) { 35 | 36 | this.data.Dispose(ref allocator); 37 | 38 | for (int i = 0, length = this.tails.Length; i < length; ++i) { 39 | 40 | ref var tail = ref this.tails[in allocator, i]; 41 | tail.Dispose(ref allocator); 42 | 43 | } 44 | 45 | this.tails.Dispose(ref allocator); 46 | this = default; 47 | 48 | } 49 | 50 | [INLINE(256)] 51 | public unsafe void* GetUnsafePtr(in MemoryAllocator allocator) { 52 | 53 | return this.data.GetUnsafePtr(in allocator); 54 | 55 | } 56 | 57 | [INLINE(256)] 58 | public UnsafeMemArraySlicedAllocator Merge(int sizeOf, ref MemoryAllocator allocator) { 59 | 60 | if (this.tails.isCreated == false || this.tails.Length == 0) { 61 | 62 | return this; 63 | 64 | } 65 | 66 | //var arr = PoolArrayNative.Spawn(this.Length); 67 | //if (this.data.isCreated == true) NativeArrayUtils.Copy(this.data, 0, ref arr, 0, this.data.Length); 68 | var elementSize = sizeOf; 69 | var ptr = this.data.Length * elementSize; 70 | this.data.Resize(sizeOf, ref allocator, this.Length); 71 | for (int i = 0, length = this.tails.Length; i < length; ++i) { 72 | 73 | ref var tail = ref this.tails[in allocator, i]; 74 | if (tail.isCreated == false) continue; 75 | 76 | allocator.MemCopy(this.data.arrPtr, ptr, tail.arrPtr, 0, tail.Length * elementSize); 77 | ptr += tail.Length * elementSize; 78 | tail.Dispose(ref allocator); 79 | 80 | } 81 | 82 | this.tails = default; 83 | this.tailsLength = 0; 84 | 85 | return this; 86 | 87 | } 88 | 89 | [INLINE(256)] 90 | public ref T Get(in MemoryAllocator allocator, int index) where T : unmanaged { 91 | 92 | var data = this.data; 93 | if (index >= data.Length) { 94 | 95 | // Look into tails 96 | var tails = this.tails; 97 | index -= data.Length; 98 | for (int i = 0, length = tails.Length; i < length; ++i) { 99 | 100 | ref var tail = ref tails[in allocator, i]; 101 | var len = tail.Length; 102 | if (index < len) return ref tail.Get(in allocator, index); 103 | index -= len; 104 | 105 | } 106 | 107 | } 108 | 109 | return ref data.Get(in allocator, index); 110 | 111 | } 112 | 113 | [INLINE(256)] 114 | public UnsafeMemArraySlicedAllocator Resize(int sizeOf, ref MemoryAllocator allocator, int newLength, out bool result, ClearOptions options = ClearOptions.ClearMemory) { 115 | 116 | if (this.isCreated == false) { 117 | 118 | result = true; 119 | this = new UnsafeMemArraySlicedAllocator(sizeOf, ref allocator, newLength, options); 120 | return this; 121 | 122 | } 123 | 124 | var index = newLength - 1; 125 | if (index >= this.Length) { 126 | 127 | // Do we need any tail? 128 | var tails = this.tails; 129 | if (tails.isCreated == true) { 130 | // Look into tails 131 | var ptr = this.data.Length; 132 | for (int i = 0, length = this.tails.Length; i < length; ++i) { 133 | 134 | // For each tail determine do we need to resize any tail to store index? 135 | var tail = tails[in allocator, i]; 136 | ptr += tail.Length; 137 | if (index >= ptr) continue; 138 | 139 | // We have found tail without resize needed 140 | // Tail was found - we do not need to resize 141 | result = false; 142 | return this; 143 | 144 | } 145 | } 146 | 147 | // Need to add new tail and resize tails container 148 | var idx = tails.isCreated == false ? 0 : tails.Length; 149 | var size = this.Length; 150 | tails.Resize(ref allocator, idx + 1, options); 151 | var bucketSize = index + UnsafeMemArraySlicedAllocator.BUCKET_SIZE - size; 152 | var newTail = new UnsafeMemArrayAllocator(sizeOf, ref allocator, bucketSize); 153 | tails[in allocator, idx] = newTail; 154 | this.tails = tails; 155 | this.tailsLength += bucketSize; 156 | result = true; 157 | return this; 158 | 159 | } 160 | 161 | // We dont need to resize any 162 | result = false; 163 | return this; 164 | 165 | } 166 | 167 | [INLINE(256)] 168 | public void Clear(int sizeOf, ref MemoryAllocator allocator) { 169 | 170 | this.Clear(sizeOf, ref allocator, 0, this.Length); 171 | 172 | } 173 | 174 | [INLINE(256)] 175 | public void Clear(int sizeOf, ref MemoryAllocator allocator, int index, int length) { 176 | 177 | this.Merge(sizeOf, ref allocator); 178 | var size = sizeOf; 179 | allocator.MemClear(this.data.arrPtr, index * size, length * size); 180 | 181 | } 182 | 183 | } 184 | 185 | public struct MemArraySlicedAllocator where T : unmanaged { 186 | 187 | public MemArrayAllocator data; 188 | public MemArrayAllocator> tails; 189 | public int tailsLength; 190 | 191 | public bool isCreated { 192 | [INLINE(256)] 193 | get => this.data.isCreated; 194 | } 195 | 196 | public int Length { 197 | [INLINE(256)] 198 | get => this.data.Length + this.tailsLength; 199 | } 200 | 201 | [INLINE(256)] 202 | public MemArraySlicedAllocator(ref MemoryAllocator allocator, int length, ClearOptions options = ClearOptions.ClearMemory) { 203 | 204 | this.data = new MemArrayAllocator(ref allocator, length, options); 205 | this.tails = default; 206 | this.tailsLength = 0; 207 | 208 | } 209 | 210 | [INLINE(256)] 211 | public void Dispose(ref MemoryAllocator allocator) { 212 | 213 | this.data.Dispose(ref allocator); 214 | 215 | for (int i = 0, length = this.tails.Length; i < length; ++i) { 216 | 217 | ref var tail = ref this.tails[in allocator, i]; 218 | tail.Dispose(ref allocator); 219 | 220 | } 221 | 222 | this.tails.Dispose(ref allocator); 223 | this = default; 224 | 225 | } 226 | 227 | [INLINE(256)] 228 | public unsafe void* GetUnsafePtr(in MemoryAllocator allocator) { 229 | 230 | return this.data.GetUnsafePtr(in allocator); 231 | 232 | } 233 | 234 | [INLINE(256)] 235 | public MemArraySlicedAllocator Merge(ref MemoryAllocator allocator) { 236 | 237 | if (this.tails.isCreated == false || this.tails.Length == 0) { 238 | 239 | return this; 240 | 241 | } 242 | 243 | //var arr = PoolArrayNative.Spawn(this.Length); 244 | //if (this.data.isCreated == true) NativeArrayUtils.Copy(this.data, 0, ref arr, 0, this.data.Length); 245 | var elementSize = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf(); 246 | var ptr = this.data.Length * elementSize; 247 | this.data.Resize(ref allocator, this.Length); 248 | for (int i = 0, length = this.tails.Length; i < length; ++i) { 249 | 250 | ref var tail = ref this.tails[in allocator, i]; 251 | if (tail.isCreated == false) continue; 252 | 253 | allocator.MemCopy(this.data.arrPtr, ptr, tail.arrPtr, 0, tail.Length * elementSize); 254 | ptr += tail.Length * elementSize; 255 | tail.Dispose(ref allocator); 256 | 257 | } 258 | 259 | this.tails = default; 260 | this.tailsLength = 0; 261 | 262 | return this; 263 | 264 | } 265 | 266 | public ref T this[in MemoryAllocator allocator, int index] { 267 | [INLINE(256)] 268 | get { 269 | var data = this.data; 270 | if (index >= data.Length) { 271 | 272 | // Look into tails 273 | var tails = this.tails; 274 | index -= data.Length; 275 | for (int i = 0, length = tails.Length; i < length; ++i) { 276 | 277 | ref var tail = ref tails[in allocator, i]; 278 | var len = tail.Length; 279 | if (index < len) return ref tail[in allocator, index]; 280 | index -= len; 281 | 282 | } 283 | 284 | } 285 | 286 | return ref data[in allocator, index]; 287 | } 288 | } 289 | 290 | [INLINE(256)] 291 | public MemArraySlicedAllocator Resize(ref MemoryAllocator allocator, int newLength, out bool result, ClearOptions options = ClearOptions.ClearMemory) { 292 | 293 | if (this.isCreated == false) { 294 | 295 | result = true; 296 | this = new MemArraySlicedAllocator(ref allocator, newLength, options); 297 | return this; 298 | 299 | } 300 | 301 | var index = newLength - 1; 302 | if (index >= this.Length) { 303 | 304 | // Do we need any tail? 305 | var tails = this.tails; 306 | if (tails.isCreated == true) { 307 | // Look into tails 308 | var ptr = this.data.Length; 309 | for (int i = 0, length = this.tails.Length; i < length; ++i) { 310 | 311 | // For each tail determine do we need to resize any tail to store index? 312 | var tail = tails[in allocator, i]; 313 | ptr += tail.Length; 314 | if (index >= ptr) continue; 315 | 316 | // We have found tail without resize needed 317 | // Tail was found - we do not need to resize 318 | result = false; 319 | return this; 320 | 321 | } 322 | } 323 | 324 | // Need to add new tail and resize tails container 325 | var idx = tails.isCreated == false ? 0 : tails.Length; 326 | var size = this.Length; 327 | tails.Resize(ref allocator, idx + 1, options); 328 | var bucketSize = index + UnsafeMemArraySlicedAllocator.BUCKET_SIZE - size; 329 | var newTail = new MemArrayAllocator(ref allocator, bucketSize); 330 | tails[in allocator, idx] = newTail; 331 | this.tails = tails; 332 | this.tailsLength += bucketSize; 333 | result = true; 334 | return this; 335 | 336 | } 337 | 338 | // We dont need to resize any 339 | result = false; 340 | return this; 341 | 342 | } 343 | 344 | [INLINE(256)] 345 | public void Clear(ref MemoryAllocator allocator) { 346 | 347 | this.Clear(ref allocator, 0, this.Length); 348 | 349 | } 350 | 351 | [INLINE(256)] 352 | public void Clear(ref MemoryAllocator allocator, int index, int length) { 353 | 354 | this.Merge(ref allocator); 355 | var size = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf(); 356 | allocator.MemClear(this.data.arrPtr, index * size, length * size); 357 | 358 | } 359 | 360 | } 361 | 362 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemArraySliced.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c78f04efcc46221438ed7aa4df305048 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemoryAllocator.Core.cs: -------------------------------------------------------------------------------- 1 | //#define MEMORY_ALLOCATOR_BOUNDS_CHECK 2 | 3 | using System; 4 | using Unity.Collections; 5 | using Unity.Collections.LowLevel.Unsafe; 6 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 7 | using BURST = Unity.Burst.BurstCompileAttribute; 8 | 9 | namespace ME.MA { 10 | 11 | public unsafe class MemoryAllocatorProxy { 12 | 13 | public struct Dump { 14 | 15 | public string[] blocks; 16 | 17 | } 18 | 19 | private readonly MemoryAllocator allocator; 20 | 21 | public MemoryAllocatorProxy(MemoryAllocator allocator) { 22 | 23 | this.allocator = allocator; 24 | 25 | } 26 | 27 | public Dump[] dump { 28 | get { 29 | var list = new System.Collections.Generic.List(); 30 | for (int i = 0; i < this.allocator.zonesListCount; ++i) { 31 | var zone = this.allocator.zonesList[i]; 32 | var blocks = new System.Collections.Generic.List(); 33 | MemoryAllocator.ZmDumpHeap(zone, blocks); 34 | var item = new Dump() { 35 | blocks = blocks.ToArray(), 36 | }; 37 | list.Add(item); 38 | } 39 | 40 | return list.ToArray(); 41 | } 42 | } 43 | 44 | public Dump[] checks { 45 | get { 46 | var list = new System.Collections.Generic.List(); 47 | for (int i = 0; i < this.allocator.zonesListCount; ++i) { 48 | var zone = this.allocator.zonesList[i]; 49 | var blocks = new System.Collections.Generic.List(); 50 | MemoryAllocator.ZmCheckHeap(zone, blocks); 51 | var item = new Dump() { 52 | blocks = blocks.ToArray(), 53 | }; 54 | list.Add(item); 55 | } 56 | 57 | return list.ToArray(); 58 | } 59 | } 60 | 61 | } 62 | 63 | [System.Diagnostics.DebuggerTypeProxyAttribute(typeof(MemoryAllocatorProxy))] 64 | //[BURST(CompileSynchronously = true)] 65 | public unsafe partial struct MemoryAllocator { 66 | 67 | private const int ZONE_ID = 0x1d4a11; 68 | 69 | private const int MIN_FRAGMENT = 64; 70 | 71 | private const byte BLOCK_STATE_FREE = 0; 72 | private const byte BLOCK_STATE_USED = 1; 73 | 74 | 75 | public struct MemZone { 76 | 77 | public int size; // total bytes malloced, including header 78 | public MemBlock blocklist; // start / end cap for linked list 79 | public MemBlockOffset rover; 80 | 81 | } 82 | 83 | public struct MemBlock { 84 | 85 | public int size; // including the header and possibly tiny fragments 86 | public byte state; 87 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 88 | public int id; // should be ZONEID 89 | #endif 90 | public MemBlockOffset next; 91 | public MemBlockOffset prev; 92 | 93 | }; 94 | 95 | public readonly struct MemBlockOffset { 96 | 97 | public readonly long value; 98 | 99 | public MemBlockOffset(void* block, MemZone* zone) { 100 | this.value = (byte*)block - (byte*)zone; 101 | } 102 | 103 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 104 | public readonly MemBlock* Ptr(void* zone) { 105 | return (MemBlock*)((byte*)zone + this.value); 106 | } 107 | 108 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 109 | public static bool operator ==(MemBlockOffset a, MemBlockOffset b) => a.value == b.value; 110 | 111 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 112 | public static bool operator !=(MemBlockOffset a, MemBlockOffset b) => a.value != b.value; 113 | 114 | public bool Equals(MemBlockOffset other) { 115 | return this.value == other.value; 116 | } 117 | 118 | public override bool Equals(object obj) { 119 | return obj is MemBlockOffset other && Equals(other); 120 | } 121 | 122 | public override int GetHashCode() { 123 | return this.value.GetHashCode(); 124 | } 125 | 126 | } 127 | 128 | //[BURST(CompileSynchronously = true)] 129 | public static void ZmClearZone(MemZone* zone) { 130 | var block = (MemBlock*)((byte*)zone + sizeof(MemZone)); 131 | var blockOffset = new MemBlockOffset(block, zone); 132 | 133 | // set the entire zone to one free block 134 | zone->blocklist.next = zone->blocklist.prev = blockOffset; 135 | 136 | zone->blocklist.state = MemoryAllocator.BLOCK_STATE_USED; 137 | zone->rover = blockOffset; 138 | 139 | block->prev = block->next = new MemBlockOffset(&zone->blocklist, zone); 140 | 141 | block->state = MemoryAllocator.BLOCK_STATE_FREE; 142 | 143 | block->size = zone->size - TSize.size; 144 | } 145 | 146 | //[BURST(CompileSynchronously = true)] 147 | public static MemZone* ZmCreateZone(int size) { 148 | 149 | size = MemoryAllocator.ZmGetMemBlockSize(size) + TSize.size; 150 | var zone = (MemZone*)UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf(), Allocator.Persistent); 151 | UnsafeUtility.MemSet(zone, 0, size); 152 | zone->size = size; 153 | MemoryAllocator.ZmClearZone(zone); 154 | 155 | return zone; 156 | } 157 | 158 | //[BURST(CompileSynchronously = true)] 159 | public static MemZone* ZmReallocZone(MemZone* zone, int newSize) { 160 | if (zone->size >= newSize) return zone; 161 | 162 | var newZone = MemoryAllocator.ZmCreateZone(newSize); 163 | var extra = newZone->size - zone->size; 164 | 165 | UnsafeUtility.MemCpy(newZone, zone, zone->size); 166 | 167 | newZone->size = zone->size + extra; 168 | 169 | var top = newZone->rover.Ptr(newZone); 170 | 171 | for (var block = newZone->blocklist.next.Ptr(newZone); block != &newZone->blocklist; block = block->next.Ptr(newZone)) { 172 | if (block > top) { 173 | top = block; 174 | } 175 | } 176 | 177 | if (top->state == MemoryAllocator.BLOCK_STATE_FREE) { 178 | top->size += extra; 179 | } else { 180 | var newblock = (MemBlock*)((byte*)top + top->size); 181 | var newblockOffset = new MemBlockOffset(newblock, newZone); 182 | newblock->size = extra; 183 | 184 | newblock->state = MemoryAllocator.BLOCK_STATE_FREE; 185 | newblock->prev = new MemBlockOffset(top, newZone); 186 | newblock->next = top->next; 187 | newblock->next.Ptr(newZone)->prev = newblockOffset; 188 | 189 | top->next = newblockOffset; 190 | newZone->rover = newblockOffset; 191 | } 192 | 193 | MemoryAllocator.ZmFreeZone(zone); 194 | 195 | return newZone; 196 | } 197 | 198 | //[BURST(CompileSynchronously = true)] 199 | public static void ZmFreeZone(MemZone* zone) { 200 | UnsafeUtility.Free(zone, Allocator.Persistent); 201 | } 202 | 203 | //[BURST(CompileSynchronously = true)] 204 | public static bool ZmFree(MemZone* zone, void* ptr) { 205 | var block = (MemBlock*)((byte*)ptr - TSize.size); 206 | var blockOffset = new MemBlockOffset(block, zone); 207 | 208 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 209 | if (block->id != MemoryAllocator.ZONE_ID) { 210 | //return false; 211 | throw new System.ArgumentException("ZmFree: freed a pointer without ZONEID"); 212 | } 213 | #endif 214 | 215 | // mark as free 216 | block->state = MemoryAllocator.BLOCK_STATE_FREE; 217 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 218 | block->id = 0; 219 | #endif 220 | 221 | var other = block->prev.Ptr(zone); 222 | var otherOffset = block->prev; 223 | 224 | if (other->state == MemoryAllocator.BLOCK_STATE_FREE) { 225 | // merge with previous free block 226 | other->size += block->size; 227 | other->next = block->next; 228 | other->next.Ptr(zone)->prev = otherOffset; 229 | 230 | if (blockOffset == zone->rover) zone->rover = otherOffset; 231 | 232 | block = other; 233 | blockOffset = otherOffset; 234 | } 235 | 236 | other = block->next.Ptr(zone); 237 | otherOffset = block->next; 238 | if (other->state == MemoryAllocator.BLOCK_STATE_FREE) { 239 | // merge the next free block onto the end 240 | block->size += other->size; 241 | block->next = other->next; 242 | block->next.Ptr(zone)->prev = blockOffset; 243 | 244 | if (otherOffset == zone->rover) zone->rover = blockOffset; 245 | } 246 | 247 | return true; 248 | } 249 | 250 | //[BURST(CompileSynchronously = true)] 251 | private static int ZmGetMemBlockSize(int size) { 252 | return ((size + 3) & ~3) + TSize.size; 253 | } 254 | 255 | //[BURST(CompileSynchronously = true)] 256 | public static void* ZmMalloc(MemZone* zone, int size) { 257 | size = MemoryAllocator.ZmGetMemBlockSize(size); 258 | 259 | // scan through the block list, 260 | // looking for the first free block 261 | // of sufficient size, 262 | // throwing out any purgable blocks along the way. 263 | 264 | // if there is a free block behind the rover, 265 | // back up over them 266 | var @base = zone->rover.Ptr(zone); 267 | 268 | if (@base->prev.Ptr(zone)->state != MemoryAllocator.BLOCK_STATE_FREE) @base = @base->prev.Ptr(zone); 269 | 270 | var rover = @base; 271 | var start = @base->prev.Ptr(zone); 272 | 273 | do { 274 | if (rover == start) { 275 | // scanned all the way around the list 276 | return null; 277 | //throw new System.OutOfMemoryException($"Malloc: failed on allocation of {size} bytes"); 278 | } 279 | 280 | if (rover->state != MemoryAllocator.BLOCK_STATE_FREE) { 281 | // hit a block that can't be purged, 282 | // so move base past it 283 | @base = rover = rover->next.Ptr(zone); 284 | } else 285 | rover = rover->next.Ptr(zone); 286 | } while (@base->state != MemoryAllocator.BLOCK_STATE_FREE || @base->size < size); 287 | 288 | 289 | // found a block big enough 290 | var extra = @base->size - size; 291 | 292 | if (extra > MemoryAllocator.MIN_FRAGMENT) { 293 | // there will be a free fragment after the allocated block 294 | var newblock = (MemBlock*)((byte*)@base + size); 295 | var newblockOffset = new MemBlockOffset(newblock, zone); 296 | newblock->size = extra; 297 | 298 | // NULL indicates free block. 299 | newblock->state = MemoryAllocator.BLOCK_STATE_FREE; 300 | newblock->prev = new MemBlockOffset(@base, zone); 301 | newblock->next = @base->next; 302 | newblock->next.Ptr(zone)->prev = newblockOffset; 303 | 304 | @base->next = newblockOffset; 305 | @base->size = size; 306 | 307 | } 308 | 309 | @base->state = MemoryAllocator.BLOCK_STATE_USED; 310 | 311 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 312 | @base->id = MemoryAllocator.ZONE_ID; 313 | #endif 314 | 315 | // next allocation will start looking here 316 | zone->rover = @base->next; 317 | 318 | return (void*)((byte*)@base + TSize.size); 319 | } 320 | 321 | [INLINE(256)] 322 | public static void ZmDumpHeap(MemZone* zone, System.Collections.Generic.List results) { 323 | results.Add($"zone size: {zone->size}; location: {new IntPtr(zone)}; rover block offset: {zone->rover.value}"); 324 | 325 | for (var block = zone->blocklist.next.Ptr(zone);; block = block->next.Ptr(zone)) { 326 | 327 | results.Add($"block offset: {(byte*)block - (byte*)@zone}; size: {block->size}; state: {block->state}"); 328 | 329 | if (block->next.Ptr(zone) == &zone->blocklist) break; 330 | 331 | MemoryAllocator.ZmCheckBlock(zone, block, results); 332 | } 333 | } 334 | 335 | [INLINE(256)] 336 | public static void ZmCheckHeap(MemZone* zone, System.Collections.Generic.List results) { 337 | for (var block = zone->blocklist.next.Ptr(zone);; block = block->next.Ptr(zone)) { 338 | if (block->next.Ptr(zone) == &zone->blocklist) { 339 | // all blocks have been hit 340 | break; 341 | } 342 | 343 | MemoryAllocator.ZmCheckBlock(zone, block, results); 344 | } 345 | } 346 | 347 | [INLINE(256)] 348 | private static void ZmCheckBlock(MemZone* zone, MemBlock* block, System.Collections.Generic.List results) { 349 | if ((byte*)block + block->size != (byte*)block->next.Ptr(zone)) { 350 | results.Add("CheckHeap: block size does not touch the next block\n"); 351 | } 352 | 353 | if (block->next.Ptr(zone)->prev.Ptr(zone) != block) { 354 | results.Add("CheckHeap: next block doesn't have proper back link\n"); 355 | } 356 | 357 | if (block->state == MemoryAllocator.BLOCK_STATE_FREE && block->next.Ptr(zone)->state == MemoryAllocator.BLOCK_STATE_FREE) { 358 | results.Add("CheckHeap: two consecutive free blocks\n"); 359 | } 360 | } 361 | 362 | //[BURST(CompileSynchronously = true)] 363 | public static int GetZmFreeMemory(MemZone* zone) { 364 | var free = 0; 365 | 366 | for (var block = zone->blocklist.next.Ptr(zone); block != &zone->blocklist; block = block->next.Ptr(zone)) { 367 | if (block->state == MemoryAllocator.BLOCK_STATE_FREE) free += block->size; 368 | } 369 | 370 | return free; 371 | } 372 | 373 | //[BURST(CompileSynchronously = true)] 374 | public static bool ZmHasFreeBlock(MemZone* zone, int size) { 375 | size = MemoryAllocator.ZmGetMemBlockSize(size); 376 | 377 | for (var block = zone->blocklist.next.Ptr(zone); block != &zone->blocklist; block = block->next.Ptr(zone)) { 378 | if (block->state == MemoryAllocator.BLOCK_STATE_FREE && block->size > size) { 379 | return true; 380 | } 381 | 382 | } 383 | 384 | return false; 385 | } 386 | 387 | } 388 | 389 | } 390 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemoryAllocator.Core.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d5198f658349d084a8a906b2fd24b4a2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemoryAllocator.cs: -------------------------------------------------------------------------------- 1 | //#define MEMORY_ALLOCATOR_BOUNDS_CHECK 2 | //#define LOGS_ENABLED 3 | 4 | using System; 5 | using Unity.Collections; 6 | using Unity.Collections.LowLevel.Unsafe; 7 | using INLINE = System.Runtime.CompilerServices.MethodImplAttribute; 8 | using BURST = Unity.Burst.BurstCompileAttribute; 9 | 10 | namespace ME.MA { 11 | 12 | using MemPtr = System.Int64; 13 | 14 | public struct TSize where T : struct { 15 | 16 | public static readonly int size = UnsafeUtility.SizeOf(); 17 | 18 | } 19 | 20 | public struct TAlign where T : struct { 21 | 22 | public static readonly int align = UnsafeUtility.AlignOf(); 23 | 24 | } 25 | 26 | #if !LOGS_ENABLED && !MEMORY_ALLOCATOR_BOUNDS_CHECK 27 | //[BURST(CompileSynchronously = true)] 28 | #endif 29 | public static unsafe class MemoryAllocatorExt { 30 | 31 | #if !LOGS_ENABLED && !MEMORY_ALLOCATOR_BOUNDS_CHECK 32 | //[BURST(CompileSynchronously = true)] 33 | #endif 34 | public static MemPtr Alloc(this ref MemoryAllocator allocator, long size) { 35 | 36 | void* ptr = null; 37 | 38 | for (int i = 0; i < allocator.zonesListCount; i++) { 39 | ptr = MemoryAllocator.ZmMalloc(allocator.zonesList[i], (int)size); 40 | 41 | if (ptr != null) { 42 | var memPtr = allocator.GetSafePtr(ptr, i); 43 | #if LOGS_ENABLED 44 | MemoryAllocator.LogAdd(memPtr, size); 45 | #endif 46 | return memPtr; 47 | } 48 | } 49 | 50 | { 51 | var zone = MemoryAllocator.ZmCreateZone((int)Math.Max(size, MemoryAllocator.MIN_ZONE_SIZE)); 52 | var zoneIndex = allocator.AddZone(zone); 53 | 54 | ptr = MemoryAllocator.ZmMalloc(zone, (int)size); 55 | 56 | var memPtr = allocator.GetSafePtr(ptr, zoneIndex); 57 | #if LOGS_ENABLED 58 | MemoryAllocator.LogAdd(memPtr, size); 59 | #endif 60 | return memPtr; 61 | } 62 | 63 | } 64 | 65 | #if !LOGS_ENABLED && !MEMORY_ALLOCATOR_BOUNDS_CHECK 66 | //[BURST(CompileSynchronously = true)] 67 | #endif 68 | public static bool Free(this ref MemoryAllocator allocator, MemPtr ptr) { 69 | 70 | if (ptr == 0) return false; 71 | 72 | var zoneIndex = ptr >> 32; 73 | 74 | #if LOGS_ENABLED 75 | if (startLog == true) { 76 | MemoryAllocator.LogRemove(ptr); 77 | } 78 | #endif 79 | 80 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 81 | if (zoneIndex >= this.zonesListCount || this.zonesList[zoneIndex]->size < (ptr & MemoryAllocator.OFFSET_MASK)) { 82 | throw new OutOfBoundsException(); 83 | } 84 | #endif 85 | 86 | return MemoryAllocator.ZmFree(allocator.zonesList[zoneIndex], allocator.GetUnsafePtr(ptr)); 87 | } 88 | 89 | } 90 | 91 | public unsafe partial struct MemoryAllocator : IDisposable { 92 | 93 | #if LOGS_ENABLED && UNITY_EDITOR 94 | [Unity.Burst.BurstDiscardAttribute] 95 | public static void LogAdd(MemPtr memPtr, long size) { 96 | if (startLog == true) { 97 | var str = "ALLOC: " + memPtr + ", SIZE: " + size; 98 | strList.Add(memPtr, str + "\n" + UnityEngine.StackTraceUtility.ExtractStackTrace()); 99 | } 100 | } 101 | 102 | [Unity.Burst.BurstDiscardAttribute] 103 | public static void LogRemove(MemPtr memPtr) { 104 | strList.Remove(memPtr); 105 | } 106 | 107 | public static bool startLog; 108 | public static System.Collections.Generic.Dictionary strList = new System.Collections.Generic.Dictionary(); 109 | [UnityEditor.MenuItem("ME.ECS/Debug/Allocator: Start Log")] 110 | public static void StartLog() { 111 | startLog = true; 112 | } 113 | 114 | [UnityEditor.MenuItem("ME.ECS/Debug/Allocator: End Log")] 115 | public static void EndLog() { 116 | startLog = false; 117 | MemoryAllocator.strList.Clear(); 118 | } 119 | 120 | [UnityEditor.MenuItem("ME.ECS/Debug/Allocator: Print Log")] 121 | public static void PrintLog() { 122 | foreach (var item in MemoryAllocator.strList) { 123 | UnityEngine.Debug.Log(item.Key + "\n" + item.Value); 124 | } 125 | } 126 | #endif 127 | 128 | private const long OFFSET_MASK = 0xFFFFFFFF; 129 | internal const long MIN_ZONE_SIZE = 512 * 1024; 130 | private const int MIN_ZONES_LIST_CAPACITY = 20; 131 | 132 | [NativeDisableUnsafePtrRestriction] 133 | internal MemZone** zonesList; 134 | internal int zonesListCount; 135 | internal int zonesListCapacity; 136 | internal long maxSize; 137 | 138 | public bool isValid => this.zonesList != null; 139 | 140 | public int GetReservedSize() { 141 | 142 | var size = 0; 143 | for (int i = 0; i < this.zonesListCount; i++) { 144 | var zone = this.zonesList[i]; 145 | size += zone->size; 146 | } 147 | 148 | return size; 149 | 150 | } 151 | 152 | public int GetUsedSize() { 153 | 154 | var size = 0; 155 | for (int i = 0; i < this.zonesListCount; i++) { 156 | var zone = this.zonesList[i]; 157 | size += zone->size; 158 | size -= MemoryAllocator.GetZmFreeMemory(zone); 159 | } 160 | 161 | return size; 162 | 163 | } 164 | 165 | public int GetFreeSize() { 166 | 167 | var size = 0; 168 | for (int i = 0; i < this.zonesListCount; i++) { 169 | var zone = this.zonesList[i]; 170 | size += MemoryAllocator.GetZmFreeMemory(zone); 171 | } 172 | 173 | return size; 174 | 175 | } 176 | 177 | /// 178 | /// Constructors 179 | /// 180 | public MemoryAllocator Initialize(long initialSize, long maxSize = -1L) { 181 | 182 | if (maxSize < initialSize) maxSize = initialSize; 183 | 184 | this.AddZone(MemoryAllocator.ZmCreateZone((int)Math.Max(initialSize, MemoryAllocator.MIN_ZONE_SIZE))); 185 | 186 | this.maxSize = maxSize; 187 | 188 | return this; 189 | } 190 | 191 | public void Dispose() { 192 | 193 | this.FreeZones(); 194 | 195 | if (this.zonesList != null) { 196 | UnsafeUtility.Free(this.zonesList, Allocator.Persistent); 197 | this.zonesList = null; 198 | } 199 | 200 | this.zonesListCapacity = 0; 201 | this.maxSize = default; 202 | 203 | } 204 | 205 | public void CopyFrom(in MemoryAllocator other) { 206 | 207 | if (other.zonesList == null && this.zonesList == null) { 208 | 209 | } else if (other.zonesList == null && this.zonesList != null) { 210 | this.FreeZones(); 211 | } else { 212 | 213 | var areEquals = true; 214 | if (this.zonesListCount == other.zonesListCount) { 215 | 216 | for (int i = 0; i < other.zonesListCount; ++i) { 217 | ref var curZone = ref this.zonesList[i]; 218 | var otherZone = other.zonesList[i]; 219 | { 220 | // resize zone 221 | curZone = MemoryAllocator.ZmReallocZone(curZone, otherZone->size); 222 | UnsafeUtility.MemCpy(curZone, otherZone, otherZone->size); 223 | } 224 | } 225 | 226 | } else { 227 | areEquals = false; 228 | } 229 | 230 | if (areEquals == false) { 231 | 232 | this.FreeZones(); 233 | 234 | for (int i = 0; i < other.zonesListCount; i++) { 235 | var otherZone = other.zonesList[i]; 236 | var zone = MemoryAllocator.ZmCreateZone(otherZone->size); 237 | UnsafeUtility.MemCpy(zone, otherZone, otherZone->size); 238 | this.AddZone(zone); 239 | } 240 | 241 | } 242 | 243 | } 244 | 245 | this.maxSize = other.maxSize; 246 | 247 | } 248 | 249 | private void FreeZones() { 250 | if (this.zonesListCount > 0 && this.zonesList != null) { 251 | for (int i = 0; i < this.zonesListCount; i++) { 252 | MemoryAllocator.ZmFreeZone(this.zonesList[i]); 253 | } 254 | } 255 | 256 | this.zonesListCount = 0; 257 | } 258 | 259 | internal int AddZone(MemZone* zone) { 260 | 261 | if (this.zonesListCapacity <= this.zonesListCount) { 262 | var capacity = Math.Max(MemoryAllocator.MIN_ZONES_LIST_CAPACITY, this.zonesListCapacity * 2); 263 | var list = (MemZone**)UnsafeUtility.Malloc(capacity * sizeof(MemZone*), UnsafeUtility.AlignOf(), Allocator.Persistent); 264 | 265 | if (this.zonesList != null) { 266 | for (int i = 0; i < this.zonesListCount; i++) { 267 | list[i] = this.zonesList[i]; 268 | } 269 | 270 | UnsafeUtility.Free(this.zonesList, Allocator.Persistent); 271 | } 272 | 273 | this.zonesList = list; 274 | this.zonesListCapacity = capacity; 275 | } 276 | 277 | this.zonesList[this.zonesListCount++] = zone; 278 | 279 | return this.zonesListCount - 1; 280 | } 281 | 282 | /// 283 | /// Base 284 | /// 285 | 286 | [INLINE(256)] 287 | public readonly ref T Ref(MemPtr ptr) where T : struct { 288 | return ref UnsafeUtility.AsRef(this.GetUnsafePtr(ptr)); 289 | } 290 | 291 | [INLINE(256)] 292 | public MemPtr AllocData(T data) where T : struct { 293 | var ptr = this.Alloc(); 294 | this.Ref(ptr) = data; 295 | return ptr; 296 | } 297 | 298 | [INLINE(256)] 299 | public MemPtr Alloc() where T : struct { 300 | var size = TSize.size; 301 | var alignOf = TAlign.align; 302 | return this.Alloc(size + alignOf); 303 | } 304 | 305 | [INLINE(256)] 306 | public MemPtr ReAlloc(MemPtr ptr, long size) { 307 | 308 | if (ptr == 0L) return this.Alloc(size); 309 | 310 | var blockSize = ((MemBlock*)((byte*)this.GetUnsafePtr(ptr) - TSize.size))->size; 311 | var blockDataSize = blockSize - TSize.size; 312 | if (blockDataSize > size) { 313 | return ptr; 314 | } 315 | 316 | if (blockDataSize < 0) { 317 | throw new Exception(); 318 | } 319 | 320 | var newPtr = this.Alloc(size); 321 | this.MemMove(newPtr, 0, ptr, 0, blockDataSize); 322 | this.Free(ptr); 323 | 324 | return newPtr; 325 | 326 | } 327 | 328 | [INLINE(256)] 329 | public readonly void MemCopy(MemPtr dest, long destOffset, MemPtr source, long sourceOffset, long length) { 330 | 331 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 332 | var destZoneIndex = dest >> 32; 333 | var sourceZoneIndex = source >> 32; 334 | var destMaxOffset = (dest & MemoryAllocator.OFFSET_MASK) + destOffset + length; 335 | var sourceMaxOffset = (source & MemoryAllocator.OFFSET_MASK) + sourceOffset + length; 336 | 337 | if (destZoneIndex >= this.zonesListCount || sourceZoneIndex >= this.zonesListCount) { 338 | throw new OutOfBoundsException(); 339 | } 340 | 341 | if (this.zonesList[destZoneIndex]->size < destMaxOffset || this.zonesList[sourceZoneIndex]->size < sourceMaxOffset) { 342 | throw new OutOfBoundsException(); 343 | } 344 | #endif 345 | 346 | UnsafeUtility.MemCpy(this.GetUnsafePtr(dest + destOffset), this.GetUnsafePtr(source + sourceOffset), length); 347 | 348 | } 349 | 350 | [INLINE(256)] 351 | public readonly void MemMove(MemPtr dest, long destOffset, MemPtr source, long sourceOffset, long length) { 352 | 353 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 354 | var destZoneIndex = dest >> 32; 355 | var sourceZoneIndex = source >> 32; 356 | var destMaxOffset = (dest & MemoryAllocator.OFFSET_MASK) + destOffset + length; 357 | var sourceMaxOffset = (source & MemoryAllocator.OFFSET_MASK) + sourceOffset + length; 358 | 359 | if (destZoneIndex >= this.zonesListCount || sourceZoneIndex >= this.zonesListCount) { 360 | throw new OutOfBoundsException(); 361 | } 362 | 363 | if (this.zonesList[destZoneIndex]->size < destMaxOffset || this.zonesList[sourceZoneIndex]->size < sourceMaxOffset) { 364 | throw new OutOfBoundsException(); 365 | } 366 | #endif 367 | 368 | UnsafeUtility.MemMove(this.GetUnsafePtr(dest + destOffset), this.GetUnsafePtr(source + sourceOffset), length); 369 | 370 | } 371 | 372 | [INLINE(256)] 373 | public readonly void MemClear(MemPtr dest, long destOffset, long length) { 374 | 375 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 376 | var zoneIndex = dest >> 32; 377 | 378 | if (zoneIndex >= this.zonesListCount || this.zonesList[zoneIndex]->size < ((dest & MemoryAllocator.OFFSET_MASK) + destOffset + length)) { 379 | throw new OutOfBoundsException(); 380 | } 381 | #endif 382 | 383 | UnsafeUtility.MemClear(this.GetUnsafePtr(dest + destOffset), length); 384 | } 385 | 386 | [INLINE(256)] 387 | public void Prepare(long size) { 388 | 389 | for (int i = 0; i < this.zonesListCount; i++) { 390 | if (MemoryAllocator.ZmHasFreeBlock(this.zonesList[i], (int)size) == true) { 391 | return; 392 | } 393 | } 394 | 395 | this.AddZone(MemoryAllocator.ZmCreateZone((int)Math.Max(size, MemoryAllocator.MIN_ZONE_SIZE))); 396 | 397 | } 398 | 399 | [INLINE(256)] 400 | public readonly void* GetUnsafePtr(in MemPtr ptr) { 401 | 402 | var zoneIndex = ptr >> 32; 403 | var offset = (ptr & MemoryAllocator.OFFSET_MASK); 404 | 405 | #if MEMORY_ALLOCATOR_BOUNDS_CHECK 406 | if (zoneIndex < this.zonesListCount && this.zonesList[zoneIndex]->size < offset) { 407 | throw new OutOfBoundsException(); 408 | } 409 | #endif 410 | 411 | return (byte*)this.zonesList[zoneIndex] + offset; 412 | } 413 | 414 | [INLINE(256)] 415 | internal readonly MemPtr GetSafePtr(void* ptr, int zoneIndex) { 416 | var index = (long)zoneIndex << 32; 417 | var offset = ((byte*)ptr - (byte*)this.zonesList[zoneIndex]); 418 | 419 | return index | offset; 420 | } 421 | 422 | /// 423 | /// Arrays 424 | /// 425 | [INLINE(256)] 426 | public readonly ref T RefArray(MemPtr ptr, int index) where T : struct { 427 | var size = TSize.size; 428 | return ref UnsafeUtility.AsRef(this.GetUnsafePtr(ptr + index * size)); 429 | } 430 | 431 | [INLINE(256)] 432 | public MemPtr ReAllocArray(MemPtr ptr, int newLength) where T : struct { 433 | var size = TSize.size; 434 | return this.ReAlloc(ptr, size * newLength); 435 | } 436 | 437 | [INLINE(256)] 438 | public MemPtr ReAllocArray(int sizeOf, MemPtr ptr, int newLength) { 439 | var size = sizeOf; 440 | return this.ReAlloc(ptr, size * newLength); 441 | } 442 | 443 | [INLINE(256)] 444 | public MemPtr AllocArray(int length) where T : struct { 445 | var size = TSize.size; 446 | return this.Alloc(size * length); 447 | } 448 | 449 | [INLINE(256)] 450 | public MemPtr AllocArray(int length, int sizeOf) { 451 | var size = sizeOf; 452 | return this.Alloc(size * length); 453 | } 454 | 455 | } 456 | 457 | } 458 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/MemoryAllocator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a158cc0066b34ba98304c8945333e981 3 | timeCreated: 1657124593 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/NativeArrayUtils.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | public unsafe class NativeArrayUtils { 4 | 5 | #if INLINE_METHODS 6 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 7 | #endif 8 | public static void Copy(in MemoryAllocator allocator, 9 | in T[] src, 10 | int srcIndex, 11 | ref MemArrayAllocator dst, 12 | int dstIndex, 13 | int length) where T : unmanaged { 14 | 15 | var gcHandle = System.Runtime.InteropServices.GCHandle.Alloc(src, System.Runtime.InteropServices.GCHandleType.Pinned); 16 | var num = gcHandle.AddrOfPinnedObject(); 17 | Unity.Collections.LowLevel.Unsafe.UnsafeUtility.MemCpy((void*)((System.IntPtr)dst.arrPtr + dstIndex * Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf()), (void*)((System.IntPtr)(void*)num + srcIndex * Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf()), (long)(length * Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf())); 18 | gcHandle.Free(); 19 | 20 | } 21 | 22 | #if INLINE_METHODS 23 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 24 | #endif 25 | public static void Copy(ref MemoryAllocator allocator, 26 | in MemArrayAllocator fromArr, 27 | ref MemArrayAllocator arr) where T : unmanaged { 28 | 29 | NativeArrayUtils.Copy(ref allocator, fromArr, 0, ref arr, 0, fromArr.Length); 30 | 31 | } 32 | 33 | #if INLINE_METHODS 34 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 35 | #endif 36 | public static void CopyExact(ref MemoryAllocator allocator, 37 | in MemArrayAllocator fromArr, 38 | ref MemArrayAllocator arr) where T : unmanaged { 39 | 40 | NativeArrayUtils.Copy(ref allocator, fromArr, 0, ref arr, 0, fromArr.Length, true); 41 | 42 | } 43 | 44 | #if INLINE_METHODS 45 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 46 | #endif 47 | public static void Copy(ref MemoryAllocator allocator, 48 | in MemArrayAllocator fromArr, 49 | int sourceIndex, 50 | ref MemArrayAllocator arr, 51 | int destIndex, 52 | int length, 53 | bool copyExact = false) where T : unmanaged { 54 | 55 | switch (fromArr.isCreated) { 56 | case false when arr.isCreated == false: 57 | return; 58 | 59 | case false when arr.isCreated == true: 60 | arr.Dispose(ref allocator); 61 | arr = default; 62 | return; 63 | } 64 | 65 | if (arr.isCreated == false || (copyExact == false ? arr.Length < fromArr.Length : arr.Length != fromArr.Length)) { 66 | 67 | if (arr.isCreated == true) arr.Dispose(ref allocator); 68 | arr = new MemArrayAllocator(ref allocator, fromArr.Length); 69 | 70 | } 71 | 72 | var size = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.SizeOf(); 73 | allocator.MemMove(arr.arrPtr, destIndex * size, fromArr.arrPtr, sourceIndex * size, length * size); 74 | 75 | } 76 | 77 | #if INLINE_METHODS 78 | [System.Runtime.CompilerServices.MethodImplAttribute(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] 79 | #endif 80 | public static void CopyNoChecks(ref MemoryAllocator allocator, 81 | in MemArrayAllocator fromArr, 82 | int sourceIndex, 83 | ref MemArrayAllocator arr, 84 | int destIndex, 85 | int length) where T : unmanaged { 86 | 87 | var size = sizeof(T); 88 | allocator.MemCopy(arr.arrPtr, destIndex * size, fromArr.arrPtr, sourceIndex * size, length * size); 89 | 90 | } 91 | 92 | } 93 | 94 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/NativeArrayUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2270d50f7fbc4107935ea260ce8dc83f 3 | timeCreated: 1658750562 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/StaticAllocators.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | #if UNITY_EDITOR 4 | [UnityEditor.InitializeOnLoadAttribute] 5 | #endif 6 | public static class StaticAllocatorInitializer { 7 | 8 | private static readonly Destructor finalize = new Destructor(); 9 | 10 | static StaticAllocatorInitializer() { 11 | 12 | // 4 MB of persistent memory + no max size 13 | StaticAllocators.persistent.Data = new MemoryAllocator().Initialize(4 * 1024 * 1024, -1); 14 | 15 | // 256 KB of temp memory + max size = 256 KB 16 | StaticAllocators.temp.Data = new MemoryAllocator().Initialize(256 * 1024, 256 * 1024); 17 | 18 | } 19 | 20 | private sealed class Destructor { 21 | 22 | ~Destructor() { 23 | StaticAllocators.persistent.Data.Dispose(); 24 | StaticAllocators.temp.Data.Dispose(); 25 | } 26 | 27 | } 28 | 29 | } 30 | 31 | public struct StaticAllocators { 32 | 33 | internal static readonly Unity.Burst.SharedStatic persistent = Unity.Burst.SharedStatic.GetOrCreate(); 34 | internal static readonly Unity.Burst.SharedStatic temp = Unity.Burst.SharedStatic.GetOrCreate(); 35 | 36 | public static ref MemoryAllocator GetAllocator(AllocatorType type) { 37 | 38 | switch (type) { 39 | case AllocatorType.Persistent: 40 | return ref StaticAllocators.persistent.Data; 41 | 42 | case AllocatorType.Temp: 43 | return ref StaticAllocators.temp.Data; 44 | } 45 | 46 | throw new System.Exception($"Allocator type {type} is unknown"); 47 | 48 | } 49 | 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Core/StaticAllocators.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f4dc40f83dac4fd9ade548edf10cd89c 3 | timeCreated: 1657572907 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Debug.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0be41243d6894a16b4115a5dabebd3c4 3 | timeCreated: 1659116510 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Debug/Proxy.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | using Collections; 4 | 5 | public static class StaticAllocatorProxy { 6 | 7 | public static MemoryAllocator defaultAllocator; 8 | public static ref MemoryAllocator allocator { 9 | get { 10 | return ref StaticAllocatorProxy.defaultAllocator; 11 | } 12 | 13 | } 14 | 15 | } 16 | 17 | public class MemArrayAllocatorProxy where T : unmanaged { 18 | 19 | private MemArrayAllocator arr; 20 | 21 | public MemArrayAllocatorProxy(MemArrayAllocator arr) { 22 | 23 | this.arr = arr; 24 | 25 | } 26 | 27 | public T[] items { 28 | get { 29 | var arr = new T[this.arr.Length]; 30 | for (int i = 0; i < this.arr.Length; ++i) { 31 | arr[i] = this.arr[in StaticAllocatorProxy.allocator, i]; 32 | } 33 | 34 | return arr; 35 | } 36 | } 37 | 38 | } 39 | 40 | public class ListProxy where T : unmanaged { 41 | 42 | private List arr; 43 | 44 | public ListProxy(List arr) { 45 | 46 | this.arr = arr; 47 | 48 | } 49 | 50 | public int Capacity { 51 | get { 52 | if (StaticAllocatorProxy.allocator.isValid == false) return 0; 53 | return this.arr.Capacity(StaticAllocatorProxy.allocator); 54 | } 55 | } 56 | 57 | public int Count { 58 | get { 59 | if (StaticAllocatorProxy.allocator.isValid == false) return 0; 60 | return this.arr.Count; 61 | } 62 | } 63 | 64 | public T[] items { 65 | get { 66 | if (StaticAllocatorProxy.allocator.isValid == false) return null; 67 | var arr = new T[this.arr.Count]; 68 | for (int i = 0; i < this.arr.Count; ++i) { 69 | arr[i] = this.arr[in StaticAllocatorProxy.allocator, i]; 70 | } 71 | 72 | return arr; 73 | } 74 | } 75 | 76 | } 77 | 78 | public class HashSetProxy where T : unmanaged { 79 | 80 | private HashSet arr; 81 | 82 | public HashSetProxy(HashSet arr) { 83 | 84 | this.arr = arr; 85 | 86 | } 87 | 88 | public int Count { 89 | get { 90 | if (StaticAllocatorProxy.allocator.isValid == false) return 0; 91 | return this.arr.Count; 92 | } 93 | } 94 | 95 | public MemArrayAllocator buckets => this.arr.buckets; 96 | public MemArrayAllocator.Slot> slots => this.arr.slots; 97 | public int count => this.arr.count; 98 | public int version => this.arr.version; 99 | public int freeList => this.arr.freeList; 100 | public int lastIndex => this.arr.lastIndex; 101 | 102 | public T[] items { 103 | get { 104 | if (StaticAllocatorProxy.allocator.isValid == false) return null; 105 | var arr = new T[this.arr.Count]; 106 | var i = 0; 107 | var e = this.arr.GetEnumerator(in StaticAllocatorProxy.allocator); 108 | while (e.MoveNext() == true) { 109 | arr[i++] = e.Current; 110 | } 111 | e.Dispose(); 112 | 113 | return arr; 114 | } 115 | } 116 | 117 | } 118 | 119 | public class DictionaryProxy where K : unmanaged where V : unmanaged { 120 | 121 | private Dictionary arr; 122 | 123 | public DictionaryProxy(Dictionary arr) { 124 | 125 | this.arr = arr; 126 | 127 | } 128 | 129 | public int Count { 130 | get { 131 | if (StaticAllocatorProxy.allocator.isValid == false) return 0; 132 | return this.arr.Count; 133 | } 134 | } 135 | 136 | public MemArrayAllocator buckets => this.arr.buckets; 137 | public MemArrayAllocator.Entry> entries => this.arr.entries; 138 | public int count => this.arr.count; 139 | public int version => this.arr.version; 140 | public int freeList => this.arr.freeList; 141 | public int freeCount => this.arr.freeCount; 142 | 143 | public System.Collections.Generic.KeyValuePair[] items { 144 | get { 145 | if (StaticAllocatorProxy.allocator.isValid == false) return null; 146 | var arr = new System.Collections.Generic.KeyValuePair[this.arr.Count]; 147 | var i = 0; 148 | var e = this.arr.GetEnumerator(in StaticAllocatorProxy.allocator); 149 | while (e.MoveNext() == true) { 150 | arr[i++] = e.Current; 151 | } 152 | e.Dispose(); 153 | 154 | return arr; 155 | } 156 | } 157 | 158 | } 159 | 160 | public class StackProxy where T : unmanaged { 161 | 162 | private Stack arr; 163 | 164 | public StackProxy(Stack arr) { 165 | 166 | this.arr = arr; 167 | 168 | } 169 | 170 | public int Count { 171 | get { 172 | if (StaticAllocatorProxy.allocator.isValid == false) return 0; 173 | return this.arr.Count; 174 | } 175 | } 176 | 177 | public T[] items { 178 | get { 179 | if (StaticAllocatorProxy.allocator.isValid == false) return null; 180 | var arr = new T[this.arr.Count]; 181 | var i = 0; 182 | var e = this.arr.GetEnumerator(in StaticAllocatorProxy.allocator); 183 | while (e.MoveNext() == true) { 184 | arr[i++] = e.Current; 185 | } 186 | e.Dispose(); 187 | 188 | return arr; 189 | } 190 | } 191 | 192 | } 193 | 194 | public class QueueProxy where T : unmanaged { 195 | 196 | private Queue arr; 197 | 198 | public QueueProxy(Queue arr) { 199 | 200 | this.arr = arr; 201 | 202 | } 203 | 204 | public int Count { 205 | get { 206 | if (StaticAllocatorProxy.allocator.isValid == false) return 0; 207 | return this.arr.Count; 208 | } 209 | } 210 | 211 | public T[] items { 212 | get { 213 | if (StaticAllocatorProxy.allocator.isValid == false) return null; 214 | var arr = new T[this.arr.Count]; 215 | var i = 0; 216 | var e = this.arr.GetEnumerator(in StaticAllocatorProxy.allocator); 217 | while (e.MoveNext() == true) { 218 | arr[i++] = e.Current; 219 | } 220 | e.Dispose(); 221 | 222 | return arr; 223 | } 224 | } 225 | 226 | } 227 | 228 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Debug/Proxy.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a73859f8915f43969a2b5035a0a8ba88 3 | timeCreated: 1659116520 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Exceptions.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | public class OutOfBoundsException : System.Exception { 4 | 5 | public OutOfBoundsException() : base("ME.MA Exception") { } 6 | public OutOfBoundsException(string message) : base(message) { } 7 | 8 | } 9 | 10 | public class CollectionNotCreated : System.Exception { 11 | 12 | public CollectionNotCreated() : base("ME.MA Exception") { } 13 | public CollectionNotCreated(string message) : base(message) { } 14 | 15 | } 16 | 17 | public static class E { 18 | 19 | public static void RANGE(int index, int lowBound, int highBound) { 20 | 21 | if (index < lowBound || index >= highBound) { 22 | 23 | throw new OutOfBoundsException($"index {index} must be in range {lowBound}..{highBound}"); 24 | 25 | } 26 | 27 | } 28 | 29 | public static void IS_CREATED(T collection) where T : ME.MA.IIsCreated { 30 | 31 | if (collection.isCreated == false) { 32 | 33 | IS_CREATED_BURST_DISCARD(collection); 34 | throw new CollectionNotCreated("Collection not created"); 35 | 36 | } 37 | 38 | } 39 | 40 | [Unity.Burst.BurstDiscardAttribute] 41 | private static void IS_CREATED_BURST_DISCARD(T collection) where T : ME.MA.IIsCreated { 42 | 43 | if (collection.isCreated == false) { 44 | 45 | throw new CollectionNotCreated($"{collection.GetType()} not created"); 46 | 47 | } 48 | 49 | } 50 | 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Exceptions.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9b875899104a4afdbff295238be2442b 3 | timeCreated: 1667980069 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Flags.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA { 2 | 3 | public enum AllocatorType { 4 | 5 | Invalid = 0, 6 | Persistent, 7 | Temp, 8 | 9 | } 10 | 11 | public enum ClearOptions { 12 | 13 | ClearMemory, 14 | UninitializedMemory, 15 | 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/Flags.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0de7a0e82f514f338b0fb08e62b0d7d8 3 | timeCreated: 1654769522 -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/ME.MemoryAllocator.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ME.MemoryAllocator", 3 | "rootNamespace": "", 4 | "references": [ 5 | "Unity.Burst" 6 | ], 7 | "includePlatforms": [], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": true, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /MemoryAllocator/Runtime/ME.MemoryAllocator.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 08f563318a7e54cf6bb569680dbe7963 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /MemoryAllocator/Tests.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 30b0a4cfee2c843e9a85656ea4ec9c96 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /MemoryAllocator/Tests/Core.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7e6ad6a270028f2409c8643361ca311a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /MemoryAllocator/Tests/Dictionary.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace ME.MA.Tests.Collections { 4 | 5 | using NUnit.Framework; 6 | using UnityEngine; 7 | using ME.MA.Collections; 8 | 9 | public class Dictionary_Tests { 10 | 11 | [Test] 12 | public void Initialize() { 13 | 14 | var allocator = Core_Tests.GetAllocator(10); 15 | 16 | var list = new Dictionary(ref allocator, 10); 17 | Assert.IsTrue(list.isCreated); 18 | list.Dispose(ref allocator); 19 | 20 | allocator.Dispose(); 21 | 22 | } 23 | 24 | [Test] 25 | public void ForEach() { 26 | 27 | var allocator = Core_Tests.GetAllocator(10); 28 | 29 | var list = new Dictionary(ref allocator, 10); 30 | for (int i = 0; i < 100; ++i) { 31 | 32 | list.Add(ref allocator, i, new Vector3Int(i, i, i)); 33 | 34 | } 35 | 36 | var cnt = 0; 37 | var e = list.GetEnumerator(in allocator); 38 | while (e.MoveNext() == true) { 39 | Assert.IsTrue(e.Current.Key == e.Current.Value.x && e.Current.Value.x >= 0 && e.Current.Value.x < 100); 40 | ++cnt; 41 | } 42 | e.Dispose(); 43 | 44 | Assert.IsTrue(list.Count == 100); 45 | Assert.IsTrue(cnt == 100); 46 | 47 | allocator.Dispose(); 48 | 49 | } 50 | 51 | [Test] 52 | public void Add() { 53 | 54 | var allocator = Core_Tests.GetAllocator(10); 55 | StaticAllocatorProxy.defaultAllocator = allocator; 56 | 57 | var list = new Dictionary(ref allocator, 10); 58 | for (int i = 0; i < 100; ++i) { 59 | 60 | list.Add(ref allocator, i, i); 61 | 62 | } 63 | 64 | Assert.IsTrue(list.Count == 100); 65 | 66 | allocator.Dispose(); 67 | 68 | } 69 | 70 | [Test] 71 | public void Contains() { 72 | 73 | var allocator = Core_Tests.GetAllocator(10); 74 | 75 | var cnt = 1000; 76 | var list = new Dictionary(ref allocator, 10); 77 | for (int i = 0; i < cnt; ++i) { 78 | 79 | list.Add(ref allocator, i, i); 80 | 81 | } 82 | 83 | for (int i = 0; i < cnt; ++i) { 84 | 85 | Assert.IsTrue(list.ContainsKey(in allocator, i)); 86 | Assert.IsTrue(list.ContainsValue(in allocator, i)); 87 | 88 | } 89 | 90 | allocator.Dispose(); 91 | 92 | } 93 | 94 | [Test] 95 | public void LikeADictionary() { 96 | 97 | var allocator = Core_Tests.GetAllocator(10); 98 | StaticAllocatorProxy.defaultAllocator = allocator; 99 | 100 | var cnt = 1000; 101 | var source = new System.Collections.Generic.Dictionary(10); 102 | var list = new Dictionary(ref allocator, 10); 103 | var testData = new int[cnt]; 104 | for (int i = 0; i < cnt; ++i) { 105 | 106 | testData[i] = i; 107 | list.Add(ref allocator, i, i); 108 | source.Add(i, i); 109 | 110 | } 111 | 112 | testData = testData.OrderBy(x => Random.value).ToArray(); 113 | 114 | for (int i = 0; i < testData.Length; ++i) { 115 | 116 | var key = testData[i]; 117 | Assert.IsTrue(list.ContainsKey(in allocator, key)); 118 | Assert.IsTrue(source.ContainsKey(key)); 119 | 120 | } 121 | 122 | { 123 | var newDic = new Dictionary(ref allocator, list.Count); 124 | newDic.CopyFrom(ref allocator, list); 125 | 126 | for (int i = 0; i < testData.Length; ++i) { 127 | 128 | var key = testData[i]; 129 | Assert.IsTrue(newDic.ContainsKey(in allocator, key)); 130 | 131 | } 132 | } 133 | 134 | for (int i = 0; i < testData.Length / 2; ++i) { 135 | 136 | var key = testData[i]; 137 | Assert.IsTrue(list.Remove(ref allocator, key)); 138 | Assert.IsTrue(source.Remove(key)); 139 | 140 | } 141 | 142 | Assert.IsTrue(list.Count == cnt / 2); 143 | Assert.IsTrue(source.Count == cnt / 2); 144 | 145 | for (int i = testData.Length / 2; i < testData.Length; ++i) { 146 | 147 | var key = testData[i]; 148 | Assert.IsTrue(list.Remove(ref allocator, key)); 149 | Assert.IsTrue(source.Remove(key)); 150 | 151 | } 152 | 153 | Assert.AreEqual(0, source.Count); 154 | Assert.AreEqual(0, list.Count); 155 | 156 | list.Clear(in allocator); 157 | source.Clear(); 158 | 159 | testData = new int[] { 160 | 38, 33, 138, 13, 17, 52, 130, 59, 2, 140, 64, 66, 60, 67, 35, 164, 6, 80, 81, 83, 4, 5, 155, 137, 3, 22, 65, 23, 24, 21, 18, 125, 82, 37, 118, 133, 75, 178 161 | }; 162 | 163 | testData = testData.OrderBy(x => Random.value).ToArray(); 164 | 165 | foreach (var data in testData) { 166 | 167 | list.Add(ref allocator, data, data); 168 | source.Add(data, data); 169 | 170 | } 171 | 172 | for (int i = 0; i < testData.Length; ++i) { 173 | 174 | var key = testData[i]; 175 | Assert.IsTrue(list.ContainsKey(in allocator, key)); 176 | Assert.IsTrue(source.ContainsKey(key)); 177 | 178 | } 179 | 180 | { 181 | var newDic = new Dictionary(ref allocator, list.Count); 182 | newDic.CopyFrom(ref allocator, list); 183 | 184 | for (int i = 0; i < testData.Length; ++i) { 185 | 186 | var key = testData[i]; 187 | Assert.IsTrue(newDic.ContainsKey(in allocator, key)); 188 | 189 | } 190 | } 191 | 192 | { 193 | var newDic = new Dictionary(ref allocator, 1); 194 | var testData1 = new int[] { 195 | 38, 33, 138, 13, 17, 52, 130, 59, 2, 140, 64, 66, 60, 67, 35, 164, 6, 80, 81, 83, 4, 5, 155, 137, 3, 22, 65, 23, 24, 21, 18, 125, 82, 37, 118, 133, 75, 196 | }; 197 | foreach (var item in testData1) { 198 | newDic.Add(ref allocator, item, item); 199 | } 200 | 201 | var newDic2 = new Dictionary(ref allocator, newDic.Count + 1); 202 | newDic2.CopyFrom(ref allocator, newDic); 203 | newDic2.Add(ref allocator, 178, 178); 204 | newDic.Add(ref allocator, 178, 178); 205 | Assert.IsTrue(newDic.ContainsKey(in allocator, 118)); 206 | Assert.IsTrue(newDic2.ContainsKey(in allocator, 118)); 207 | } 208 | 209 | allocator.Dispose(); 210 | 211 | } 212 | 213 | [Test] 214 | public void Remove() { 215 | 216 | var allocator = Core_Tests.GetAllocator(10); 217 | 218 | var list = new Dictionary(ref allocator, 10); 219 | for (int i = 0; i < 100; ++i) { 220 | 221 | list.Add(ref allocator, i, new Vector3(i, i, i)); 222 | 223 | } 224 | 225 | Assert.IsTrue(list.Count == 100); 226 | Assert.IsTrue(list.Remove(ref allocator, 50)); 227 | Assert.IsFalse(list.Remove(ref allocator, 50)); 228 | Assert.IsTrue(list.Count == 99); 229 | 230 | allocator.Dispose(); 231 | 232 | } 233 | 234 | [Test] 235 | public void Clear() { 236 | 237 | var allocator = Core_Tests.GetAllocator(10); 238 | 239 | var list = new Dictionary(ref allocator, 10); 240 | for (int i = 0; i < 100; ++i) { 241 | 242 | list.Add(ref allocator, i, new Vector3(i, i, i)); 243 | 244 | } 245 | 246 | Assert.IsTrue(list.Count == 100); 247 | list.Clear(in allocator); 248 | Assert.IsTrue(list.Count == 0); 249 | 250 | allocator.Dispose(); 251 | 252 | } 253 | 254 | } 255 | 256 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/Dictionary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7fa16eac08dc45bab1f0bca8cb804a43 3 | timeCreated: 1658478247 -------------------------------------------------------------------------------- /MemoryAllocator/Tests/EquatableDictionary.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Tests.Collections { 2 | 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | using ME.MA.Collections; 6 | 7 | public class EquatableDictionary_Tests { 8 | 9 | [Test] 10 | public void Initialize() { 11 | 12 | var allocator = Core_Tests.GetAllocator(10); 13 | 14 | var list = new EquatableDictionary(ref allocator, 10); 15 | Assert.IsTrue(list.isCreated); 16 | list.Dispose(ref allocator); 17 | 18 | allocator.Dispose(); 19 | 20 | } 21 | 22 | [Test] 23 | public void ForEach() { 24 | 25 | var allocator = Core_Tests.GetAllocator(10); 26 | 27 | var list = new EquatableDictionary(ref allocator, 10); 28 | for (int i = 0; i < 100; ++i) { 29 | 30 | list.Add(ref allocator, i, new Vector3Int(i, i, i)); 31 | 32 | } 33 | 34 | var cnt = 0; 35 | var e = list.GetEnumerator(in allocator); 36 | while (e.MoveNext() == true) { 37 | Assert.IsTrue(e.Current.Key == e.Current.Value.x && e.Current.Value.x >= 0 && e.Current.Value.x < 100); 38 | ++cnt; 39 | } 40 | e.Dispose(); 41 | 42 | Assert.IsTrue(list.Count == 100); 43 | Assert.IsTrue(cnt == 100); 44 | 45 | allocator.Dispose(); 46 | 47 | } 48 | 49 | [Test] 50 | public void Add() { 51 | 52 | var allocator = Core_Tests.GetAllocator(10); 53 | 54 | var list = new EquatableDictionary(ref allocator, 10); 55 | for (int i = 0; i < 100; ++i) { 56 | 57 | list.Add(ref allocator, i, new Vector3(i, i, i)); 58 | 59 | } 60 | 61 | Assert.IsTrue(list.Count == 100); 62 | 63 | allocator.Dispose(); 64 | 65 | } 66 | 67 | [Test] 68 | public void Contains() { 69 | 70 | var allocator = Core_Tests.GetAllocator(10); 71 | 72 | var cnt = 2; 73 | var list = new EquatableDictionary(ref allocator, 10); 74 | for (int i = 0; i < cnt; ++i) { 75 | 76 | list.Add(ref allocator, i, new Vector3(i, i, i)); 77 | 78 | } 79 | 80 | for (int i = 0; i < cnt; ++i) { 81 | 82 | Assert.IsTrue(list.ContainsKey(in allocator, i)); 83 | Assert.IsTrue(list.ContainsValue(in allocator, new Vector3(i, i, i))); 84 | 85 | } 86 | 87 | allocator.Dispose(); 88 | 89 | } 90 | 91 | [Test] 92 | public void Remove() { 93 | 94 | var allocator = Core_Tests.GetAllocator(10); 95 | 96 | var list = new EquatableDictionary(ref allocator, 10); 97 | for (int i = 0; i < 100; ++i) { 98 | 99 | list.Add(ref allocator, i, new Vector3(i, i, i)); 100 | 101 | } 102 | 103 | Assert.IsTrue(list.Count == 100); 104 | Assert.IsTrue(list.Remove(ref allocator, 50)); 105 | Assert.IsFalse(list.Remove(ref allocator, 50)); 106 | Assert.IsTrue(list.Count == 99); 107 | 108 | allocator.Dispose(); 109 | 110 | } 111 | 112 | [Test] 113 | public void Clear() { 114 | 115 | var allocator = Core_Tests.GetAllocator(10); 116 | 117 | var list = new EquatableDictionary(ref allocator, 10); 118 | for (int i = 0; i < 100; ++i) { 119 | 120 | list.Add(ref allocator, i, new Vector3(i, i, i)); 121 | 122 | } 123 | 124 | Assert.IsTrue(list.Count == 100); 125 | list.Clear(in allocator); 126 | Assert.IsTrue(list.Count == 0); 127 | 128 | allocator.Dispose(); 129 | 130 | } 131 | 132 | } 133 | 134 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/EquatableDictionary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2a1ba15356d6419b8fed9c675e76c941 3 | timeCreated: 1659023342 -------------------------------------------------------------------------------- /MemoryAllocator/Tests/EquatableHashSet.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Tests.Collections { 2 | 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | using ME.MA.Collections; 6 | 7 | public class EquatableHashSet_Tests { 8 | 9 | [Test] 10 | public void Initialize() { 11 | 12 | var allocator = Core_Tests.GetAllocator(10); 13 | 14 | var list = new EquatableHashSet(ref allocator, 10); 15 | Assert.IsTrue(list.isCreated); 16 | list.Dispose(ref allocator); 17 | 18 | allocator.Dispose(); 19 | 20 | } 21 | 22 | [Test] 23 | public void ForEach() { 24 | 25 | var allocator = Core_Tests.GetAllocator(10); 26 | 27 | var list = new EquatableHashSet(ref allocator, 10); 28 | for (int i = 0; i < 100; ++i) { 29 | 30 | list.Add(ref allocator, new Vector3(i, i, i)); 31 | 32 | } 33 | 34 | var cnt = 0; 35 | var e = list.GetEnumerator(in allocator); 36 | while (e.MoveNext() == true) { 37 | Assert.IsTrue(e.Current.x >= 0 && e.Current.x < 100); 38 | ++cnt; 39 | } 40 | e.Dispose(); 41 | 42 | Assert.IsTrue(list.Count == 100); 43 | Assert.IsTrue(cnt == 100); 44 | 45 | allocator.Dispose(); 46 | 47 | } 48 | 49 | [Test] 50 | public void Add() { 51 | 52 | var allocator = Core_Tests.GetAllocator(10); 53 | 54 | var list = new EquatableHashSet(ref allocator, 10); 55 | for (int i = 0; i < 100; ++i) { 56 | 57 | list.Add(ref allocator, new Vector3(i, i, i)); 58 | 59 | } 60 | 61 | Assert.IsTrue(list.Count == 100); 62 | 63 | allocator.Dispose(); 64 | 65 | } 66 | 67 | [Test] 68 | public void Contains() { 69 | 70 | var allocator = Core_Tests.GetAllocator(10); 71 | 72 | var cnt = 2; 73 | var list = new EquatableHashSet(ref allocator, 10); 74 | for (int i = 0; i < cnt; ++i) { 75 | 76 | list.Add(ref allocator, new Vector3(i, i, i)); 77 | 78 | } 79 | 80 | for (int i = 0; i < cnt; ++i) { 81 | 82 | Assert.IsTrue(list.Contains(in allocator, new Vector3(i, i, i))); 83 | 84 | } 85 | 86 | allocator.Dispose(); 87 | 88 | } 89 | 90 | [Test] 91 | public void Remove() { 92 | 93 | var allocator = Core_Tests.GetAllocator(10); 94 | 95 | var list = new EquatableHashSet(ref allocator, 10); 96 | for (int i = 0; i < 100; ++i) { 97 | 98 | list.Add(ref allocator, new Vector3(i, i, i)); 99 | 100 | } 101 | 102 | Assert.IsTrue(list.Count == 100); 103 | Assert.IsTrue(list.Remove(ref allocator, new Vector3(50, 50, 50))); 104 | Assert.IsFalse(list.Remove(ref allocator, new Vector3(50, 50, 50))); 105 | Assert.IsTrue(list.Count == 99); 106 | 107 | allocator.Dispose(); 108 | 109 | } 110 | 111 | [Test] 112 | public void Clear() { 113 | 114 | var allocator = Core_Tests.GetAllocator(10); 115 | 116 | var list = new EquatableHashSet(ref allocator, 10); 117 | for (int i = 0; i < 100; ++i) { 118 | 119 | list.Add(ref allocator, new Vector3(i, i, i)); 120 | 121 | } 122 | 123 | Assert.IsTrue(list.Count == 100); 124 | list.Clear(in allocator); 125 | Assert.IsTrue(list.Count == 0); 126 | 127 | allocator.Dispose(); 128 | 129 | } 130 | 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/EquatableHashSet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a832adf1a9ea456fa5e4730cb46f026e 3 | timeCreated: 1659017094 -------------------------------------------------------------------------------- /MemoryAllocator/Tests/HashSet.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Tests.Collections { 2 | 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | using ME.MA.Collections; 6 | 7 | public class HashSet_Tests { 8 | 9 | [Test] 10 | public void Initialize() { 11 | 12 | var allocator = Core_Tests.GetAllocator(10); 13 | 14 | var list = new HashSet(ref allocator, 10); 15 | Assert.IsTrue(list.isCreated); 16 | list.Dispose(ref allocator); 17 | 18 | allocator.Dispose(); 19 | 20 | } 21 | 22 | [Test] 23 | public void ForEach() { 24 | 25 | var allocator = Core_Tests.GetAllocator(10); 26 | 27 | var list = new HashSet(ref allocator, 10); 28 | for (int i = 0; i < 100; ++i) { 29 | 30 | list.Add(ref allocator, new Vector3(i, i, i)); 31 | 32 | } 33 | 34 | var cnt = 0; 35 | var e = list.GetEnumerator(in allocator); 36 | while (e.MoveNext() == true) { 37 | Assert.IsTrue(e.Current.x >= 0 && e.Current.x < 100); 38 | ++cnt; 39 | } 40 | e.Dispose(); 41 | 42 | Assert.IsTrue(list.Count == 100); 43 | Assert.IsTrue(cnt == 100); 44 | 45 | allocator.Dispose(); 46 | 47 | } 48 | 49 | [Test] 50 | public void Add() { 51 | 52 | var allocator = Core_Tests.GetAllocator(10); 53 | 54 | var list = new HashSet(ref allocator, 10); 55 | for (int i = 0; i < 100; ++i) { 56 | 57 | list.Add(ref allocator, new Vector3(i, i, i)); 58 | 59 | } 60 | 61 | Assert.IsTrue(list.Count == 100); 62 | 63 | allocator.Dispose(); 64 | 65 | } 66 | 67 | [Test] 68 | public void Contains() { 69 | 70 | var allocator = Core_Tests.GetAllocator(10); 71 | 72 | var cnt = 2; 73 | var list = new HashSet(ref allocator, 10); 74 | for (int i = 0; i < cnt; ++i) { 75 | 76 | list.Add(ref allocator, new Vector3(i, i, i)); 77 | 78 | } 79 | 80 | for (int i = 0; i < cnt; ++i) { 81 | 82 | Assert.IsTrue(list.Contains(in allocator, new Vector3(i, i, i))); 83 | 84 | } 85 | 86 | allocator.Dispose(); 87 | 88 | } 89 | 90 | [Test] 91 | public void Remove() { 92 | 93 | var allocator = Core_Tests.GetAllocator(10); 94 | 95 | var list = new HashSet(ref allocator, 10); 96 | for (int i = 0; i < 100; ++i) { 97 | 98 | list.Add(ref allocator, new Vector3(i, i, i)); 99 | 100 | } 101 | 102 | Assert.IsTrue(list.Count == 100); 103 | Assert.IsTrue(list.Remove(ref allocator, new Vector3(50, 50, 50))); 104 | Assert.IsFalse(list.Remove(ref allocator, new Vector3(50, 50, 50))); 105 | Assert.IsTrue(list.Count == 99); 106 | 107 | allocator.Dispose(); 108 | 109 | } 110 | 111 | [Test] 112 | public void Clear() { 113 | 114 | var allocator = Core_Tests.GetAllocator(10); 115 | 116 | var list = new HashSet(ref allocator, 10); 117 | for (int i = 0; i < 100; ++i) { 118 | 119 | list.Add(ref allocator, new Vector3(i, i, i)); 120 | 121 | } 122 | 123 | Assert.IsTrue(list.Count == 100); 124 | list.Clear(in allocator); 125 | Assert.IsTrue(list.Count == 0); 126 | 127 | allocator.Dispose(); 128 | 129 | } 130 | 131 | } 132 | 133 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/HashSet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: af511c87b88b4f01837a21a4ab4f0206 3 | timeCreated: 1658407570 -------------------------------------------------------------------------------- /MemoryAllocator/Tests/List.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Tests.Collections { 2 | 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | using ME.MA.Collections; 6 | 7 | public class List_Tests { 8 | 9 | [Test] 10 | public void Initialize() { 11 | 12 | var allocator = Core_Tests.GetAllocator(10); 13 | 14 | var list = new List(ref allocator, 10); 15 | Assert.IsTrue(list.isCreated); 16 | list.Dispose(ref allocator); 17 | 18 | allocator.Dispose(); 19 | 20 | } 21 | 22 | [Test] 23 | public void Performance() { 24 | 25 | var allocator = Core_Tests.GetAllocator(10); 26 | 27 | var count = 1_000_000; 28 | var genericList = new System.Collections.Generic.List(count); 29 | var list = new List(ref allocator, count); 30 | for (int i = 0; i < count; ++i) { 31 | list.Add(ref allocator, i); 32 | genericList.Add(i); 33 | } 34 | 35 | { 36 | var ms = System.Diagnostics.Stopwatch.StartNew(); 37 | var sum = 0; 38 | for (int i = 0; i < count; ++i) { 39 | sum += genericList[i]; 40 | } 41 | 42 | ms.Stop(); 43 | Debug.Log("Generic List: " + ms.ElapsedMilliseconds + "ms"); 44 | } 45 | { 46 | var ms = System.Diagnostics.Stopwatch.StartNew(); 47 | var sum = 0; 48 | for (int i = 0; i < count; ++i) { 49 | sum += list[in allocator, i]; 50 | } 51 | 52 | ms.Stop(); 53 | Debug.Log("Allocator List: " + ms.ElapsedMilliseconds + "ms"); 54 | } 55 | 56 | list.Dispose(ref allocator); 57 | 58 | allocator.Dispose(); 59 | 60 | } 61 | 62 | [Test] 63 | public void ForEach() { 64 | 65 | var allocator = Core_Tests.GetAllocator(10); 66 | 67 | var list = new List(ref allocator, 10); 68 | for (int i = 0; i < 100; ++i) { 69 | 70 | list.Add(ref allocator, new Vector3(i, i, i)); 71 | 72 | } 73 | 74 | var cnt = 0; 75 | var e = list.GetEnumerator(in allocator); 76 | while (e.MoveNext() == true) { 77 | Assert.IsTrue(e.Current.x >= 0 && e.Current.x < 100); 78 | ++cnt; 79 | } 80 | e.Dispose(); 81 | 82 | Assert.IsTrue(list.Count == 100); 83 | Assert.IsTrue(cnt == 100); 84 | 85 | allocator.Dispose(); 86 | 87 | } 88 | 89 | [Test] 90 | public void Add() { 91 | 92 | var allocator = Core_Tests.GetAllocator(10); 93 | 94 | var list = new List(ref allocator, 10); 95 | for (int i = 0; i < 100; ++i) { 96 | 97 | list.Add(ref allocator, new Vector3(i, i, i)); 98 | 99 | } 100 | 101 | Assert.IsTrue(list.Count == 100); 102 | 103 | allocator.Dispose(); 104 | 105 | } 106 | 107 | [Test] 108 | public void AddRange() { 109 | 110 | var allocator = Core_Tests.GetAllocator(10); 111 | 112 | var source = new List(ref allocator, 10); 113 | var target = new List(ref allocator, 10); 114 | for (int i = 0; i < 100; ++i) { 115 | 116 | source.Add(ref allocator, i); 117 | 118 | } 119 | 120 | target.AddRange(ref allocator, source); 121 | Assert.IsTrue(source.Count == 100); 122 | Assert.IsTrue(target.Count == 100); 123 | for (int i = 0; i < 100; ++i) { 124 | 125 | Assert.IsTrue(source[in allocator, i] == target[in allocator, i]); 126 | 127 | } 128 | 129 | target.AddRange(ref allocator, source); 130 | for (int i = 0; i < 100; ++i) { 131 | 132 | Assert.IsTrue(target[in allocator, i] == i); 133 | 134 | } 135 | for (int i = 0; i < 100; ++i) { 136 | 137 | Assert.IsTrue(target[in allocator, i + 100] == i); 138 | 139 | } 140 | 141 | Assert.IsTrue(target.Count == 200); 142 | 143 | allocator.Dispose(); 144 | 145 | } 146 | 147 | [Test] 148 | public void RemoveAt() { 149 | 150 | var allocator = Core_Tests.GetAllocator(10); 151 | 152 | var list = new List(ref allocator, 10); 153 | for (int i = 0; i < 100; ++i) { 154 | 155 | list.Add(ref allocator, new Vector3(i, i, i)); 156 | 157 | } 158 | 159 | Assert.IsTrue(list.Count == 100); 160 | Assert.IsTrue(list.RemoveAt(ref allocator, 50)); 161 | Assert.IsTrue(list.Count == 99); 162 | Assert.IsFalse(list.RemoveAt(ref allocator, 100)); 163 | list.Add(ref allocator, new Vector3()); 164 | Assert.IsTrue(list.Count == 100); 165 | 166 | allocator.Dispose(); 167 | 168 | } 169 | 170 | [Test] 171 | public void Contains() { 172 | 173 | var allocator = Core_Tests.GetAllocator(10); 174 | 175 | var list = new List(ref allocator, 10); 176 | for (int i = 0; i < 100; ++i) { 177 | 178 | list.Add(ref allocator, new Vector3(i, i, i)); 179 | 180 | } 181 | 182 | for (int i = 0; i < 100; ++i) { 183 | 184 | Assert.IsTrue(list.Contains(in allocator, new Vector3(i, i, i))); 185 | 186 | } 187 | 188 | allocator.Dispose(); 189 | 190 | } 191 | 192 | [Test] 193 | public void RemoveAtFast() { 194 | 195 | var allocator = Core_Tests.GetAllocator(10); 196 | 197 | var list = new List(ref allocator, 10); 198 | for (int i = 0; i < 100; ++i) { 199 | 200 | list.Add(ref allocator, new Vector3(i, i, i)); 201 | 202 | } 203 | 204 | Assert.IsTrue(list.Count == 100); 205 | Assert.IsTrue(list.RemoveAtFast(ref allocator, 50)); 206 | Assert.IsTrue(list.Count == 99); 207 | Assert.IsFalse(list.RemoveAtFast(ref allocator, 100)); 208 | list.Add(ref allocator, new Vector3()); 209 | Assert.IsTrue(list.Count == 100); 210 | 211 | allocator.Dispose(); 212 | 213 | } 214 | 215 | [Test] 216 | public void Remove() { 217 | 218 | var allocator = Core_Tests.GetAllocator(10); 219 | 220 | var list = new List(ref allocator, 10); 221 | for (int i = 0; i < 100; ++i) { 222 | 223 | list.Add(ref allocator, new Vector3(i, i, i)); 224 | 225 | } 226 | 227 | Assert.IsTrue(list.Count == 100); 228 | Assert.IsTrue(list.Remove(ref allocator, new Vector3(50, 50, 50))); 229 | Assert.IsFalse(list.Remove(ref allocator, new Vector3(50, 50, 50))); 230 | Assert.IsTrue(list.Count == 99); 231 | 232 | allocator.Dispose(); 233 | 234 | } 235 | 236 | [Test] 237 | public void Clear() { 238 | 239 | var allocator = Core_Tests.GetAllocator(10); 240 | 241 | var list = new List(ref allocator, 10); 242 | for (int i = 0; i < 100; ++i) { 243 | 244 | list.Add(ref allocator, new Vector3(i, i, i)); 245 | 246 | } 247 | 248 | Assert.IsTrue(list.Count == 100); 249 | list.Clear(in allocator); 250 | Assert.IsTrue(list.Count == 0); 251 | 252 | allocator.Dispose(); 253 | 254 | } 255 | 256 | } 257 | 258 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/List.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: d9798b0308c041ac8fe72df081198ec1 3 | timeCreated: 1658400208 -------------------------------------------------------------------------------- /MemoryAllocator/Tests/ME.MemoryAllocator.Tests.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ME.MemoryAllocator.Tests", 3 | "rootNamespace": "", 4 | "references": [ 5 | "ME.MemoryAllocator", 6 | "UnityEditor.TestRunner" 7 | ], 8 | "includePlatforms": [ 9 | "Editor" 10 | ], 11 | "excludePlatforms": [], 12 | "allowUnsafeCode": true, 13 | "overrideReferences": false, 14 | "precompiledReferences": [], 15 | "autoReferenced": true, 16 | "defineConstraints": [], 17 | "versionDefines": [], 18 | "noEngineReferences": false 19 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/ME.MemoryAllocator.Tests.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 17d941a098cc043be98846e56ea4f8d2 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /MemoryAllocator/Tests/NativeHashSet.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Tests.Collections { 2 | 3 | using NUnit.Framework; 4 | using ME.MA.Collections; 5 | 6 | public class NativeHashSet_Tests { 7 | 8 | public struct Test : IEquatableAllocator { 9 | 10 | public float x; 11 | public float y; 12 | public float z; 13 | 14 | public Test(float x, float y, float z) { 15 | this.x = x; 16 | this.y = y; 17 | this.z = z; 18 | } 19 | 20 | public bool Equals(in MemoryAllocator allocator, Test obj) { 21 | return this.x == obj.x && 22 | this.y == obj.y && 23 | this.z == obj.z; 24 | } 25 | 26 | public int GetHash(in MemoryAllocator allocator) { 27 | return this.x.GetHashCode() ^ this.y.GetHashCode() ^ this.z.GetHashCode(); 28 | } 29 | 30 | } 31 | 32 | [Test] 33 | public void Initialize() { 34 | 35 | var allocator = Core_Tests.GetAllocator(10); 36 | 37 | var list = new NativeHashSet(ref allocator, 10); 38 | Assert.IsTrue(list.isCreated); 39 | list.Dispose(ref allocator); 40 | 41 | allocator.Dispose(); 42 | 43 | } 44 | 45 | [Test] 46 | public void ForEach() { 47 | 48 | var allocator = Core_Tests.GetAllocator(10); 49 | 50 | var list = new NativeHashSet(ref allocator, 10); 51 | for (int i = 0; i < 100; ++i) { 52 | 53 | list.Add(ref allocator, new Test(i, i, i)); 54 | 55 | } 56 | 57 | var cnt = 0; 58 | var e = list.GetEnumerator(in allocator); 59 | while (e.MoveNext() == true) { 60 | Assert.IsTrue(e.Current.x >= 0 && e.Current.x < 100); 61 | ++cnt; 62 | } 63 | e.Dispose(); 64 | 65 | Assert.IsTrue(list.Count == 100); 66 | Assert.IsTrue(cnt == 100); 67 | 68 | allocator.Dispose(); 69 | 70 | } 71 | 72 | [Test] 73 | public void Add() { 74 | 75 | var allocator = Core_Tests.GetAllocator(10); 76 | 77 | var list = new NativeHashSet(ref allocator, 10); 78 | for (int i = 0; i < 100; ++i) { 79 | 80 | list.Add(ref allocator, new Test(i, i, i)); 81 | 82 | } 83 | 84 | Assert.IsTrue(list.Count == 100); 85 | 86 | allocator.Dispose(); 87 | 88 | } 89 | 90 | [Test] 91 | public void Contains() { 92 | 93 | var allocator = Core_Tests.GetAllocator(10); 94 | 95 | var cnt = 2; 96 | var list = new NativeHashSet(ref allocator, 10); 97 | for (int i = 0; i < cnt; ++i) { 98 | 99 | list.Add(ref allocator, new Test(i, i, i)); 100 | 101 | } 102 | 103 | for (int i = 0; i < cnt; ++i) { 104 | 105 | Assert.IsTrue(list.Contains(in allocator, new Test(i, i, i))); 106 | 107 | } 108 | 109 | allocator.Dispose(); 110 | 111 | } 112 | 113 | [Test] 114 | public void Remove() { 115 | 116 | var allocator = Core_Tests.GetAllocator(10); 117 | 118 | var list = new NativeHashSet(ref allocator, 10); 119 | for (int i = 0; i < 100; ++i) { 120 | 121 | list.Add(ref allocator, new Test(i, i, i)); 122 | 123 | } 124 | 125 | Assert.IsTrue(list.Count == 100); 126 | Assert.IsTrue(list.Remove(ref allocator, new Test(50, 50, 50))); 127 | Assert.IsFalse(list.Remove(ref allocator, new Test(50, 50, 50))); 128 | Assert.IsTrue(list.Count == 99); 129 | 130 | allocator.Dispose(); 131 | 132 | } 133 | 134 | [Test] 135 | public void Clear() { 136 | 137 | var allocator = Core_Tests.GetAllocator(10); 138 | 139 | var list = new NativeHashSet(ref allocator, 10); 140 | for (int i = 0; i < 100; ++i) { 141 | 142 | list.Add(ref allocator, new Test(i, i, i)); 143 | 144 | } 145 | 146 | Assert.IsTrue(list.Count == 100); 147 | list.Clear(in allocator); 148 | Assert.IsTrue(list.Count == 0); 149 | 150 | allocator.Dispose(); 151 | 152 | } 153 | 154 | } 155 | 156 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/NativeHashSet.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb53971a62f5468793647b3950fb740d 3 | timeCreated: 1659017105 -------------------------------------------------------------------------------- /MemoryAllocator/Tests/Queue.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Tests.Collections { 2 | 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | using ME.MA.Collections; 6 | 7 | public class Queue_Tests { 8 | 9 | [Test] 10 | public void Initialize() { 11 | 12 | var allocator = Core_Tests.GetAllocator(10); 13 | 14 | var list = new Queue(ref allocator, 10); 15 | Assert.IsTrue(list.isCreated); 16 | list.Dispose(ref allocator); 17 | 18 | allocator.Dispose(); 19 | 20 | } 21 | 22 | [Test] 23 | public void ForEach() { 24 | 25 | var allocator = Core_Tests.GetAllocator(10); 26 | 27 | var list = new Queue(ref allocator, 10); 28 | for (int i = 0; i < 100; ++i) { 29 | 30 | list.Enqueue(ref allocator, new Vector3(i, i, i)); 31 | 32 | } 33 | 34 | var cnt = 0; 35 | var e = list.GetEnumerator(in allocator); 36 | while (e.MoveNext() == true) { 37 | Assert.IsTrue(e.Current.x >= 0 && e.Current.x < 100); 38 | ++cnt; 39 | } 40 | e.Dispose(); 41 | 42 | Assert.IsTrue(list.Count == 100); 43 | Assert.IsTrue(cnt == 100); 44 | 45 | allocator.Dispose(); 46 | 47 | } 48 | 49 | [Test] 50 | public void Enqueue() { 51 | 52 | var allocator = Core_Tests.GetAllocator(10); 53 | 54 | var list = new Queue(ref allocator, 10); 55 | for (int i = 0; i < 100; ++i) { 56 | 57 | list.Enqueue(ref allocator, new Vector3(i, i, i)); 58 | 59 | } 60 | 61 | Assert.IsTrue(list.Count == 100); 62 | 63 | allocator.Dispose(); 64 | 65 | } 66 | 67 | [Test] 68 | public void Contains() { 69 | 70 | var allocator = Core_Tests.GetAllocator(10); 71 | 72 | var list = new Queue(ref allocator, 10); 73 | for (int i = 0; i < 100; ++i) { 74 | 75 | list.Enqueue(ref allocator, new Vector3(i, i, i)); 76 | 77 | } 78 | 79 | for (int i = 0; i < 100; ++i) { 80 | 81 | Assert.IsTrue(list.Contains(in allocator, new Vector3(i, i, i))); 82 | 83 | } 84 | 85 | allocator.Dispose(); 86 | 87 | } 88 | 89 | [Test] 90 | public void Dequeue() { 91 | 92 | var allocator = Core_Tests.GetAllocator(10); 93 | 94 | var list = new Queue(ref allocator, 10); 95 | for (int i = 0; i < 100; ++i) { 96 | 97 | list.Enqueue(ref allocator, new Vector3Int(i, i, i)); 98 | 99 | } 100 | 101 | Assert.IsTrue(list.Count == 100); 102 | Assert.IsTrue(list.Dequeue(in allocator) == new Vector3Int(0, 0, 0)); 103 | Assert.IsTrue(list.Dequeue(in allocator) == new Vector3Int(1, 1, 1)); 104 | Assert.IsTrue(list.Count == 98); 105 | 106 | allocator.Dispose(); 107 | 108 | } 109 | 110 | [Test] 111 | public void Peek() { 112 | 113 | var allocator = Core_Tests.GetAllocator(10); 114 | 115 | var list = new Queue(ref allocator, 10); 116 | for (int i = 0; i < 100; ++i) { 117 | 118 | list.Enqueue(ref allocator, new Vector3Int(i, i, i)); 119 | 120 | } 121 | 122 | Assert.IsTrue(list.Count == 100); 123 | Assert.IsTrue(list.Peek(in allocator) == new Vector3Int(0, 0, 0)); 124 | Assert.IsTrue(list.Dequeue(in allocator) == new Vector3Int(0, 0, 0)); 125 | Assert.IsTrue(list.Count == 99); 126 | 127 | allocator.Dispose(); 128 | 129 | } 130 | 131 | [Test] 132 | public void EnqueueDequeue() { 133 | 134 | var allocator = Core_Tests.GetAllocator(10); 135 | 136 | var list = new Queue(ref allocator, 10); 137 | for (int i = 0; i < 100; ++i) { 138 | 139 | list.Enqueue(ref allocator, new Vector3Int(i, i, i)); 140 | 141 | } 142 | 143 | Assert.IsTrue(list.Count == 100); 144 | 145 | for (int i = 0; i < 100; ++i) { 146 | 147 | Assert.IsTrue(list.Dequeue(in allocator) == new Vector3Int(i, i, i)); 148 | 149 | } 150 | 151 | Assert.IsTrue(list.Count == 0); 152 | 153 | allocator.Dispose(); 154 | 155 | } 156 | 157 | [Test] 158 | public void Clear() { 159 | 160 | var allocator = Core_Tests.GetAllocator(10); 161 | 162 | var list = new Queue(ref allocator, 10); 163 | for (int i = 0; i < 100; ++i) { 164 | 165 | list.Enqueue(ref allocator, new Vector3(i, i, i)); 166 | 167 | } 168 | 169 | Assert.IsTrue(list.Count == 100); 170 | list.Clear(in allocator); 171 | Assert.IsTrue(list.Count == 0); 172 | 173 | allocator.Dispose(); 174 | 175 | } 176 | 177 | } 178 | 179 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/Queue.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eb4c8c981338415d873f188559fb6641 3 | timeCreated: 1659028954 -------------------------------------------------------------------------------- /MemoryAllocator/Tests/Stack.cs: -------------------------------------------------------------------------------- 1 | namespace ME.MA.Tests.Collections { 2 | 3 | using NUnit.Framework; 4 | using UnityEngine; 5 | using ME.MA.Collections; 6 | 7 | public class Stack_Tests { 8 | 9 | [Test] 10 | public void Initialize() { 11 | 12 | var allocator = Core_Tests.GetAllocator(10); 13 | 14 | var list = new Stack(ref allocator, 10); 15 | Assert.IsTrue(list.isCreated); 16 | list.Dispose(ref allocator); 17 | 18 | allocator.Dispose(); 19 | 20 | } 21 | 22 | [Test] 23 | public void ForEach() { 24 | 25 | var allocator = Core_Tests.GetAllocator(10); 26 | 27 | var list = new Stack(ref allocator, 10); 28 | for (int i = 0; i < 100; ++i) { 29 | 30 | list.Push(ref allocator, new Vector3(i, i, i)); 31 | 32 | } 33 | 34 | var cnt = 0; 35 | var e = list.GetEnumerator(in allocator); 36 | while (e.MoveNext() == true) { 37 | Assert.IsTrue(e.Current.x >= 0 && e.Current.x < 100); 38 | ++cnt; 39 | } 40 | e.Dispose(); 41 | 42 | Assert.IsTrue(list.Count == 100); 43 | Assert.IsTrue(cnt == 100); 44 | 45 | allocator.Dispose(); 46 | 47 | } 48 | 49 | [Test] 50 | public void Push() { 51 | 52 | var allocator = Core_Tests.GetAllocator(10); 53 | 54 | var list = new Stack(ref allocator, 10); 55 | for (int i = 0; i < 100; ++i) { 56 | 57 | list.Push(ref allocator, new Vector3(i, i, i)); 58 | 59 | } 60 | 61 | Assert.IsTrue(list.Count == 100); 62 | 63 | allocator.Dispose(); 64 | 65 | } 66 | 67 | [Test] 68 | public void Contains() { 69 | 70 | var allocator = Core_Tests.GetAllocator(10); 71 | 72 | var list = new Stack(ref allocator, 10); 73 | for (int i = 0; i < 100; ++i) { 74 | 75 | list.Push(ref allocator, new Vector3(i, i, i)); 76 | 77 | } 78 | 79 | for (int i = 0; i < 100; ++i) { 80 | 81 | Assert.IsTrue(list.Contains(in allocator, new Vector3(i, i, i))); 82 | 83 | } 84 | 85 | allocator.Dispose(); 86 | 87 | } 88 | 89 | [Test] 90 | public void Pop() { 91 | 92 | var allocator = Core_Tests.GetAllocator(10); 93 | 94 | var list = new Stack(ref allocator, 10); 95 | for (int i = 0; i < 100; ++i) { 96 | 97 | list.Push(ref allocator, new Vector3Int(i, i, i)); 98 | 99 | } 100 | 101 | Assert.IsTrue(list.Count == 100); 102 | Assert.IsTrue(list.Pop(in allocator) == new Vector3Int(99, 99, 99)); 103 | Assert.IsTrue(list.Pop(in allocator) == new Vector3Int(98, 98, 98)); 104 | Assert.IsTrue(list.Count == 98); 105 | 106 | allocator.Dispose(); 107 | 108 | } 109 | 110 | [Test] 111 | public void Peek() { 112 | 113 | var allocator = Core_Tests.GetAllocator(10); 114 | 115 | var list = new Stack(ref allocator, 10); 116 | for (int i = 0; i < 100; ++i) { 117 | 118 | list.Push(ref allocator, new Vector3Int(i, i, i)); 119 | 120 | } 121 | 122 | Assert.IsTrue(list.Count == 100); 123 | Assert.IsTrue(list.Peek(in allocator) == new Vector3Int(99, 99, 99)); 124 | Assert.IsTrue(list.Pop(in allocator) == new Vector3Int(99, 99, 99)); 125 | Assert.IsTrue(list.Count == 99); 126 | 127 | allocator.Dispose(); 128 | 129 | } 130 | 131 | [Test] 132 | public void PushPop() { 133 | 134 | var allocator = Core_Tests.GetAllocator(10); 135 | 136 | var list = new Stack(ref allocator, 10); 137 | for (int i = 0; i < 100; ++i) { 138 | 139 | list.Push(ref allocator, new Vector3Int(i, i, i)); 140 | 141 | } 142 | 143 | Assert.IsTrue(list.Count == 100); 144 | 145 | for (int i = 0; i < 100; ++i) { 146 | 147 | Assert.IsTrue(list.Pop(in allocator) == new Vector3Int(100 - i - 1, 100 - i - 1, 100 - i - 1)); 148 | 149 | } 150 | 151 | Assert.IsTrue(list.Count == 0); 152 | 153 | allocator.Dispose(); 154 | 155 | } 156 | 157 | [Test] 158 | public void Clear() { 159 | 160 | var allocator = Core_Tests.GetAllocator(10); 161 | 162 | var list = new Stack(ref allocator, 10); 163 | for (int i = 0; i < 100; ++i) { 164 | 165 | list.Push(ref allocator, new Vector3(i, i, i)); 166 | 167 | } 168 | 169 | Assert.IsTrue(list.Count == 100); 170 | list.Clear(in allocator); 171 | Assert.IsTrue(list.Count == 0); 172 | 173 | allocator.Dispose(); 174 | 175 | } 176 | 177 | } 178 | 179 | } -------------------------------------------------------------------------------- /MemoryAllocator/Tests/Stack.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: dd0ab4418d464593bf88796e9642f10b 3 | timeCreated: 1659026196 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ME.MemoryAllocator 2 | 3 | Custom memory allocator with CopyFrom method and basic collections like a List, Dictionary, Queue, Stack, SparseSet. 4 | 5 | 1. Very fast malloc/free 6 | 2. Unmanaged 7 | 3. Immediate serialization/deserialization 8 | 4. You can store collections inside collections 9 | 5. Works with Unity Burst 10 | 6. Can be copied/cloned very fast 11 | 7. Custom memory pointers 12 | 8. Standard collections implemented 13 | 14 | Documentation: https://github.com/chromealex/ecs/blob/master/Docs/MemoryAllocator.md 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.me.memoryallocator", 3 | "version": "1.0.0", 4 | "displayName": "ME.MemoryAllocator", 5 | "description": "ME.MemoryAllocator - custom memory allocator with CopyFrom method and basic collections like a List, Dictionary, Queue, Stack, SparseSet.", 6 | "unity": "2020.3", 7 | "documentationUrl": "https://github.com/chromealex/ecs/blob/master/README.md", 8 | "dependencies": { 9 | "com.unity.burst": "1.5.6" 10 | }, 11 | "scopedRegistries": [ 12 | { 13 | "name": "General", 14 | "url": "https://github.com/chromealex/ecs-submodule.git", 15 | "scopes": [ 16 | "com.me.ecs" 17 | ] 18 | } 19 | ], 20 | "keywords": [ 21 | "csharp", 22 | "memory", 23 | "mamoryallocator", 24 | "allocator", 25 | "collections" 26 | ], 27 | "author": { 28 | "name": "Alex Silaev", 29 | "email": "chrome.alex@gmail.com" 30 | } 31 | } --------------------------------------------------------------------------------