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