1 /** 2 * Copyright: Enalye 3 * License: Zlib 4 * Authors: Enalye 5 */ 6 module grimoire.runtime.indexedarray; 7 8 import std.parallelism; 9 import std.range; 10 11 /** 12 Defragmenting array with referencable value by index. 13 14 For optimisation purposes, the index returned by the foreach statement 15 is the internal one : \ 16 - Do not attempt to use this index for anything other than calling the 17 markInternalForRemoval function. 18 */ 19 class DynamicIndexedArray(T) { 20 alias InternalIndex = size_t; 21 private { 22 uint _capacity = 32u; 23 uint _dataTop = 0u; 24 uint _availableIndexesTop = 0u; 25 uint _removeTop = 0u; 26 27 T[] _dataTable; 28 uint[] _availableIndexes; 29 uint[] _translationTable; 30 uint[] _reverseTranslationTable; 31 uint[] _removeTable; 32 } 33 34 @property { 35 /// Number of items in the list. 36 uint length() const { return _dataTop; } 37 /// Current max. 38 uint capacity() const { return _capacity; } 39 /// The array itself. 40 /// Avoid changing positions/size/etc. 41 T[] data() { return _dataTable; } 42 } 43 44 /// Ctor 45 this() { 46 _dataTable.length = _capacity; 47 _availableIndexes.length = _capacity; 48 _translationTable.length = _capacity; 49 _reverseTranslationTable.length = _capacity; 50 _removeTable.length = _capacity; 51 } 52 53 /** 54 Add a new item on the list. 55 Returns: The index of the object. 56 ___ 57 This index will never change and will remain valid 58 as long as the object is not removed from the list. 59 */ 60 uint push(T value) { 61 uint index; 62 63 if((_dataTop + 1u) == _capacity) { 64 doubleCapacity(); 65 } 66 67 if(_availableIndexesTop) { 68 //Take out the last available index on the list. 69 _availableIndexesTop--; 70 index = _availableIndexes[_availableIndexesTop]; 71 } 72 else { 73 //Or use a new id. 74 index = _dataTop; 75 } 76 77 //Add the value to the data stack. 78 _dataTable[_dataTop] = value; 79 _translationTable[index] = _dataTop; 80 _reverseTranslationTable[_dataTop] = index; 81 82 ++_dataTop; 83 84 return index; 85 } 86 87 /// Immediatly remove a value from the list. \ 88 /// Use the index returned by `push`. 89 void pop(uint index) { 90 uint valueIndex = _translationTable[index]; 91 92 //Push the index on the available indexes stack. 93 _availableIndexes[_availableIndexesTop] = index; 94 _availableIndexesTop++; 95 96 //Invalidate the index. 97 _translationTable[index] = -1; 98 99 //Take the top value on the stack and fill the gap. 100 _dataTop--; 101 if (valueIndex < _dataTop) { 102 uint userIndex = _reverseTranslationTable[_dataTop]; 103 _dataTable[valueIndex] = _dataTable[_dataTop]; 104 _translationTable[userIndex] = valueIndex; 105 _reverseTranslationTable[valueIndex] = userIndex; 106 } 107 } 108 109 /// Empty the list. 110 void reset() { 111 _dataTop = 0u; 112 _availableIndexesTop = 0u; 113 _removeTop = 0u; 114 } 115 116 /// The value will be removed with the next `sweepMarkedData`. \ 117 /// Use the index given by the for loop. 118 void markInternalForRemoval(InternalIndex index) { 119 synchronized { 120 _removeTable[_removeTop] = _reverseTranslationTable[index]; 121 _removeTop ++; 122 } 123 } 124 125 /// The value will be removed with the next `sweepMarkedData`. \ 126 /// Use the index returned by `push`. 127 void markForRemoval(uint index) { 128 _removeTable[_removeTop] = index; 129 _removeTop ++; 130 } 131 132 /// Marked values will be removed from the list. \ 133 /// Call this function **outside** of the loop that iterate over this list. 134 void sweepMarkedData() { 135 for(uint i = 0u; i < _removeTop; i++) { 136 pop(_removeTable[i]); 137 } 138 _removeTop = 0u; 139 } 140 141 /// = operator 142 int opApply(int delegate(ref T) dlg) { 143 int result; 144 145 foreach(i; 0u .. _dataTop) { 146 result = dlg(_dataTable[i]); 147 148 if(result) 149 break; 150 } 151 152 return result; 153 } 154 155 /// Ditto 156 int opApply(int delegate(const ref T) dlg) const { 157 int result; 158 159 foreach(i; 0u .. _dataTop) { 160 result = dlg(_dataTable[i]); 161 162 if(result) 163 break; 164 } 165 166 return result; 167 } 168 169 /// Ditto 170 int opApply(int delegate(ref T, InternalIndex) dlg) { 171 int result; 172 173 foreach(i; 0u .. _dataTop) { 174 result = dlg(_dataTable[i], i); 175 176 if(result) 177 break; 178 } 179 180 return result; 181 } 182 183 /// Ditto 184 int opApply(int delegate(const ref T, InternalIndex) dlg) const { 185 int result; 186 187 foreach(i; 0u .. _dataTop) { 188 result = dlg(_dataTable[i], i); 189 190 if(result) 191 break; 192 } 193 194 return result; 195 } 196 197 /// [] operator 198 T opIndex(uint index) { 199 return _dataTable[_translationTable[index]]; 200 } 201 202 private void doubleCapacity() { 203 _capacity <<= 1; 204 _dataTable.length = _capacity; 205 _availableIndexes.length = _capacity; 206 _translationTable.length = _capacity; 207 _reverseTranslationTable.length = _capacity; 208 _removeTable.length = _capacity; 209 } 210 }