1 /// Copyright: Copyright (c) 2017-2020 Andrey Penechko.
2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
3 /// Authors: Andrey Penechko.
4 
5 module vox.ir.ir_small_array;
6 
7 import std.bitmanip : bitfields;
8 import std.format : formattedWrite;
9 import vox.all;
10 
11 /// Stores 0-2 items inline
12 /// Allocates memory from IrFuncStorage.arrayBuffer for arrays with > 2 slots
13 struct IrSmallArray
14 {
15 	union {
16 		IrIndex[2] items; // .kind != IrValueKind.array
17 		struct {
18 			// .kind == IrValueKind.array
19 			// offset into IrFuncStorage.arrayBuffer
20 			IrIndex arrayIndex;
21 			mixin(bitfields!(
22 				// Number of items inside big array
23 				uint, "arrayLength",   23,
24 				// capacity is bigCapacity
25 				uint, "capacityPower",  5,
26 				// 4 bits need to be clear, so IrIndex.kind == IrvalueKind.none
27 				// important for automatic index fixing after moving
28 				// when switching from built-in items to external array
29 				// item[1] must cleared
30 				uint, "", 4
31 			));
32 		}
33 	}
34 
35 	private uint bigCapacity() { return 1 << capacityPower; }
36 
37 	uint capacity()
38 	{
39 		if (isBig) return bigCapacity;
40 		else return 2;
41 	}
42 
43 	uint length()
44 	{
45 		if (items[0].kind == IrValueKind.array)
46 			return arrayLength;
47 		else if (items[1].isDefined)
48 			return 2;
49 		else if (items[0].isDefined)
50 			return 1;
51 		else
52 			return 0;
53 	}
54 
55 	bool empty() { return length == 0; }
56 
57 	void free(IrFunction* ir)
58 	{
59 		if (isBig)
60 		{
61 			ir.freeIrArray(arrayIndex, bigCapacity);
62 		}
63 		items[0] = IrIndex();
64 		items[1] = IrIndex();
65 	}
66 
67 	bool isBig()
68 	{
69 		return items[0].kind == IrValueKind.array;
70 	}
71 
72 	alias opCall = range;
73 	IrSmallArrayIterator range(IrFunction* ir) return
74 	{
75 		return IrSmallArrayIterator(&this, ir);
76 	}
77 
78 	void replaceAll(IrFunction* ir, IrIndex what, IrIndex byWhat)
79 	{
80 		foreach (ref IrIndex item; range(ir))
81 		{
82 			if (item == what) item = byWhat;
83 		}
84 	}
85 
86 	bool contains(IrFunction* ir, IrIndex what)
87 	{
88 		foreach (IrIndex item; range(ir))
89 		{
90 			if (item == what) return true;
91 		}
92 		return false;
93 	}
94 
95 	/// Returns true if replacement was performed
96 	bool replaceFirst(IrFunction* ir, IrIndex what, IrIndex byWhat)
97 	{
98 		foreach (ref IrIndex item; range(ir))
99 		{
100 			if (item == what) {
101 				item = byWhat;
102 				return true;
103 			}
104 		}
105 		return false;
106 	}
107 
108 	// asserts if not found
109 	uint findFirst(IrFunction* ir, IrIndex what)
110 	{
111 		//writefln("findFirst %s %s", what, data(ir));
112 		foreach (size_t index, ref IrIndex item; range(ir))
113 		{
114 			if (item == what) return cast(uint)index;
115 		}
116 		assert(false, "Item not found");
117 	}
118 
119 	// returns uint.max if not found
120 	uint tryFindFirst(IrFunction* ir, IrIndex what)
121 	{
122 		foreach (size_t index, ref IrIndex item; range(ir))
123 		{
124 			if (item == what) return cast(uint)index;
125 		}
126 		return uint.max;
127 	}
128 
129 	/// Removes all occurences of what
130 	/// Preserves order of items
131 	uint removeStable(IrFunction* ir, IrIndex what)
132 	{
133 		if (isBig) // external array
134 		{
135 			uint numRemoved = 0;
136 			IrIndex* array = ir.getArray(arrayIndex);
137 
138 			foreach(i, ref IrIndex item; array[0..arrayLength])
139 			{
140 				if (item == what)
141 					++numRemoved;
142 				else
143 					array[i - numRemoved] = item;
144 			}
145 			arrayLength = arrayLength - numRemoved;
146 			afterRemoveBig(ir, array); // free array if needed
147 			return numRemoved;
148 		}
149 		else return removeSmall(ir, what); // 0, 1, 2
150 	}
151 
152 	/// Removes all occurences of what
153 	/// Alters order of items
154 	uint removeUnstable(IrFunction* ir, IrIndex what)
155 	{
156 		if (isBig) // external array
157 		{
158 			uint tempLength = arrayLength;
159 			IrIndex* array = ir.getArray(arrayIndex);
160 
161 			uint i = 0;
162 			while (i < tempLength)
163 			{
164 				if (array[i] == what) {
165 					--tempLength;
166 					array[i] = array[tempLength];
167 				}
168 				else
169 					++i;
170 			}
171 			uint numRemoved = arrayLength - tempLength;
172 			arrayLength = tempLength;
173 			afterRemoveBig(ir, array); // free array if needed
174 			return numRemoved;
175 		}
176 		else return removeSmall(ir, what); // 0, 1, 2
177 	}
178 
179 	private uint removeSmall(IrFunction* ir, IrIndex what)
180 	{
181 		// no harm if what or items[0/1] is null here
182 		if (items[0] == what) {
183 			// remove first item in-place
184 			if (items[1] == what) {
185 				// and second too
186 				items[0] = IrIndex();
187 				items[1] = IrIndex();
188 				return 2;
189 			} else {
190 				// only first
191 				items[0] = items[1];
192 				items[1] = IrIndex();
193 				return 1;
194 			}
195 		} else if (items[1] == what) {
196 			// remove second item in-place
197 			items[1] = IrIndex();
198 			return 1;
199 		}
200 		return 0;
201 	}
202 
203 	private void afterRemoveBig(IrFunction* ir, IrIndex* array)
204 	{
205 		switch (arrayLength)
206 		{
207 			case 0:
208 				IrIndex arrayIndexCopy = arrayIndex;
209 				ir.freeIrArray(arrayIndex, bigCapacity);
210 				items[0] = IrIndex();
211 				items[1] = IrIndex();
212 				break;
213 			case 1:
214 				IrIndex arrayIndexCopy = arrayIndex;
215 				uint _capacity = bigCapacity;
216 				items[0] = array[0];
217 				items[1] = IrIndex();
218 				ir.freeIrArray(arrayIndexCopy, _capacity);
219 				break;
220 			case 2:
221 				IrIndex arrayIndexCopy = arrayIndex;
222 				uint _capacity = bigCapacity;
223 				items[0] = array[0];
224 				items[1] = array[1];
225 				ir.freeIrArray(arrayIndexCopy, _capacity);
226 				break;
227 			default: break;
228 		}
229 	}
230 
231 	IrIndex[] data(IrFunction* ir) return
232 	{
233 		if (isBig)
234 		{
235 			IrIndex* array = ir.getArray(arrayIndex);
236 			return array[0..arrayLength];
237 		}
238 		else
239 		{
240 			if (items[1].isDefined)
241 			{
242 				return items[0..2];
243 			}
244 			else if (items[0].isDefined)
245 			{
246 				return items[0..1];
247 			}
248 			else
249 			{
250 				return null;
251 			}
252 		}
253 	}
254 
255 	ref IrIndex opIndex(size_t index, IrFunction* ir) return
256 	{
257 		if (isBig)
258 		{
259 			IrIndex* array = ir.getArray(arrayIndex);
260 			assert(index < arrayLength);
261 			return array[index];
262 		}
263 		else
264 		{
265 			switch(index)
266 			{
267 				case 0:
268 					assert(items[0].isDefined, "Accessing index 0, when length is 0");
269 					return items[0];
270 				case 1:
271 					assert(items[0].isDefined, "Accessing index 1, when length is < 2");
272 					return items[1];
273 				default:
274 					assert(false);
275 			}
276 		}
277 	}
278 
279 	void append(IrBuilder* builder, IrIndex itemData)
280 	{
281 		assert(itemData.kind != IrValueKind.array, "array is not storable inside IrSmallArray");
282 		assert(itemData.kind != IrValueKind.none, "IrValueKind.none is not storable inside IrSmallArray");
283 		if (isBig)
284 		{
285 			uint _capacity = bigCapacity;
286 			IrIndex* array = builder.ir.getArray(arrayIndex);
287 			if (arrayLength < _capacity)
288 			{
289 				//writefln("append cap %s len %s %s", _capacity, arrayLength, array[0..arrayLength]);
290 				array[arrayLength] = itemData;
291 				arrayLength = arrayLength + 1;
292 			}
293 			else
294 			{
295 				//writefln("append cap %s %s len %s extend %s %s", capacityPower, _capacity, arrayLength, arrayIndex, array[0..arrayLength]);
296 
297 				if (builder.tryExtendArray(arrayIndex, _capacity))
298 				{
299 					// extend was performed in-place
300 					array[arrayLength] = itemData;
301 
302 					// update instance
303 					arrayLength = arrayLength + 1;
304 					capacityPower = capacityPower + 1;
305 				}
306 				else
307 				{
308 					// allocate twice bigger array
309 					uint newCapacity = _capacity * 2;
310 					IrIndex newArrayIndex = builder.allocateIrArray(newCapacity);
311 					//writefln("  alloc cap %s %s", newCapacity, newArrayIndex);
312 					IrIndex* newArray = builder.ir.getArray(newArrayIndex);
313 					// copy old data
314 					newArray[0..arrayLength] = array[0..arrayLength];
315 					// add new item
316 					newArray[arrayLength] = itemData;
317 					// free old array
318 					builder.ir.freeIrArray(arrayIndex, _capacity);
319 
320 					// update instance
321 					arrayLength = arrayLength + 1;
322 					capacityPower = capacityPower + 1;
323 					arrayIndex = newArrayIndex;
324 				}
325 
326 				//writefln("  len %s cap %s %s index %s", arrayLength, capacityPower, 1 << capacityPower, arrayIndex);
327 			}
328 		}
329 		else
330 		{
331 			if (!items[0].isDefined)
332 			{
333 				items[0] = itemData;
334 			}
335 			else if (!items[1].isDefined)
336 			{
337 				items[1] = itemData;
338 			}
339 			else
340 			{
341 				IrIndex newArrayIndex = builder.allocateIrArray(4);
342 				IrIndex* newArray = builder.ir.getArray(newArrayIndex);
343 				newArray[0] = items[0];
344 				newArray[1] = items[1];
345 				newArray[2] = itemData;
346 
347 				// update instance
348 				items[1] = IrIndex(); // clear bitfields
349 				arrayIndex = newArrayIndex;
350 				capacityPower = 2; // 1 << 2 == 4
351 				arrayLength = 3;
352 				//writefln("  len %s cap %s %s index %s", arrayLength, capacityPower, 1 << capacityPower, arrayIndex);
353 			}
354 		}
355 	}
356 
357 	void toString(scope void delegate(const(char)[]) sink)
358 	{
359 		if (isBig)
360 			sink.formattedWrite("[%s items]", arrayLength);
361 		else if (items[1].isDefined)
362 			sink.formattedWrite("[%s, %s]", items[0], items[1]);
363 		else if (items[0].isDefined)
364 			sink.formattedWrite("[%s]", items[0]);
365 		else sink("[]");
366 	}
367 }
368 
369 unittest
370 {
371 	import std.stdio;
372 	ubyte[1024] irBuf, tempBuf;
373 	IrFunction ir;
374 	CompilationContext context;
375 	IrBuilder builder;
376 
377 	context.irStorage.arrayBuffer.setBuffer(irBuf[], 1024);
378 	context.tempBuffer.setBuffer(tempBuf[], 1024);
379 
380 	builder.context = &context;
381 	builder.ir = &ir;
382 	ir.arrayPtr = context.irStorage.arrayBuffer.nextPtr;
383 
384 	IrSmallArray vec;
385 
386 	assert(vec.length == 0);
387 	assert(!vec.isBig);
388 
389 	vec.append(&builder, IrIndex(1, IrValueKind.instruction));
390 	assert(vec.length == 1);
391 	assert(!vec.isBig);
392 
393 	vec.append(&builder, IrIndex(2, IrValueKind.instruction));
394 	assert(vec.length == 2);
395 	assert(!vec.isBig);
396 
397 	vec.append(&builder, IrIndex(3, IrValueKind.instruction));
398 	assert(vec.length == 3);
399 	assert(vec.isBig);
400 
401 	vec.append(&builder, IrIndex(4, IrValueKind.instruction));
402 	assert(vec.length == 4);
403 	assert(vec.isBig);
404 }
405 
406 struct IrSmallArrayIterator
407 {
408 	IrSmallArray* array;
409 	IrFunction* ir;
410 
411 	int opApply(scope int delegate(size_t, ref IrIndex) dg)
412 	{
413 		return opApplyImpl!2(dg);
414 	}
415 
416 	int opApply(scope int delegate(ref IrIndex) dg)
417 	{
418 		return opApplyImpl!1(dg);
419 	}
420 
421 	int opApplyImpl(uint size, Del)(scope Del dg)
422 	{
423 		if (array.isBig) // external array
424 		{
425 			IrIndex* data = ir.getArray(array.arrayIndex);
426 			uint length = array.arrayLength;
427 			foreach(size_t index, ref IrIndex item; data[0..length])
428 			{
429 				static if (size == 2) {
430 					if (int res = dg(index, item)) return res;
431 				} else {
432 					if (int res = dg(item)) return res;
433 				}
434 			}
435 		}
436 		else // 0, 1, 2
437 		{
438 			if (!array.items[0].isDefined) return 0; // length 0
439 
440 			static if (size == 2) {
441 				if (int res = dg(0, array.items[0])) return res; // length 1
442 				if (array.items[1].isDefined)
443 				{
444 					if (int res = dg(1, array.items[1])) return res; // length 2
445 				}
446 			} else {
447 				if (int res = dg(array.items[0])) return res; // length 1
448 				if (array.items[1].isDefined)
449 				{
450 					if (int res = dg(array.items[1])) return res; // length 2
451 				}
452 			}
453 		}
454 		return 0;
455 	}
456 }