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 }