1 module nxt.container.fixed_dynamic_array;
2 
3 import std.experimental.allocator.mallocator : Mallocator;
4 import std.experimental.allocator.common : isAllocator;
5 
6 /** Dynamically allocated (heap) array with fixed length.
7 	TODO: Move as many of members as possible to `FixedArrayStore`.
8  */
9 struct FixedDynamicArray(T, Allocator = Mallocator)
10 if (isAllocator!Allocator) {
11 @safe:
12 	import core.exception : onOutOfMemoryError;
13 	import core.memory : pureMalloc, pureFree; /+ TODO: replace with `Allocator` +/
14 	import std.traits : hasFunctionAttributes;
15 
16 pragma(inline, true):
17 
18 	/** Make and return uninitialized array of `length`.
19 	 *
20 	 * Unlike `@trusted pureMalloc` this must be `@system` because the return
21 	 * value of this factory function can be accessed in @safe code.
22 	 */
23 	static typeof(this) makeUninitializedOfLength(size_t length) @system {
24 		version (DigitalMars) pragma(inline, false); // DMD cannot inline
25 		auto ptr = pureMalloc(length * T.sizeof);
26 		if (ptr is null && length >= 1)
27 			onOutOfMemoryError();
28 		return typeof(return)(FixedArrayStore(length, cast(T*)ptr));
29 	}
30 
31 	/// Construct from `store`.
32 	private this(FixedArrayStore store) {
33 		_store = store;
34 	}
35 
36 	/// Construct uninitialized array of `length`.
37 	private this(in size_t length) @system {
38 		_store.length = length;
39 		auto ptr = pureMalloc(length * T.sizeof);
40 		if (ptr is null &&
41 			length >= 1)
42 			onOutOfMemoryError();
43 		_store.ptr = cast(T*)ptr;
44 	}
45 
46 	/// Destruct.
47 	~this() nothrow @trusted @nogc {
48 		pureFree(_store.ptr);
49 	}
50 
51 	// disable copying
52 	this(this) @disable;
53 
54 	/// Get element at index `i`.
55 	ref inout(T) opIndex(size_t i) inout @trusted return scope
56 		=> (*(cast(inout(T)[]*)&_store))[i];
57 
58 	/// Slice support.
59 	inout(T)[] opSlice(size_t i, size_t j) inout @trusted return scope
60 		=> (*(cast(inout(T)[]*)&_store))[i .. j];
61 	/// ditto
62 	inout(T)[] opSlice() inout @trusted return scope
63 		=> (*(cast(inout(T)[]*)&_store))[0 .. _store.length];
64 
65 	/// Slice assignment support.
66 	T[] opSliceAssign(U)(U value) return scope
67 		=> (*(cast(inout(T)[]*)&_store))[0 .. _store.length] = value;
68 	/// ditto
69 	T[] opSliceAssign(U)(U value, size_t i, size_t j) return scope
70 		=> (*(cast(inout(T)[]*)&_store))[i .. j] = value;
71 
72 private:
73 	static struct FixedArrayStore { /+ TODO: move to `array_store.FixedArrayStore` +/
74 		size_t length;
75 		static if (hasFunctionAttributes!(Allocator.allocate, "@nogc")) {
76 			import nxt.gc_traits : NoGc;
77 			@NoGc T* ptr;	   // non-GC-allocated
78 		} else
79 			T* ptr;			 // GC-allocated
80 	}
81 	FixedArrayStore _store;
82 }
83 
84 @safe pure nothrow @nogc unittest {
85 	alias A = FixedDynamicArray!(int);
86 	auto y = A();
87 	// assert(y.length = 0);
88 }
89 
90 @trusted pure nothrow @nogc unittest {
91 	auto x = FixedDynamicArray!(int).makeUninitializedOfLength(7);
92 	x[0] = 11;
93 	assert(x[0] == 11);
94 }
95 
96 version (unittest) {
97 	import nxt.array_help : s;
98 }