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