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 }