1 module nulib.collections.internal.marray;
2 import numem.core.memory : nu_is_overlapping;
3 import numem.core.hooks;
4 import numem.core.traits;
5 import numem;
6 
7 /**
8     A managed array with utilities for range manipulation
9 */
10 struct ManagedArray(T, bool ownsMemory = true) {
11     alias memory this;
12 
13     // Backing slice of the vector.
14     T[] memory;
15 
16     // Memory capacity of the slice.
17     size_t capacity = 0;
18 
19     // Resizing algorithm
20     pragma(inline, true)
21     void resize(size_t length) @trusted {
22         
23         // Early exit.
24         if (length == memory.length)
25             return;
26 
27         // Reserve if over-allocating for the current
28         // size.
29         if (length > capacity)
30             this.reserve(length);
31 
32         // If the new length is lower than the prior length
33         // Destroy the prior allocated objects.
34         if (length < memory.length) {
35             this.deleteRange(memory.ptr[length..memory.length]);
36         }
37 
38         // If it's bigger, initialize the memory in the new range.
39         if (length > memory.length) {
40         
41             // Initialize the newly prepared memory by
42             // bliting T.init to it.
43             nogc_initialize(memory.ptr[memory.length..length]);
44         }
45 
46         memory = memory.ptr[0..length];
47     }
48 
49     // Reservation algorithm
50     pragma(inline, true)
51     void reserve(size_t length) @trusted {
52         if (length == 0) {
53             memory = memory.nu_resize(0);
54             this.capacity = 0;
55             return;
56         }
57 
58         // Resize down if neccesary.
59         size_t sliceSize = memory.length;
60         if (sliceSize > length) {
61             sliceSize = length;
62             this.reserve(sliceSize);
63         }
64 
65         memory = memory.nu_resize(length)[0..sliceSize];
66         this.capacity = length;
67     }
68 
69     // Range deletion algorithm.
70     pragma(inline, true)
71     void deleteRange(U)(U[] range) if (is(Unconst!U == Unconst!T)) {
72         static if (ownsMemory) {
73             nogc_delete(range);
74         } else {
75             nogc_initialize(range);
76         }
77 
78         // Handle array rearrangement.
79         ptrdiff_t startIdx = cast(ptrdiff_t)(range.ptr - memory.ptr);
80         ptrdiff_t endIdx = startIdx+range.length;
81         size_t leftoverCount = (memory.length-endIdx);
82         if (startIdx >= 0) {
83             
84             // Shift old memory in
85             if (endIdx < memory.length) {
86                 nu_memmove(cast(void*)&memory[startIdx], cast(void*)&memory[endIdx], leftoverCount*T.sizeof);
87             }
88 
89             // Hide the now invalid memory.
90             memory = memory[0..startIdx+leftoverCount];
91         }
92     }
93 
94     // Checks whether src or dst is moving into our memory slice.
95     // This is used in moveRange to allow for overlapping moves.
96     pragma(inline, true)
97     bool isMovingIntoSelf(U)(U[] dst, U[] src) if (is(Unconst!U == Unconst!T)) {
98         return 
99             nu_is_overlapping(cast(void*)memory.ptr, memory.length*T.sizeof, cast(void*)dst.ptr, dst.length*T.sizeof) && 
100             nu_is_overlapping(cast(void*)memory.ptr, memory.length*T.sizeof, cast(void*)src.ptr, src.length*T.sizeof);
101     }
102 
103     // Checks whether src or dst is moving into our memory slice.
104     // This is used in moveRange to allow for overlapping moves.
105     pragma(inline, true)
106     bool isMovingIntoSelf(U)(U[] src) if (is(Unconst!U == Unconst!T)) {
107         return 
108             nu_is_overlapping(cast(void*)memory.ptr, memory.length*T.sizeof, cast(void*)src.ptr, src.length*T.sizeof);
109     }
110 
111     // Range move algorithm.
112     pragma(inline, true)
113     void moveRange(U)(U[] dst, U[] src) if (is(Unconst!U == Unconst!T)) {
114 
115         // Handle overlapping moves.
116         if (isMovingIntoSelf(dst, src)) {
117             src = rangeCopy(src);
118             static if (hasElaborateMove!T) {
119                 nogc_move(dst, src);
120             } else {
121                 nogc_copy(dst, src);
122             }
123             src = src.nu_resize(0);
124             return;
125         }
126 
127         // Non-overlapping moves.
128         static if (hasElaborateMove!T) {
129             nogc_move(dst, src);
130         } else {
131             nogc_copy(dst, src);
132         }
133     }
134 
135     // Take ownership of memory store.
136     pragma(inline, true)
137     T[] take() {
138         auto mem = memory;
139         this.memory = null;
140         return mem;
141     }
142 
143     // Reverses the contents of the array
144     pragma(inline, true)
145     void reverse() {
146         alias U = Unconst!(T)[];
147 
148         if (memory.length == 0)
149             return;
150         
151         auto mmemory = cast(U)memory;
152         foreach(i; 0..memory.length/2) {
153             auto a = memory[i];
154             auto b = memory[$-(i+1)];
155 
156             mmemory[i] = b;
157             mmemory[$-(i+1)] = a;
158         }
159     }
160 
161     // Flips the endianness of the elements within the array.
162     pragma(inline, true)
163     void flipEndian() {
164         alias U = Unconst!(T);
165         alias UX = ubyte[U.sizeof];
166 
167         static if (U.sizeof > 1) {
168             
169             import nulib.memory.endian : nu_etoh, ALT_ENDIAN;
170             cast(void)nu_etoh!(UX, ALT_ENDIAN)(cast(UX[])memory[0..$]);
171         }
172     }
173 }
174 
175 // Checks whether 2 memory ranges overlap.
176 pragma(inline, true)
177 bool isOverlapping(T, U)(T[] dst, U[] src) if (is(Unconst!U == Unconst!T)) {
178     return 
179         nu_is_overlapping(cast(void*)dst.ptr, dst.length*T.sizeof, cast(void*)src.ptr, src.length*T.sizeof);
180 }
181 
182 // Reverses the contents of the array
183 pragma(inline, true)
184 void arrReverse(T)(T[] memory) {
185     alias U = Unconst!(T)[];
186 
187     if (memory.length == 0)
188         return;
189 
190     U tmp;
191     foreach(i; 0..memory.length/2) {
192         size_t lhs = i;
193         size_t rhs = memory.length-i;
194 
195         tmp = (cast(U[])memory)[lhs];
196         (cast(U[])memory)[lhs] = (cast(U[])memory)[rhs];
197         (cast(U[])memory)[rhs] = tmp;
198     }
199 }
200 
201 // Flips the endianness of the elements within the array.
202 pragma(inline, true)
203 void arrFlipEndian(T)(T[] memory) {
204     static if (T.sizeof > 1) {
205         alias U = Unconst!(T)[];
206         import nulib.memory.endian : nu_etoh, ALT_ENDIAN;
207     
208         cast(void)nu_etoh!(U[], ALT_ENDIAN)(cast(U[])memory);
209     }
210 }
211 
212 // Range copy for moves internally.
213 pragma(inline, true)
214 T[] rangeCopy(T)(T[] src) {
215     alias UT = Unconst!(T)[];
216 
217     UT[] dst;
218     dst = dst.nu_resize(src.length);
219     static if (hasElaborateMove!T) {
220         nogc_move(cast(UT)dst[0..$], cast(UT)src[0..$]);
221     } else {
222         nogc_copy(cast(UT)dst[0..$], cast(UT)src[0..$]);
223     }
224     
225     return cast(T[])dst;
226 }