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     import nxt.container_traits : mustAddGCRange;
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     pragma(inline)              // DMD cannot inline
24     static typeof(this) makeUninitializedOfLength(size_t length) @system
25     {
26         auto ptr = pureMalloc(length * T.sizeof);
27         if (ptr is null && length >= 1)
28         {
29             onOutOfMemoryError();
30         }
31         return typeof(return)(Store(length, cast(T*)ptr));
32     }
33 
34     pragma(inline)              // DMD cannot inline
35     static typeof(this) withLength(size_t length) @system
36     {
37         auto ptr = pureCalloc(length, T.sizeof);
38         if (ptr is null && length >= 1)
39         {
40             onOutOfMemoryError();
41         }
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 && length >= 1)
57         {
58             onOutOfMemoryError();
59         }
60         _store.ptr = cast(T*)ptr;
61     }
62 
63     /// Destruct.
64     ~this() @trusted @nogc
65     {
66         pureFree(_store.ptr);
67     }
68 
69     // disable copying
70     @disable this(this);
71 
72     /// Get element at index `i`.
73     scope ref inout(T) opIndex(size_t i) inout @system return
74     {
75         return _store.ptr[i];
76     }
77 
78     /// Slice support.
79     scope inout(T)[] opSlice(size_t i, size_t j) inout @system return
80     {
81         return _store.ptr[i .. j];
82     }
83     /// ditto
84     scope inout(T)[] opSlice() inout @system return
85     {
86         return _store.ptr[0 .. _store.length];
87     }
88 
89     /// Slice assignment support.
90     scope T[] opSliceAssign(U)(U value) return
91     {
92         return _store.ptr[0 .. _store.length] = value;
93     }
94     /// ditto
95     scope T[] opSliceAssign(U)(U value, size_t i, size_t j) return
96     {
97         return _store.ptr[i .. j] = value;
98     }
99 
100 private:
101     static struct Store
102     {
103         size_t length;
104         import nxt.gc_traits : NoGc;
105         @NoGc T* ptr;           // non-GC-allocated store pointer
106     }
107     Store _store;
108 }
109 
110 @system pure nothrow @nogc unittest
111 {
112     auto x = FixedDynamicArray!(int).makeUninitializedOfLength(7);
113     x[0] = 11;
114     assert(x[0] == 11);
115 
116     auto y = FixedDynamicArray!(int).withLength(3);
117     y[0] = 11;
118     assert(y[] == [11, 0, 0].s);
119 }
120 
121 version(unittest)
122 {
123     import nxt.array_help : s;
124 }