1 /// Author: Aziz Köksal 2 /// License: GPL3 3 /// $(Maturity low) 4 module dil.Array; 5 6 import core.stdc.stdlib, 7 core.memory, 8 core.exception; 9 import std.traits : hasIndirections; 10 import common; 11 12 /// The system size of a memory page. Default to 4k. 13 static size_t PAGESIZE = 4096; 14 15 extern (C) void* memcpy(void*, const void*, size_t); 16 extern (C) void onOutOfMemoryError(); 17 18 static this() 19 { 20 version(linux) 21 { 22 import core.sys.posix.unistd; 23 PAGESIZE = cast(size_t)sysconf(_SC_PAGE_SIZE); 24 } 25 } 26 27 /// An array implementation that uses D's Garbage Collector functions. 28 struct DArray(E) 29 { 30 E* ptr; 31 E* cur; 32 E* end; 33 34 /// A block needs to be scanned by the GC, if E as pointers. 35 enum scanIfPtrs = hasIndirections!E ? 0 : GC.BlkAttr.NO_SCAN; 36 37 /// Constructs a DArray and reserves space for n elements. 38 this(size_t n = 0) 39 { 40 if (n) 41 if (auto p = cast(E*)GC.malloc(n * E.sizeof, scanIfPtrs)) 42 end = (ptr = cur = p) + n; 43 } 44 45 invariant() 46 { 47 if (!ptr) 48 assert(cur is null && end is null, "!(ptr == cur == end == 0)"); 49 else 50 assert(ptr <= cur && cur <= end, 51 Format("!({} <= {} <= {})", ptr, cur, end)); 52 } 53 54 /// Returns the size of the array. 55 size_t len() @property const 56 { 57 return cur - ptr; 58 } 59 60 /// Sets the size of the array. 61 /// Resizes space if necessary. Does not deallocate if n is zero. 62 void len(size_t n) @property 63 { 64 if ((ptr + n) > end) 65 reserve(n); 66 cur = ptr + n; 67 } 68 69 /// Returns the remaining space before a reallocation is needed. 70 size_t rem() @property const 71 { 72 return end - cur; 73 } 74 75 /// Sets the capacity so that space for n elements remain. 76 void rem(size_t n) @property 77 { 78 cap = len + n; 79 } 80 81 /// Returns the total capacity of the array. 82 size_t cap() @property const 83 { 84 return end - ptr; 85 } 86 87 /// Sets the capacity to exactly n elements. 88 void cap(size_t n) @property 89 { 90 reserve(n); 91 } 92 93 /// Reserves memory for at least n elements. 94 void reserve(size_t n) 95 { 96 auto len = this.len; 97 len = (len < n ? len : n); // min(len, n) 98 auto addBytes = (n <= cap ? 0 : n - cap) * E.sizeof; 99 if (!ptr || GC.extend(ptr, addBytes, addBytes) == 0) 100 { // Couldn't extend. Allocate new memory. 101 auto newPtr = cast(E*)GC.malloc(n * E.sizeof, scanIfPtrs); 102 if (len) // Copy to the new block if there are elements. 103 newPtr[0..len] = ptr[0..len]; 104 ptr = newPtr; 105 } 106 cur = ptr + len; 107 end = ptr + n; 108 } 109 110 /// Calls the GC to free the memory and clears this DArray. 111 void free() 112 { 113 GC.free(ptr); 114 ptr = cur = end = null; 115 } 116 117 /// Appends x to the array. 118 /// Appends the elements if X itself is an array. 119 void opOpAssign(string op : "~", X)(const X x) 120 { 121 static if (is(typeof(cur[0] == x[0]))) 122 { 123 auto n = x.length; 124 ensureOrGrow(n); 125 cur[0..n] = x; 126 cur += n; 127 } 128 else 129 { 130 ensureOrGrow(1); 131 *cur++ = x; 132 } 133 assert(cur <= end); 134 } 135 136 /// Appends several items to the array. 137 void put(Xs...)(Xs xs) 138 { 139 foreach (x; xs) 140 this ~= x; 141 } 142 143 /// Appends several items to the array, 144 /// without checking for sufficient space. 145 void putUnsafe(Xs...)(Xs xs) 146 { 147 foreach (x; xs) 148 static if (is(typeof(cur[0] == x[0]))) 149 { 150 auto n = x.length; 151 cur[0..n] = x; 152 cur += n; 153 } 154 else 155 *cur++ = x; 156 assert(cur <= end); 157 } 158 159 /// Sets: cap = next_pow_of_2(cap) 160 void growP2() 161 { 162 import core.bitop : bsr; 163 auto x = cap; 164 if (x & (x - 1)) 165 { // Round up to the next power of 2. 166 x = 2 << bsr(x); 167 if (x == 0) // Did it overflow? 168 x = size_t.max / 2 + 1; // Set the highest bit. 169 } 170 cap = x; 171 } 172 173 /// Ensure space for 'ne' number of elements or grow by 1.5. 174 void ensureOrGrow(size_t ne) 175 { 176 auto mincap = len + ne; 177 if (mincap > cap) 178 cap = (mincap << 1) - (mincap >> 1); 179 } 180 181 /// Sets: cap *= 1.5 182 void growX1_5() 183 { 184 cap = (cap << 1) - (cap >> 1); 185 } 186 187 /// Sets: cap *= 2 188 void growX2() 189 { 190 cap = cap * 2; 191 } 192 193 /// Adds x to ptr or subtracts from end. 194 inout(E)* indexPtr(T)(T x) inout 195 { 196 static if (is(T : Neg)) 197 auto p = cur - x; 198 else 199 auto p = ptr + x; 200 assert(ptr <= p && p <= cur); 201 return p; 202 } 203 204 /// Returns a slice. 205 inout(E)[] opSlice() inout 206 { 207 return ptr[0..len]; 208 } 209 /// ditto 210 alias array = opSlice; 211 212 /// Returns a slice from x to y. 213 inout(E)[] opSlice(X, Y)(X x, Y y) inout 214 { 215 auto p = indexPtr!X(x), cur = indexPtr!Y(y); 216 return p[0..cur-p]; 217 } 218 219 /// Returns the character at position x. 220 inout(E) opIndex(T)(T x) inout 221 { 222 return *indexPtr!T(x); 223 } 224 225 /// Assigns e at position x. 226 ref DArray opIndexAssign(T)(E e, T x) 227 { 228 *indexPtr!T(x) = e; 229 return this; 230 } 231 232 /// Returns a copy. 233 E[] dup() 234 { 235 return this[].dup; 236 } 237 238 /// Returns the contents and clears this DArray. 239 E[] take() 240 { 241 auto contents = this[]; 242 ptr = cur = end = null; 243 return contents; 244 } 245 } 246 247 alias CharArray = DArray!char; 248 249 /// Fast, mutable, resizable array implementation. 250 struct Array 251 { 252 alias E = ubyte; /// Alias to ubyte. Dereferencing void* gives no value. 253 E* ptr; /// Points to the start of the buffer. 254 E* cur; /// Points to the end of the contents. Only dereference if cur < end. 255 E* end; /// Points to the end of the reserved space. 256 257 /// Constructs an Array and reserves space of n bytes. 258 this(size_t n = 0) 259 { 260 if (n) 261 if (auto p = cast(E*)malloc(n)) 262 end = (ptr = cur = p) + n; 263 else 264 onOutOfMemoryError(); 265 } 266 267 invariant() 268 { 269 if (!ptr) 270 assert(cur is null && end is null, "!(ptr == cur == end == 0)"); 271 else 272 assert(ptr <= cur && cur <= end, 273 Format("!({} <= {} <= {})", ptr, cur, end)); 274 } 275 276 /// Returns the size of the Array in bytes. 277 size_t len() @property const 278 { 279 return cur - ptr; 280 } 281 282 /// Sets the size of the Array in bytes. 283 /// Resizes space if necessary. Does not deallocate if n is zero. 284 void len(size_t n) @property 285 { 286 if ((ptr + n) > end) 287 reserve(n); 288 cur = ptr + n; 289 } 290 291 /// Returns the remaining space in bytes before a reallocation is needed. 292 size_t rem() @property const 293 { 294 return end - cur; 295 } 296 297 /// Sets the capacity so that n bytes of space remain. 298 void rem(size_t n) @property 299 { 300 cap = len + n; 301 } 302 303 /// Returns the total capacity of the Array. 304 size_t cap() @property const 305 { 306 return end - ptr; 307 } 308 309 /// Sets the capacity to exactly n bytes. 310 void cap(size_t n) @property 311 { 312 reserve(n); 313 } 314 315 /// Allocates exactly n bytes. May shrink or extend in place if possible. 316 /// Does not zero out memory. Destroys if n is zero. 317 /// Throws: OutOfMemoryError. 318 void reserve(size_t n) 319 { 320 if (n == 0) 321 return destroy(); 322 auto new_ptr = ptr; 323 new_ptr = cast(E*)(new_ptr ? realloc(new_ptr, n) : malloc(n)); 324 if (!new_ptr) 325 onOutOfMemoryError(); 326 auto len = this.len; 327 ptr = new_ptr; 328 cur = new_ptr + (len < n ? len : n); // min(len, n) 329 end = new_ptr + n; 330 } 331 332 /// Frees the allocated memory. 333 void destroy() 334 { 335 free(ptr); 336 ptr = cur = end = null; 337 } 338 339 /// Grows the capacity by n or cap * 1.5. 340 void growcap(size_t n = 0) 341 { 342 if (!n) 343 n = (cap << 1) - (cap >> 1); // cap *= 1.5 344 else 345 n += cap; 346 reserve(n); 347 } 348 349 /// Shrinks the Array by n bytes. 350 void shrinkby(size_t n) 351 { 352 reserve(n < cap ? cap - n : 0); 353 } 354 355 /// Compacts the capacity to the actual length of the Array. 356 /// Destroys if the length is zero. 357 void compact() 358 { 359 reserve(len); 360 } 361 362 /// Appends x of any type to the Array. 363 /// Appends the elements if X is an array. 364 void opOpAssign(string op : "~", X)(const X x) 365 { 366 static if (is(X : Elem[], Elem)) 367 { 368 auto n = x.length * Elem.sizeof; 369 if (cur + n >= end) 370 rem = n; 371 memcpy(cur, x.ptr, n); 372 cur += n; 373 } 374 else 375 { 376 enum n = X.sizeof; 377 if (cur + n >= end) 378 rem = n; 379 static if (n <= size_t.sizeof) 380 { 381 string unroll(string s, size_t times) { 382 return times == 1 ? s : s ~ unroll(s, times-1); 383 } 384 385 auto p = cast(E*)&x, c = cur; 386 mixin(unroll("*c++ = *p++;", n)); 387 cur = c; 388 } 389 else 390 { 391 memcpy(cur, &x, n); 392 cur += n; 393 } 394 } 395 assert(cur <= end); 396 } 397 398 /// Returns a copy allocated using the GC and destroys this Array. 399 A get(A = E)() 400 { 401 auto result = this[].dup; 402 destroy(); 403 return *cast(A*)&result; 404 } 405 406 /// Returns a slice into the Array. 407 /// Warning: The memory may leak if not freed, or destroyed prematurely. 408 E[] opSlice(size_t i, size_t j) 409 { 410 assert(i <= j && j < len); 411 return ptr[i..j]; 412 } 413 414 /// ditto 415 E[] opSlice() 416 { 417 return ptr[0..len]; 418 } 419 420 /// Returns the Array as a dynamic array. 421 A elems(A = E[])() 422 { 423 return cast(A)this[]; 424 } 425 } 426 427 void testArray() 428 { 429 }