1 /** 2 * This module contains all functions related to an object's lifetime: 3 * allocation, resizing, deallocation, and finalization. 4 * 5 * Copyright: Copyright Digital Mars 2000 - 2012. 6 * License: Distributed under the 7 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 8 * (See accompanying file LICENSE) 9 * Authors: Walter Bright, Sean Kelly, Steven Schveighoffer 10 * Source: $(DRUNTIMESRC rt/_lifetime.d) 11 */ 12 13 module rt.lifetime; 14 15 import core.attribute : weak; 16 import core.internal.array.utils : __arrayStart, __arrayClearPad; 17 import core.memory; 18 debug(PRINTF) import core.stdc.stdio; 19 static import rt.tlsgc; 20 21 alias BlkInfo = GC.BlkInfo; 22 alias BlkAttr = GC.BlkAttr; 23 24 private 25 { 26 alias bool function(Object) CollectHandler; 27 __gshared CollectHandler collectHandler = null; 28 29 extern (C) void _d_monitordelete(Object h, bool det); 30 31 enum : size_t 32 { 33 PAGESIZE = 4096, 34 BIGLENGTHMASK = ~(PAGESIZE - 1), 35 SMALLPAD = 1, 36 MEDPAD = ushort.sizeof, 37 LARGEPREFIX = 16, // 16 bytes padding at the front of the array 38 LARGEPAD = LARGEPREFIX + 1, 39 MAXSMALLSIZE = 256-SMALLPAD, 40 MAXMEDSIZE = (PAGESIZE / 2) - MEDPAD 41 } 42 } 43 44 // Now-removed symbol, kept around for ABI 45 // Some programs are dynamically linked, so best to err on the side of keeping symbols around for a while (especially extern(C) ones) 46 // https://github.com/dlang/druntime/pull/3361 47 deprecated extern (C) void lifetime_init() 48 { 49 } 50 51 /** 52 Allocate memory using the garbage collector 53 54 DMD uses this to allocate closures: 55 --- 56 void f(byte[24] x) 57 { 58 return () => x; // `x` is on stack, must be moved to heap to keep it alive 59 } 60 --- 61 62 Params: 63 sz = number of bytes to allocate 64 65 Returns: pointer to `sz` bytes of free, uninitialized memory, managed by the GC. 66 */ 67 extern (C) void* _d_allocmemory(size_t sz) @weak 68 { 69 return GC.malloc(sz); 70 } 71 72 /** 73 Create a new class instance. 74 75 Allocates memory and sets fields to their initial value, but does not call a constructor. 76 77 --- 78 new Object() // _d_newclass(typeid(Object)) 79 --- 80 Params: 81 ci = `TypeInfo_Class` object, to provide instance size and initial bytes to copy 82 83 Returns: newly created object 84 */ 85 extern (C) Object _d_newclass(const ClassInfo ci) @weak 86 { 87 import core.stdc.stdlib; 88 import core.exception : onOutOfMemoryError; 89 void* p; 90 auto init = ci.initializer; 91 92 debug(PRINTF) printf("_d_newclass(ci = %p, %s)\n", ci, cast(char *)ci.name); 93 if (ci.m_flags & TypeInfo_Class.ClassFlags.isCOMclass) 94 { /* COM objects are not garbage collected, they are reference counted 95 * using AddRef() and Release(). They get free'd by C's free() 96 * function called by Release() when Release()'s reference count goes 97 * to zero. 98 */ 99 p = malloc(init.length); 100 if (!p) 101 onOutOfMemoryError(); 102 } 103 else 104 { 105 // TODO: should this be + 1 to avoid having pointers to the next block? 106 BlkAttr attr = BlkAttr.NONE; 107 // extern(C++) classes don't have a classinfo pointer in their vtable so the GC can't finalize them 108 if (ci.m_flags & TypeInfo_Class.ClassFlags.hasDtor 109 && !(ci.m_flags & TypeInfo_Class.ClassFlags.isCPPclass)) 110 attr |= BlkAttr.FINALIZE; 111 if (ci.m_flags & TypeInfo_Class.ClassFlags.noPointers) 112 attr |= BlkAttr.NO_SCAN; 113 p = GC.malloc(init.length, attr, ci); 114 debug(PRINTF) printf(" p = %p\n", p); 115 } 116 117 debug(PRINTF) 118 { 119 printf("p = %p\n", p); 120 printf("ci = %p, ci.init.ptr = %p, len = %llu\n", ci, init.ptr, cast(ulong)init.length); 121 printf("vptr = %p\n", *cast(void**) init); 122 printf("vtbl[0] = %p\n", (*cast(void***) init)[0]); 123 printf("vtbl[1] = %p\n", (*cast(void***) init)[1]); 124 printf("init[0] = %x\n", (cast(uint*) init)[0]); 125 printf("init[1] = %x\n", (cast(uint*) init)[1]); 126 printf("init[2] = %x\n", (cast(uint*) init)[2]); 127 printf("init[3] = %x\n", (cast(uint*) init)[3]); 128 printf("init[4] = %x\n", (cast(uint*) init)[4]); 129 } 130 131 // initialize it 132 p[0 .. init.length] = init[]; 133 134 debug(PRINTF) printf("initialization done\n"); 135 return cast(Object) p; 136 } 137 138 139 /** 140 * 141 */ 142 extern (C) void _d_delinterface(void** p) 143 { 144 if (*p) 145 { 146 Interface* pi = **cast(Interface ***)*p; 147 Object o = cast(Object)(*p - pi.offset); 148 149 _d_delclass(&o); 150 *p = null; 151 } 152 } 153 154 155 // used for deletion 156 private extern (D) alias void function (Object) fp_t; 157 158 159 /** 160 * 161 */ 162 extern (C) void _d_delclass(Object* p) @weak 163 { 164 if (*p) 165 { 166 debug(PRINTF) printf("_d_delclass(%p)\n", *p); 167 168 ClassInfo **pc = cast(ClassInfo **)*p; 169 if (*pc) 170 { 171 ClassInfo c = **pc; 172 173 rt_finalize(cast(void*) *p); 174 175 if (c.deallocator) 176 { 177 fp_t fp = cast(fp_t)c.deallocator; 178 (*fp)(*p); // call deallocator 179 *p = null; 180 return; 181 } 182 } 183 else 184 { 185 rt_finalize(cast(void*) *p); 186 } 187 GC.free(cast(void*) *p); 188 *p = null; 189 } 190 } 191 192 // strip const/immutable/shared/inout from type info 193 inout(TypeInfo) unqualify(return scope inout(TypeInfo) cti) pure nothrow @nogc 194 { 195 TypeInfo ti = cast() cti; 196 while (ti) 197 { 198 // avoid dynamic type casts 199 auto tti = typeid(ti); 200 if (tti is typeid(TypeInfo_Const)) 201 ti = (cast(TypeInfo_Const)cast(void*)ti).base; 202 else if (tti is typeid(TypeInfo_Invariant)) 203 ti = (cast(TypeInfo_Invariant)cast(void*)ti).base; 204 else if (tti is typeid(TypeInfo_Shared)) 205 ti = (cast(TypeInfo_Shared)cast(void*)ti).base; 206 else if (tti is typeid(TypeInfo_Inout)) 207 ti = (cast(TypeInfo_Inout)cast(void*)ti).base; 208 else 209 break; 210 } 211 return ti; 212 } 213 214 // size used to store the TypeInfo at the end of an allocation for structs that have a destructor 215 size_t structTypeInfoSize(const TypeInfo ti) pure nothrow @nogc 216 { 217 if (ti && typeid(ti) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast 218 { 219 auto sti = cast(TypeInfo_Struct)cast(void*)ti; 220 if (sti.xdtor) 221 return size_t.sizeof; 222 } 223 return 0; 224 } 225 226 /** dummy class used to lock for shared array appending */ 227 private class ArrayAllocLengthLock 228 {} 229 230 /** 231 Set the allocated length of the array block. This is called 232 any time an array is appended to or its length is set. 233 234 The allocated block looks like this for blocks < PAGESIZE: 235 236 |elem0|elem1|elem2|...|elemN-1|emptyspace|N*elemsize| 237 238 239 The size of the allocated length at the end depends on the block size: 240 241 a block of 16 to 256 bytes has an 8-bit length. 242 243 a block with 512 to pagesize/2 bytes has a 16-bit length. 244 245 For blocks >= pagesize, the length is a size_t and is at the beginning of the 246 block. The reason we have to do this is because the block can extend into 247 more pages, so we cannot trust the block length if it sits at the end of the 248 block, because it might have just been extended. If we can prove in the 249 future that the block is unshared, we may be able to change this, but I'm not 250 sure it's important. 251 252 In order to do put the length at the front, we have to provide 16 bytes 253 buffer space in case the block has to be aligned properly. In x86, certain 254 SSE instructions will only work if the data is 16-byte aligned. In addition, 255 we need the sentinel byte to prevent accidental pointers to the next block. 256 Because of the extra overhead, we only do this for page size and above, where 257 the overhead is minimal compared to the block size. 258 259 So for those blocks, it looks like: 260 261 |N*elemsize|padding|elem0|elem1|...|elemN-1|emptyspace|sentinelbyte| 262 263 where elem0 starts 16 bytes after the first byte. 264 */ 265 bool __setArrayAllocLength(ref BlkInfo info, size_t newlength, bool isshared, const TypeInfo tinext, size_t oldlength = ~0) pure nothrow 266 { 267 import core.atomic; 268 269 size_t typeInfoSize = structTypeInfoSize(tinext); 270 271 if (info.size <= 256) 272 { 273 import core.checkedint; 274 275 bool overflow; 276 auto newlength_padded = addu(newlength, 277 addu(SMALLPAD, typeInfoSize, overflow), 278 overflow); 279 280 if (newlength_padded > info.size || overflow) 281 // new size does not fit inside block 282 return false; 283 284 auto length = cast(ubyte *)(info.base + info.size - typeInfoSize - SMALLPAD); 285 if (oldlength != ~0) 286 { 287 if (isshared) 288 { 289 return cas(cast(shared)length, cast(ubyte)oldlength, cast(ubyte)newlength); 290 } 291 else 292 { 293 if (*length == cast(ubyte)oldlength) 294 *length = cast(ubyte)newlength; 295 else 296 return false; 297 } 298 } 299 else 300 { 301 // setting the initial length, no cas needed 302 *length = cast(ubyte)newlength; 303 } 304 if (typeInfoSize) 305 { 306 auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); 307 *typeInfo = cast() tinext; 308 } 309 } 310 else if (info.size < PAGESIZE) 311 { 312 if (newlength + MEDPAD + typeInfoSize > info.size) 313 // new size does not fit inside block 314 return false; 315 auto length = cast(ushort *)(info.base + info.size - typeInfoSize - MEDPAD); 316 if (oldlength != ~0) 317 { 318 if (isshared) 319 { 320 return cas(cast(shared)length, cast(ushort)oldlength, cast(ushort)newlength); 321 } 322 else 323 { 324 if (*length == oldlength) 325 *length = cast(ushort)newlength; 326 else 327 return false; 328 } 329 } 330 else 331 { 332 // setting the initial length, no cas needed 333 *length = cast(ushort)newlength; 334 } 335 if (typeInfoSize) 336 { 337 auto typeInfo = cast(TypeInfo*)(info.base + info.size - size_t.sizeof); 338 *typeInfo = cast() tinext; 339 } 340 } 341 else 342 { 343 if (newlength + LARGEPAD > info.size) 344 // new size does not fit inside block 345 return false; 346 auto length = cast(size_t *)(info.base); 347 if (oldlength != ~0) 348 { 349 if (isshared) 350 { 351 return cas(cast(shared)length, cast(size_t)oldlength, cast(size_t)newlength); 352 } 353 else 354 { 355 if (*length == oldlength) 356 *length = newlength; 357 else 358 return false; 359 } 360 } 361 else 362 { 363 // setting the initial length, no cas needed 364 *length = newlength; 365 } 366 if (typeInfoSize) 367 { 368 auto typeInfo = cast(TypeInfo*)(info.base + size_t.sizeof); 369 *typeInfo = cast()tinext; 370 } 371 } 372 return true; // resize succeeded 373 } 374 375 /** 376 get the allocation size of the array for the given block (without padding or type info) 377 */ 378 private size_t __arrayAllocLength(ref BlkInfo info, const TypeInfo tinext) pure nothrow 379 { 380 if (info.size <= 256) 381 return *cast(ubyte *)(info.base + info.size - structTypeInfoSize(tinext) - SMALLPAD); 382 383 if (info.size < PAGESIZE) 384 return *cast(ushort *)(info.base + info.size - structTypeInfoSize(tinext) - MEDPAD); 385 386 return *cast(size_t *)(info.base); 387 } 388 389 /** 390 get the padding required to allocate size bytes. Note that the padding is 391 NOT included in the passed in size. Therefore, do NOT call this function 392 with the size of an allocated block. 393 */ 394 private size_t __arrayPad(size_t size, const TypeInfo tinext) nothrow pure @trusted 395 { 396 return size > MAXMEDSIZE ? LARGEPAD : ((size > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + structTypeInfoSize(tinext)); 397 } 398 399 /** 400 allocate an array memory block by applying the proper padding and 401 assigning block attributes if not inherited from the existing block 402 */ 403 private BlkInfo __arrayAlloc(size_t arrsize, const scope TypeInfo ti, const TypeInfo tinext) nothrow pure 404 { 405 import core.checkedint; 406 407 size_t typeInfoSize = structTypeInfoSize(tinext); 408 size_t padsize = arrsize > MAXMEDSIZE ? LARGEPAD : ((arrsize > MAXSMALLSIZE ? MEDPAD : SMALLPAD) + typeInfoSize); 409 410 bool overflow; 411 auto padded_size = addu(arrsize, padsize, overflow); 412 413 if (overflow) 414 return BlkInfo(); 415 416 uint attr = (!(tinext.flags & 1) ? BlkAttr.NO_SCAN : 0) | BlkAttr.APPENDABLE; 417 if (typeInfoSize) 418 attr |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; 419 420 auto bi = GC.qalloc(padded_size, attr, tinext); 421 __arrayClearPad(bi, arrsize, padsize); 422 return bi; 423 } 424 425 private BlkInfo __arrayAlloc(size_t arrsize, ref BlkInfo info, const scope TypeInfo ti, const TypeInfo tinext) 426 { 427 import core.checkedint; 428 429 if (!info.base) 430 return __arrayAlloc(arrsize, ti, tinext); 431 432 immutable padsize = __arrayPad(arrsize, tinext); 433 bool overflow; 434 auto padded_size = addu(arrsize, padsize, overflow); 435 if (overflow) 436 { 437 return BlkInfo(); 438 } 439 440 auto bi = GC.qalloc(padded_size, info.attr, tinext); 441 __arrayClearPad(bi, arrsize, padsize); 442 return bi; 443 } 444 445 /** 446 cache for the lookup of the block info 447 */ 448 private enum N_CACHE_BLOCKS=8; 449 450 // note this is TLS, so no need to sync. 451 BlkInfo *__blkcache_storage; 452 453 static if (N_CACHE_BLOCKS==1) 454 { 455 version=single_cache; 456 } 457 else 458 { 459 //version=simple_cache; // uncomment to test simple cache strategy 460 //version=random_cache; // uncomment to test random cache strategy 461 462 // ensure N_CACHE_BLOCKS is power of 2. 463 static assert(!((N_CACHE_BLOCKS - 1) & N_CACHE_BLOCKS)); 464 465 version (random_cache) 466 { 467 int __nextRndNum = 0; 468 } 469 int __nextBlkIdx; 470 } 471 472 @property BlkInfo *__blkcache() nothrow 473 { 474 if (!__blkcache_storage) 475 { 476 import core.stdc.stdlib; 477 import core.stdc.string; 478 // allocate the block cache for the first time 479 immutable size = BlkInfo.sizeof * N_CACHE_BLOCKS; 480 __blkcache_storage = cast(BlkInfo *)malloc(size); 481 memset(__blkcache_storage, 0, size); 482 } 483 return __blkcache_storage; 484 } 485 486 // called when thread is exiting. 487 static ~this() 488 { 489 // free the blkcache 490 if (__blkcache_storage) 491 { 492 import core.stdc.stdlib; 493 free(__blkcache_storage); 494 __blkcache_storage = null; 495 } 496 } 497 498 499 // we expect this to be called with the lock in place 500 void processGCMarks(BlkInfo* cache, scope rt.tlsgc.IsMarkedDg isMarked) nothrow 501 { 502 // called after the mark routine to eliminate block cache data when it 503 // might be ready to sweep 504 505 debug(PRINTF) printf("processing GC Marks, %x\n", cache); 506 if (cache) 507 { 508 debug(PRINTF) foreach (i; 0 .. N_CACHE_BLOCKS) 509 { 510 printf("cache entry %d has base ptr %x\tsize %d\tflags %x\n", i, cache[i].base, cache[i].size, cache[i].attr); 511 } 512 auto cache_end = cache + N_CACHE_BLOCKS; 513 for (;cache < cache_end; ++cache) 514 { 515 if (cache.base != null && !isMarked(cache.base)) 516 { 517 debug(PRINTF) printf("clearing cache entry at %x\n", cache.base); 518 cache.base = null; // clear that data. 519 } 520 } 521 } 522 } 523 524 unittest 525 { 526 // Bugzilla 10701 - segfault in GC 527 ubyte[] result; result.length = 4096; 528 GC.free(result.ptr); 529 GC.collect(); 530 } 531 532 /** 533 Get the cached block info of an interior pointer. Returns null if the 534 interior pointer's block is not cached. 535 536 NOTE: The base ptr in this struct can be cleared asynchronously by the GC, 537 so any use of the returned BlkInfo should copy it and then check the 538 base ptr of the copy before actually using it. 539 540 TODO: Change this function so the caller doesn't have to be aware of this 541 issue. Either return by value and expect the caller to always check 542 the base ptr as an indication of whether the struct is valid, or set 543 the BlkInfo as a side-effect and return a bool to indicate success. 544 */ 545 BlkInfo *__getBlkInfo(void *interior) nothrow 546 { 547 BlkInfo *ptr = __blkcache; 548 version (single_cache) 549 { 550 if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) 551 return ptr; 552 return null; // not in cache. 553 } 554 else version (simple_cache) 555 { 556 foreach (i; 0..N_CACHE_BLOCKS) 557 { 558 if (ptr.base && ptr.base <= interior && (interior - ptr.base) < ptr.size) 559 return ptr; 560 ptr++; 561 } 562 } 563 else 564 { 565 // try to do a smart lookup, using __nextBlkIdx as the "head" 566 auto curi = ptr + __nextBlkIdx; 567 for (auto i = curi; i >= ptr; --i) 568 { 569 if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) 570 return i; 571 } 572 573 for (auto i = ptr + N_CACHE_BLOCKS - 1; i > curi; --i) 574 { 575 if (i.base && i.base <= interior && cast(size_t)(interior - i.base) < i.size) 576 return i; 577 } 578 } 579 return null; // not in cache. 580 } 581 582 void __insertBlkInfoCache(BlkInfo bi, BlkInfo *curpos) nothrow 583 { 584 version (single_cache) 585 { 586 *__blkcache = bi; 587 } 588 else 589 { 590 version (simple_cache) 591 { 592 if (curpos) 593 *curpos = bi; 594 else 595 { 596 // note, this is a super-simple algorithm that does not care about 597 // most recently used. It simply uses a round-robin technique to 598 // cache block info. This means that the ordering of the cache 599 // doesn't mean anything. Certain patterns of allocation may 600 // render the cache near-useless. 601 __blkcache[__nextBlkIdx] = bi; 602 __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); 603 } 604 } 605 else version (random_cache) 606 { 607 // strategy: if the block currently is in the cache, move the 608 // current block index to the a random element and evict that 609 // element. 610 auto cache = __blkcache; 611 if (!curpos) 612 { 613 __nextBlkIdx = (__nextRndNum = 1664525 * __nextRndNum + 1013904223) & (N_CACHE_BLOCKS - 1); 614 curpos = cache + __nextBlkIdx; 615 } 616 else 617 { 618 __nextBlkIdx = curpos - cache; 619 } 620 *curpos = bi; 621 } 622 else 623 { 624 // 625 // strategy: If the block currently is in the cache, swap it with 626 // the head element. Otherwise, move the head element up by one, 627 // and insert it there. 628 // 629 auto cache = __blkcache; 630 if (!curpos) 631 { 632 __nextBlkIdx = (__nextBlkIdx+1) & (N_CACHE_BLOCKS - 1); 633 curpos = cache + __nextBlkIdx; 634 } 635 else if (curpos !is cache + __nextBlkIdx) 636 { 637 *curpos = cache[__nextBlkIdx]; 638 curpos = cache + __nextBlkIdx; 639 } 640 *curpos = bi; 641 } 642 } 643 } 644 645 /** 646 Shrink the "allocated" length of an array to be the exact size of the array. 647 648 It doesn't matter what the current allocated length of the array is, the 649 user is telling the runtime that he knows what he is doing. 650 651 Params: 652 ti = `TypeInfo` of array type 653 arr = array to shrink. Its `.length` is element length, not byte length, despite `void` type 654 */ 655 extern(C) void _d_arrayshrinkfit(const TypeInfo ti, void[] arr) nothrow 656 { 657 // note, we do not care about shared. We are setting the length no matter 658 // what, so no lock is required. 659 debug(PRINTF) printf("_d_arrayshrinkfit, elemsize = %d, arr.ptr = x%x arr.length = %d\n", ti.next.tsize, arr.ptr, arr.length); 660 auto tinext = unqualify(ti.next); 661 auto size = tinext.tsize; // array element size 662 auto cursize = arr.length * size; 663 auto isshared = typeid(ti) is typeid(TypeInfo_Shared); 664 auto bic = isshared ? null : __getBlkInfo(arr.ptr); 665 auto info = bic ? *bic : GC.query(arr.ptr); 666 if (info.base && (info.attr & BlkAttr.APPENDABLE)) 667 { 668 auto newsize = (arr.ptr - __arrayStart(info)) + cursize; 669 670 debug(PRINTF) printf("setting allocated size to %d\n", (arr.ptr - info.base) + cursize); 671 672 // destroy structs that become unused memory when array size is shrinked 673 if (typeid(tinext) is typeid(TypeInfo_Struct)) // avoid a complete dynamic type cast 674 { 675 auto sti = cast(TypeInfo_Struct)cast(void*)tinext; 676 if (sti.xdtor) 677 { 678 auto oldsize = __arrayAllocLength(info, tinext); 679 if (oldsize > cursize) 680 { 681 try 682 { 683 finalize_array(arr.ptr + cursize, oldsize - cursize, sti); 684 } 685 catch (Exception e) 686 { 687 import core.exception : onFinalizeError; 688 onFinalizeError(sti, e); 689 } 690 } 691 } 692 } 693 // Note: Since we "assume" the append is safe, it means it is not shared. 694 // Since it is not shared, we also know it won't throw (no lock). 695 if (!__setArrayAllocLength(info, newsize, false, tinext)) 696 { 697 import core.exception : onInvalidMemoryOperationError; 698 onInvalidMemoryOperationError(); 699 } 700 701 // cache the block if not already done. 702 if (!isshared && !bic) 703 __insertBlkInfoCache(info, null); 704 } 705 } 706 707 package bool hasPostblit(in TypeInfo ti) nothrow pure 708 { 709 return (&ti.postblit).funcptr !is &TypeInfo.postblit; 710 } 711 712 void __doPostblit(void *ptr, size_t len, const TypeInfo ti) 713 { 714 if (!hasPostblit(ti)) 715 return; 716 717 if (auto tis = cast(TypeInfo_Struct)ti) 718 { 719 // this is a struct, check the xpostblit member 720 auto pblit = tis.xpostblit; 721 if (!pblit) 722 // postblit not specified, no point in looping. 723 return; 724 725 // optimized for struct, call xpostblit directly for each element 726 immutable size = ti.tsize; 727 const eptr = ptr + len; 728 for (;ptr < eptr;ptr += size) 729 pblit(ptr); 730 } 731 else 732 { 733 // generic case, call the typeinfo's postblit function 734 immutable size = ti.tsize; 735 const eptr = ptr + len; 736 for (;ptr < eptr;ptr += size) 737 ti.postblit(ptr); 738 } 739 } 740 741 742 /** 743 Set the array capacity. 744 745 If the array capacity isn't currently large enough 746 to hold the requested capacity (in number of elements), then the array is 747 resized/reallocated to the appropriate size. 748 749 Pass in a requested capacity of 0 to get the current capacity. 750 751 Params: 752 ti = type info of element type 753 newcapacity = requested new capacity 754 p = pointer to array to set. Its `length` is left unchanged. 755 756 Returns: the number of elements that can actually be stored once the resizing is done 757 */ 758 extern(C) size_t _d_arraysetcapacity(const TypeInfo ti, size_t newcapacity, void[]* p) @weak 759 in 760 { 761 assert(ti); 762 assert(!(*p).length || (*p).ptr); 763 } 764 do 765 { 766 import core.stdc.string; 767 import core.exception : onOutOfMemoryError; 768 769 // step 1, get the block 770 auto isshared = typeid(ti) is typeid(TypeInfo_Shared); 771 auto bic = isshared ? null : __getBlkInfo((*p).ptr); 772 auto info = bic ? *bic : GC.query((*p).ptr); 773 auto tinext = unqualify(ti.next); 774 auto size = tinext.tsize; 775 version (D_InlineAsm_X86) 776 { 777 size_t reqsize = void; 778 779 asm 780 { 781 mov EAX, newcapacity; 782 mul EAX, size; 783 mov reqsize, EAX; 784 jnc Lcontinue; 785 } 786 } 787 else version (D_InlineAsm_X86_64) 788 { 789 size_t reqsize = void; 790 791 asm 792 { 793 mov RAX, newcapacity; 794 mul RAX, size; 795 mov reqsize, RAX; 796 jnc Lcontinue; 797 } 798 } 799 else 800 { 801 import core.checkedint : mulu; 802 803 bool overflow = false; 804 size_t reqsize = mulu(size, newcapacity, overflow); 805 if (!overflow) 806 goto Lcontinue; 807 } 808 Loverflow: 809 onOutOfMemoryError(); 810 assert(0); 811 Lcontinue: 812 813 // step 2, get the actual "allocated" size. If the allocated size does not 814 // match what we expect, then we will need to reallocate anyways. 815 816 // TODO: this probably isn't correct for shared arrays 817 size_t curallocsize = void; 818 size_t curcapacity = void; 819 size_t offset = void; 820 size_t arraypad = void; 821 if (info.base && (info.attr & BlkAttr.APPENDABLE)) 822 { 823 if (info.size <= 256) 824 { 825 arraypad = SMALLPAD + structTypeInfoSize(tinext); 826 curallocsize = *(cast(ubyte *)(info.base + info.size - arraypad)); 827 } 828 else if (info.size < PAGESIZE) 829 { 830 arraypad = MEDPAD + structTypeInfoSize(tinext); 831 curallocsize = *(cast(ushort *)(info.base + info.size - arraypad)); 832 } 833 else 834 { 835 curallocsize = *(cast(size_t *)(info.base)); 836 arraypad = LARGEPAD; 837 } 838 839 840 offset = (*p).ptr - __arrayStart(info); 841 if (offset + (*p).length * size != curallocsize) 842 { 843 curcapacity = 0; 844 } 845 else 846 { 847 // figure out the current capacity of the block from the point 848 // of view of the array. 849 curcapacity = info.size - offset - arraypad; 850 } 851 } 852 else 853 { 854 curallocsize = curcapacity = offset = 0; 855 } 856 debug(PRINTF) printf("_d_arraysetcapacity, p = x%d,%d, newcapacity=%d, info.size=%d, reqsize=%d, curallocsize=%d, curcapacity=%d, offset=%d\n", (*p).ptr, (*p).length, newcapacity, info.size, reqsize, curallocsize, curcapacity, offset); 857 858 if (curcapacity >= reqsize) 859 { 860 // no problems, the current allocated size is large enough. 861 return curcapacity / size; 862 } 863 864 // step 3, try to extend the array in place. 865 if (info.size >= PAGESIZE && curcapacity != 0) 866 { 867 auto extendsize = reqsize + offset + LARGEPAD - info.size; 868 auto u = GC.extend(info.base, extendsize, extendsize); 869 if (u) 870 { 871 // extend worked, save the new current allocated size 872 if (bic) 873 bic.size = u; // update cache 874 curcapacity = u - offset - LARGEPAD; 875 return curcapacity / size; 876 } 877 } 878 879 // step 4, if extending doesn't work, allocate a new array with at least the requested allocated size. 880 auto datasize = (*p).length * size; 881 // copy attributes from original block, or from the typeinfo if the 882 // original block doesn't exist. 883 info = __arrayAlloc(reqsize, info, ti, tinext); 884 if (info.base is null) 885 goto Loverflow; 886 // copy the data over. 887 // note that malloc will have initialized the data we did not request to 0. 888 auto tgt = __arrayStart(info); 889 memcpy(tgt, (*p).ptr, datasize); 890 891 // handle postblit 892 __doPostblit(tgt, datasize, tinext); 893 894 if (!(info.attr & BlkAttr.NO_SCAN)) 895 { 896 // need to memset the newly requested data, except for the data that 897 // malloc returned that we didn't request. 898 void *endptr = tgt + reqsize; 899 void *begptr = tgt + datasize; 900 901 // sanity check 902 assert(endptr >= begptr); 903 memset(begptr, 0, endptr - begptr); 904 } 905 906 // set up the correct length 907 __setArrayAllocLength(info, datasize, isshared, tinext); 908 if (!isshared) 909 __insertBlkInfoCache(info, bic); 910 911 *p = (cast(void*)tgt)[0 .. (*p).length]; 912 913 // determine the padding. This has to be done manually because __arrayPad 914 // assumes you are not counting the pad size, and info.size does include 915 // the pad. 916 if (info.size <= 256) 917 arraypad = SMALLPAD + structTypeInfoSize(tinext); 918 else if (info.size < PAGESIZE) 919 arraypad = MEDPAD + structTypeInfoSize(tinext); 920 else 921 arraypad = LARGEPAD; 922 923 curcapacity = info.size - arraypad; 924 return curcapacity / size; 925 } 926 927 /** 928 Allocate an array with the garbage collector. 929 930 Has three variants: 931 - `_d_newarrayU` leave elements uninitialized 932 - `_d_newarrayT` initializes to 0 (e.g `new int[]`) 933 - `_d_newarrayiT` initializes based on initializer retrieved from TypeInfo (e.g `new float[]`) 934 935 Params: 936 ti = the type of the resulting array, (may also be the corresponding `array.ptr` type) 937 length = `.length` of resulting array 938 Returns: newly allocated array 939 */ 940 extern (C) void[] _d_newarrayU(const scope TypeInfo ti, size_t length) pure nothrow @weak 941 { 942 import core.exception : onOutOfMemoryError; 943 944 auto tinext = unqualify(ti.next); 945 auto size = tinext.tsize; 946 947 debug(PRINTF) printf("_d_newarrayU(length = x%x, size = %d)\n", length, size); 948 if (length == 0 || size == 0) 949 return null; 950 951 version (D_InlineAsm_X86) 952 { 953 asm pure nothrow @nogc 954 { 955 mov EAX,size ; 956 mul EAX,length ; 957 mov size,EAX ; 958 jnc Lcontinue ; 959 } 960 } 961 else version (D_InlineAsm_X86_64) 962 { 963 asm pure nothrow @nogc 964 { 965 mov RAX,size ; 966 mul RAX,length ; 967 mov size,RAX ; 968 jnc Lcontinue ; 969 } 970 } 971 else 972 { 973 import core.checkedint : mulu; 974 975 bool overflow = false; 976 size = mulu(size, length, overflow); 977 if (!overflow) 978 goto Lcontinue; 979 } 980 Loverflow: 981 onOutOfMemoryError(); 982 assert(0); 983 Lcontinue: 984 985 auto info = __arrayAlloc(size, ti, tinext); 986 if (!info.base) 987 goto Loverflow; 988 debug(PRINTF) printf(" p = %p\n", info.base); 989 // update the length of the array 990 auto arrstart = __arrayStart(info); 991 auto isshared = typeid(ti) is typeid(TypeInfo_Shared); 992 __setArrayAllocLength(info, size, isshared, tinext); 993 return arrstart[0..length]; 994 } 995 996 /// ditto 997 extern (C) void[] _d_newarrayT(const TypeInfo ti, size_t length) pure nothrow @weak 998 { 999 import core.stdc.string; 1000 1001 void[] result = _d_newarrayU(ti, length); 1002 auto tinext = unqualify(ti.next); 1003 auto size = tinext.tsize; 1004 1005 memset(result.ptr, 0, size * length); 1006 return result; 1007 } 1008 1009 /// ditto 1010 extern (C) void[] _d_newarrayiT(const TypeInfo ti, size_t length) pure nothrow @weak 1011 { 1012 import core.internal.traits : AliasSeq; 1013 1014 void[] result = _d_newarrayU(ti, length); 1015 auto tinext = unqualify(ti.next); 1016 auto size = tinext.tsize; 1017 1018 auto init = tinext.initializer(); 1019 1020 switch (init.length) 1021 { 1022 foreach (T; AliasSeq!(ubyte, ushort, uint, ulong)) 1023 { 1024 case T.sizeof: 1025 if (tinext.talign % T.alignof == 0) 1026 { 1027 (cast(T*)result.ptr)[0 .. size * length / T.sizeof] = *cast(T*)init.ptr; 1028 return result; 1029 } 1030 goto default; 1031 } 1032 1033 default: 1034 { 1035 import core.stdc.string; 1036 immutable sz = init.length; 1037 for (size_t u = 0; u < size * length; u += sz) 1038 memcpy(result.ptr + u, init.ptr, sz); 1039 return result; 1040 } 1041 } 1042 } 1043 1044 1045 /* 1046 * Helper for creating multi-dimensional arrays 1047 */ 1048 private void[] _d_newarrayOpT(alias op)(const TypeInfo ti, size_t[] dims) 1049 { 1050 debug(PRINTF) printf("_d_newarrayOpT(ndims = %d)\n", dims.length); 1051 if (dims.length == 0) 1052 return null; 1053 1054 void[] foo(const TypeInfo ti, size_t[] dims) 1055 { 1056 auto tinext = unqualify(ti.next); 1057 auto dim = dims[0]; 1058 1059 debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, dims.length); 1060 if (dims.length == 1) 1061 { 1062 auto r = op(ti, dim); 1063 return *cast(void[]*)(&r); 1064 } 1065 1066 auto allocsize = (void[]).sizeof * dim; 1067 auto info = __arrayAlloc(allocsize, ti, tinext); 1068 auto isshared = typeid(ti) is typeid(TypeInfo_Shared); 1069 __setArrayAllocLength(info, allocsize, isshared, tinext); 1070 auto p = __arrayStart(info)[0 .. dim]; 1071 1072 foreach (i; 0..dim) 1073 { 1074 (cast(void[]*)p.ptr)[i] = foo(tinext, dims[1..$]); 1075 } 1076 return p; 1077 } 1078 1079 auto result = foo(ti, dims); 1080 debug(PRINTF) printf("result = %llx\n", result.ptr); 1081 1082 return result; 1083 } 1084 1085 1086 /** 1087 Create a new multi-dimensional array 1088 1089 Has two variants: 1090 - `_d_newarraymTX` which initializes to 0 1091 - `_d_newarraymiTX` which initializes elements based on `TypeInfo` 1092 1093 --- 1094 void main() 1095 { 1096 new int[][](10, 20); 1097 // _d_newarraymTX(typeid(float), [10, 20]); 1098 1099 new float[][][](10, 20, 30); 1100 // _d_newarraymiTX(typeid(float), [10, 20, 30]); 1101 } 1102 --- 1103 1104 Params: 1105 ti = `TypeInfo` of the array type 1106 dims = array length values for each dimension 1107 1108 Returns: 1109 newly allocated array 1110 */ 1111 extern (C) void[] _d_newarraymTX(const TypeInfo ti, size_t[] dims) @weak 1112 { 1113 debug(PRINTF) printf("_d_newarraymT(dims.length = %d)\n", dims.length); 1114 1115 if (dims.length == 0) 1116 return null; 1117 else 1118 { 1119 return _d_newarrayOpT!(_d_newarrayT)(ti, dims); 1120 } 1121 } 1122 1123 /// ditto 1124 extern (C) void[] _d_newarraymiTX(const TypeInfo ti, size_t[] dims) @weak 1125 { 1126 debug(PRINTF) printf("_d_newarraymiT(dims.length = %d)\n", dims.length); 1127 1128 if (dims.length == 0) 1129 return null; 1130 else 1131 { 1132 return _d_newarrayOpT!(_d_newarrayiT)(ti, dims); 1133 } 1134 } 1135 1136 /** 1137 Non-template version of $(REF _d_newitemT, core,lifetime) that does not perform 1138 initialization. Needed for $(REF allocEntry, rt,aaA). 1139 1140 Params: 1141 _ti = `TypeInfo` of item to allocate 1142 Returns: 1143 newly allocated item 1144 */ 1145 extern (C) void* _d_newitemU(scope const TypeInfo _ti) pure nothrow @weak 1146 { 1147 auto ti = unqualify(_ti); 1148 auto flags = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0; 1149 immutable tiSize = structTypeInfoSize(ti); 1150 immutable itemSize = ti.tsize; 1151 immutable size = itemSize + tiSize; 1152 if (tiSize) 1153 flags |= BlkAttr.STRUCTFINAL | BlkAttr.FINALIZE; 1154 1155 auto blkInf = GC.qalloc(size, flags, ti); 1156 auto p = blkInf.base; 1157 1158 if (tiSize) 1159 { 1160 // the GC might not have cleared the padding area in the block 1161 *cast(TypeInfo*)(p + (itemSize & ~(size_t.sizeof - 1))) = null; 1162 *cast(TypeInfo*)(p + blkInf.size - tiSize) = cast() ti; 1163 } 1164 1165 return p; 1166 } 1167 1168 debug(PRINTF) 1169 { 1170 extern(C) void printArrayCache() 1171 { 1172 auto ptr = __blkcache; 1173 printf("CACHE: \n"); 1174 foreach (i; 0 .. N_CACHE_BLOCKS) 1175 { 1176 printf(" %d\taddr:% .8x\tsize:% .10d\tflags:% .8x\n", i, ptr[i].base, ptr[i].size, ptr[i].attr); 1177 } 1178 } 1179 } 1180 1181 /** 1182 * 1183 */ 1184 extern (C) void _d_delmemory(void* *p) @weak 1185 { 1186 if (*p) 1187 { 1188 GC.free(*p); 1189 *p = null; 1190 } 1191 } 1192 1193 1194 /** 1195 * 1196 */ 1197 extern (C) void _d_callinterfacefinalizer(void *p) @weak 1198 { 1199 if (p) 1200 { 1201 Interface *pi = **cast(Interface ***)p; 1202 Object o = cast(Object)(p - pi.offset); 1203 rt_finalize(cast(void*)o); 1204 } 1205 } 1206 1207 1208 /** 1209 * 1210 */ 1211 extern (C) void _d_callfinalizer(void* p) @weak 1212 { 1213 rt_finalize( p ); 1214 } 1215 1216 1217 /** 1218 * 1219 */ 1220 extern (C) void rt_setCollectHandler(CollectHandler h) 1221 { 1222 collectHandler = h; 1223 } 1224 1225 1226 /** 1227 * 1228 */ 1229 extern (C) CollectHandler rt_getCollectHandler() 1230 { 1231 return collectHandler; 1232 } 1233 1234 1235 /** 1236 * 1237 */ 1238 extern (C) int rt_hasFinalizerInSegment(void* p, size_t size, uint attr, scope const(void)[] segment) nothrow 1239 { 1240 if (attr & BlkAttr.STRUCTFINAL) 1241 { 1242 if (attr & BlkAttr.APPENDABLE) 1243 return hasArrayFinalizerInSegment(p, size, segment); 1244 return hasStructFinalizerInSegment(p, size, segment); 1245 } 1246 1247 // otherwise class 1248 auto ppv = cast(void**) p; 1249 if (!p || !*ppv) 1250 return false; 1251 1252 auto c = *cast(ClassInfo*)*ppv; 1253 do 1254 { 1255 auto pf = c.destructor; 1256 if (cast(size_t)(pf - segment.ptr) < segment.length) return true; 1257 } 1258 while ((c = c.base) !is null); 1259 1260 return false; 1261 } 1262 1263 int hasStructFinalizerInSegment(void* p, size_t size, in void[] segment) nothrow 1264 { 1265 if (!p) 1266 return false; 1267 1268 auto ti = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); 1269 return cast(size_t)(cast(void*)ti.xdtor - segment.ptr) < segment.length; 1270 } 1271 1272 int hasArrayFinalizerInSegment(void* p, size_t size, in void[] segment) nothrow 1273 { 1274 if (!p) 1275 return false; 1276 1277 TypeInfo_Struct si = void; 1278 if (size < PAGESIZE) 1279 si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); 1280 else 1281 si = *cast(TypeInfo_Struct*)(p + size_t.sizeof); 1282 1283 return cast(size_t)(cast(void*)si.xdtor - segment.ptr) < segment.length; 1284 } 1285 1286 debug (VALGRIND) import etc.valgrind.valgrind; 1287 1288 // called by the GC 1289 void finalize_array2(void* p, size_t size) nothrow 1290 { 1291 debug(PRINTF) printf("rt_finalize_array2(p = %p)\n", p); 1292 1293 TypeInfo_Struct si = void; 1294 debug (VALGRIND) 1295 { 1296 auto block = p[0..size]; 1297 disableAddrReportingInRange(block); 1298 } 1299 if (size <= 256) 1300 { 1301 si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); 1302 size = *cast(ubyte*)(p + size - size_t.sizeof - SMALLPAD); 1303 } 1304 else if (size < PAGESIZE) 1305 { 1306 si = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); 1307 size = *cast(ushort*)(p + size - size_t.sizeof - MEDPAD); 1308 } 1309 else 1310 { 1311 si = *cast(TypeInfo_Struct*)(p + size_t.sizeof); 1312 size = *cast(size_t*)p; 1313 p += LARGEPREFIX; 1314 } 1315 debug (VALGRIND) enableAddrReportingInRange(block); 1316 1317 try 1318 { 1319 finalize_array(p, size, si); 1320 } 1321 catch (Exception e) 1322 { 1323 import core.exception : onFinalizeError; 1324 onFinalizeError(si, e); 1325 } 1326 } 1327 1328 void finalize_array(void* p, size_t size, const TypeInfo_Struct si) 1329 { 1330 // Due to the fact that the delete operator calls destructors 1331 // for arrays from the last element to the first, we maintain 1332 // compatibility here by doing the same. 1333 auto tsize = si.tsize; 1334 for (auto curP = p + size - tsize; curP >= p; curP -= tsize) 1335 { 1336 // call destructor 1337 si.destroy(curP); 1338 } 1339 } 1340 1341 // called by the GC 1342 void finalize_struct(void* p, size_t size) nothrow 1343 { 1344 debug(PRINTF) printf("finalize_struct(p = %p)\n", p); 1345 1346 auto ti = *cast(TypeInfo_Struct*)(p + size - size_t.sizeof); 1347 try 1348 { 1349 ti.destroy(p); // call destructor 1350 } 1351 catch (Exception e) 1352 { 1353 import core.exception : onFinalizeError; 1354 onFinalizeError(ti, e); 1355 } 1356 } 1357 1358 /** 1359 * 1360 */ 1361 extern (C) void rt_finalize2(void* p, bool det = true, bool resetMemory = true) nothrow 1362 { 1363 debug(PRINTF) printf("rt_finalize2(p = %p)\n", p); 1364 1365 auto ppv = cast(void**) p; 1366 if (!p || !*ppv) 1367 return; 1368 1369 auto pc = cast(ClassInfo*) *ppv; 1370 try 1371 { 1372 if (det || collectHandler is null || collectHandler(cast(Object) p)) 1373 { 1374 auto c = *pc; 1375 do 1376 { 1377 if (c.destructor) 1378 (cast(fp_t) c.destructor)(cast(Object) p); // call destructor 1379 } 1380 while ((c = c.base) !is null); 1381 } 1382 1383 if (ppv[1]) // if monitor is not null 1384 _d_monitordelete(cast(Object) p, det); 1385 1386 if (resetMemory) 1387 { 1388 auto w = (*pc).initializer; 1389 p[0 .. w.length] = w[]; 1390 } 1391 } 1392 catch (Exception e) 1393 { 1394 import core.exception : onFinalizeError; 1395 onFinalizeError(*pc, e); 1396 } 1397 finally 1398 { 1399 *ppv = null; // zero vptr even if `resetMemory` is false 1400 } 1401 } 1402 1403 /// Backwards compatibility 1404 extern (C) void rt_finalize(void* p, bool det = true) nothrow 1405 { 1406 rt_finalize2(p, det, true); 1407 } 1408 1409 extern (C) void rt_finalizeFromGC(void* p, size_t size, uint attr) nothrow 1410 { 1411 // to verify: reset memory necessary? 1412 if (!(attr & BlkAttr.STRUCTFINAL)) 1413 rt_finalize2(p, false, false); // class 1414 else if (attr & BlkAttr.APPENDABLE) 1415 finalize_array2(p, size); // array of structs 1416 else 1417 finalize_struct(p, size); // struct 1418 } 1419 1420 1421 /** 1422 Resize a dynamic array by setting the `.length` property 1423 1424 Newly created elements are initialized to their default value. 1425 1426 Has two variants: 1427 - `_d_arraysetlengthT` for arrays with elements that initialize to 0 1428 - `_d_arraysetlengthiT` for non-zero initializers retrieved from `TypeInfo` 1429 1430 --- 1431 void main() 1432 { 1433 int[] a = [1, 2]; 1434 a.length = 3; // gets lowered to `_d_arraysetlengthT(typeid(int[]), 3, &a)` 1435 } 1436 --- 1437 1438 Params: 1439 ti = `TypeInfo` of array 1440 newlength = new value for the array's `.length` 1441 p = pointer to array to update the `.length` of. 1442 While it's cast to `void[]`, its `.length` is still treated as element length. 1443 Returns: `*p` after being updated 1444 */ 1445 extern (C) void[] _d_arraysetlengthT(const TypeInfo ti, size_t newlength, void[]* p) @weak 1446 in 1447 { 1448 assert(ti); 1449 assert(!(*p).length || (*p).ptr); 1450 } 1451 do 1452 { 1453 import core.stdc.string; 1454 import core.exception : onOutOfMemoryError; 1455 1456 debug(PRINTF) 1457 { 1458 //printf("_d_arraysetlengthT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); 1459 if (p) 1460 printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); 1461 } 1462 1463 if (newlength <= (*p).length) 1464 { 1465 *p = (*p)[0 .. newlength]; 1466 void* newdata = (*p).ptr; 1467 return newdata[0 .. newlength]; 1468 } 1469 auto tinext = unqualify(ti.next); 1470 size_t sizeelem = tinext.tsize; 1471 1472 /* Calculate: newsize = newlength * sizeelem 1473 */ 1474 bool overflow = false; 1475 version (D_InlineAsm_X86) 1476 { 1477 size_t newsize = void; 1478 1479 asm pure nothrow @nogc 1480 { 1481 mov EAX, newlength; 1482 mul EAX, sizeelem; 1483 mov newsize, EAX; 1484 setc overflow; 1485 } 1486 } 1487 else version (D_InlineAsm_X86_64) 1488 { 1489 size_t newsize = void; 1490 1491 asm pure nothrow @nogc 1492 { 1493 mov RAX, newlength; 1494 mul RAX, sizeelem; 1495 mov newsize, RAX; 1496 setc overflow; 1497 } 1498 } 1499 else 1500 { 1501 import core.checkedint : mulu; 1502 const size_t newsize = mulu(sizeelem, newlength, overflow); 1503 } 1504 if (overflow) 1505 { 1506 onOutOfMemoryError(); 1507 assert(0); 1508 } 1509 1510 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); 1511 1512 const isshared = typeid(ti) is typeid(TypeInfo_Shared); 1513 1514 if (!(*p).ptr) 1515 { 1516 // pointer was null, need to allocate 1517 auto info = __arrayAlloc(newsize, ti, tinext); 1518 if (info.base is null) 1519 { 1520 onOutOfMemoryError(); 1521 assert(0); 1522 } 1523 __setArrayAllocLength(info, newsize, isshared, tinext); 1524 if (!isshared) 1525 __insertBlkInfoCache(info, null); 1526 void* newdata = cast(byte *)__arrayStart(info); 1527 memset(newdata, 0, newsize); 1528 *p = newdata[0 .. newlength]; 1529 return *p; 1530 } 1531 1532 const size_t size = (*p).length * sizeelem; 1533 auto bic = isshared ? null : __getBlkInfo((*p).ptr); 1534 auto info = bic ? *bic : GC.query((*p).ptr); 1535 1536 /* Attempt to extend past the end of the existing array. 1537 * If not possible, allocate new space for entire array and copy. 1538 */ 1539 bool allocateAndCopy = false; 1540 void* newdata = (*p).ptr; 1541 if (info.base && (info.attr & BlkAttr.APPENDABLE)) 1542 { 1543 // calculate the extent of the array given the base. 1544 const size_t offset = (*p).ptr - __arrayStart(info); 1545 if (info.size >= PAGESIZE) 1546 { 1547 // size of array is at the front of the block 1548 if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1549 { 1550 // check to see if it failed because there is not 1551 // enough space 1552 if (*(cast(size_t*)info.base) == size + offset) 1553 { 1554 // not enough space, try extending 1555 auto extendsize = newsize + offset + LARGEPAD - info.size; 1556 auto u = GC.extend(info.base, extendsize, extendsize); 1557 if (u) 1558 { 1559 // extend worked, now try setting the length 1560 // again. 1561 info.size = u; 1562 if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1563 { 1564 if (!isshared) 1565 __insertBlkInfoCache(info, bic); 1566 memset(newdata + size, 0, newsize - size); 1567 *p = newdata[0 .. newlength]; 1568 return *p; 1569 } 1570 } 1571 } 1572 1573 // couldn't do it, reallocate 1574 allocateAndCopy = true; 1575 } 1576 else if (!isshared && !bic) 1577 { 1578 // add this to the cache, it wasn't present previously. 1579 __insertBlkInfoCache(info, null); 1580 } 1581 } 1582 else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1583 { 1584 // could not resize in place 1585 allocateAndCopy = true; 1586 } 1587 else if (!isshared && !bic) 1588 { 1589 // add this to the cache, it wasn't present previously. 1590 __insertBlkInfoCache(info, null); 1591 } 1592 } 1593 else 1594 allocateAndCopy = true; 1595 1596 if (allocateAndCopy) 1597 { 1598 if (info.base) 1599 { 1600 if (bic) 1601 { 1602 // a chance that flags have changed since this was cached, we should fetch the most recent flags 1603 info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; 1604 } 1605 info = __arrayAlloc(newsize, info, ti, tinext); 1606 } 1607 else 1608 { 1609 info = __arrayAlloc(newsize, ti, tinext); 1610 } 1611 1612 if (info.base is null) 1613 { 1614 onOutOfMemoryError(); 1615 assert(0); 1616 } 1617 1618 __setArrayAllocLength(info, newsize, isshared, tinext); 1619 if (!isshared) 1620 __insertBlkInfoCache(info, bic); 1621 newdata = cast(byte *)__arrayStart(info); 1622 newdata[0 .. size] = (*p).ptr[0 .. size]; 1623 1624 /* Do postblit processing, as we are making a copy and the 1625 * original array may have references. 1626 * Note that this may throw. 1627 */ 1628 __doPostblit(newdata, size, tinext); 1629 } 1630 1631 // Zero the unused portion of the newly allocated space 1632 memset(newdata + size, 0, newsize - size); 1633 1634 *p = newdata[0 .. newlength]; 1635 return *p; 1636 } 1637 1638 /// ditto 1639 extern (C) void[] _d_arraysetlengthiT(const TypeInfo ti, size_t newlength, void[]* p) @weak 1640 in 1641 { 1642 assert(!(*p).length || (*p).ptr); 1643 } 1644 do 1645 { 1646 import core.stdc.string; 1647 import core.exception : onOutOfMemoryError; 1648 1649 debug(PRINTF) 1650 { 1651 //printf("_d_arraysetlengthiT(p = %p, sizeelem = %d, newlength = %d)\n", p, sizeelem, newlength); 1652 if (p) 1653 printf("\tp.ptr = %p, p.length = %d\n", (*p).ptr, (*p).length); 1654 } 1655 1656 if (newlength <= (*p).length) 1657 { 1658 *p = (*p)[0 .. newlength]; 1659 void* newdata = (*p).ptr; 1660 return newdata[0 .. newlength]; 1661 } 1662 auto tinext = unqualify(ti.next); 1663 size_t sizeelem = tinext.tsize; 1664 1665 /* Calculate: newsize = newlength * sizeelem 1666 */ 1667 bool overflow = false; 1668 version (D_InlineAsm_X86) 1669 { 1670 size_t newsize = void; 1671 1672 asm pure nothrow @nogc 1673 { 1674 mov EAX, newlength; 1675 mul EAX, sizeelem; 1676 mov newsize, EAX; 1677 setc overflow; 1678 } 1679 } 1680 else version (D_InlineAsm_X86_64) 1681 { 1682 size_t newsize = void; 1683 1684 asm pure nothrow @nogc 1685 { 1686 mov RAX, newlength; 1687 mul RAX, sizeelem; 1688 mov newsize, RAX; 1689 setc overflow; 1690 } 1691 } 1692 else 1693 { 1694 import core.checkedint : mulu; 1695 const size_t newsize = mulu(sizeelem, newlength, overflow); 1696 } 1697 if (overflow) 1698 { 1699 onOutOfMemoryError(); 1700 assert(0); 1701 } 1702 1703 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); 1704 1705 const isshared = typeid(ti) is typeid(TypeInfo_Shared); 1706 1707 static void doInitialize(void *start, void *end, const void[] initializer) 1708 { 1709 if (initializer.length == 1) 1710 { 1711 memset(start, *(cast(ubyte*)initializer.ptr), end - start); 1712 } 1713 else 1714 { 1715 auto q = initializer.ptr; 1716 immutable initsize = initializer.length; 1717 for (; start < end; start += initsize) 1718 { 1719 memcpy(start, q, initsize); 1720 } 1721 } 1722 } 1723 1724 if (!(*p).ptr) 1725 { 1726 // pointer was null, need to allocate 1727 auto info = __arrayAlloc(newsize, ti, tinext); 1728 if (info.base is null) 1729 { 1730 onOutOfMemoryError(); 1731 assert(0); 1732 } 1733 __setArrayAllocLength(info, newsize, isshared, tinext); 1734 if (!isshared) 1735 __insertBlkInfoCache(info, null); 1736 void* newdata = cast(byte *)__arrayStart(info); 1737 doInitialize(newdata, newdata + newsize, tinext.initializer); 1738 *p = newdata[0 .. newlength]; 1739 return *p; 1740 } 1741 1742 const size_t size = (*p).length * sizeelem; 1743 auto bic = isshared ? null : __getBlkInfo((*p).ptr); 1744 auto info = bic ? *bic : GC.query((*p).ptr); 1745 1746 /* Attempt to extend past the end of the existing array. 1747 * If not possible, allocate new space for entire array and copy. 1748 */ 1749 bool allocateAndCopy = false; 1750 void* newdata = (*p).ptr; 1751 1752 if (info.base && (info.attr & BlkAttr.APPENDABLE)) 1753 { 1754 // calculate the extent of the array given the base. 1755 const size_t offset = (*p).ptr - __arrayStart(info); 1756 if (info.size >= PAGESIZE) 1757 { 1758 // size of array is at the front of the block 1759 if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1760 { 1761 // check to see if it failed because there is not 1762 // enough space 1763 if (*(cast(size_t*)info.base) == size + offset) 1764 { 1765 // not enough space, try extending 1766 auto extendsize = newsize + offset + LARGEPAD - info.size; 1767 auto u = GC.extend(info.base, extendsize, extendsize); 1768 if (u) 1769 { 1770 // extend worked, now try setting the length 1771 // again. 1772 info.size = u; 1773 if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1774 { 1775 if (!isshared) 1776 __insertBlkInfoCache(info, bic); 1777 doInitialize(newdata + size, newdata + newsize, tinext.initializer); 1778 *p = newdata[0 .. newlength]; 1779 return *p; 1780 } 1781 } 1782 } 1783 1784 // couldn't do it, reallocate 1785 allocateAndCopy = true; 1786 } 1787 else if (!isshared && !bic) 1788 { 1789 // add this to the cache, it wasn't present previously. 1790 __insertBlkInfoCache(info, null); 1791 } 1792 } 1793 else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1794 { 1795 // could not resize in place 1796 allocateAndCopy = true; 1797 } 1798 else if (!isshared && !bic) 1799 { 1800 // add this to the cache, it wasn't present previously. 1801 __insertBlkInfoCache(info, null); 1802 } 1803 } 1804 else 1805 allocateAndCopy = true; 1806 1807 if (allocateAndCopy) 1808 { 1809 if (info.base) 1810 { 1811 if (bic) 1812 { 1813 // a chance that flags have changed since this was cached, we should fetch the most recent flags 1814 info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; 1815 } 1816 info = __arrayAlloc(newsize, info, ti, tinext); 1817 } 1818 else 1819 { 1820 info = __arrayAlloc(newsize, ti, tinext); 1821 } 1822 1823 if (info.base is null) 1824 { 1825 onOutOfMemoryError(); 1826 assert(0); 1827 } 1828 1829 __setArrayAllocLength(info, newsize, isshared, tinext); 1830 if (!isshared) 1831 __insertBlkInfoCache(info, bic); 1832 newdata = cast(byte *)__arrayStart(info); 1833 newdata[0 .. size] = (*p).ptr[0 .. size]; 1834 1835 /* Do postblit processing, as we are making a copy and the 1836 * original array may have references. 1837 * Note that this may throw. 1838 */ 1839 __doPostblit(newdata, size, tinext); 1840 } 1841 1842 // Initialize the unused portion of the newly allocated space 1843 doInitialize(newdata + size, newdata + newsize, tinext.initializer); 1844 *p = newdata[0 .. newlength]; 1845 return *p; 1846 } 1847 1848 1849 /** 1850 Given an array of length `size` that needs to be expanded to `newlength`, 1851 compute a new capacity. 1852 1853 Better version by Dave Fladebo: 1854 This uses an inverse logorithmic algorithm to pre-allocate a bit more 1855 space for larger arrays. 1856 - Arrays smaller than PAGESIZE bytes are left as-is, so for the most 1857 common cases, memory allocation is 1 to 1. The small overhead added 1858 doesn't affect small array perf. (it's virtually the same as 1859 current). 1860 - Larger arrays have some space pre-allocated. 1861 - As the arrays grow, the relative pre-allocated space shrinks. 1862 - The logorithmic algorithm allocates relatively more space for 1863 mid-size arrays, making it very fast for medium arrays (for 1864 mid-to-large arrays, this turns out to be quite a bit faster than the 1865 equivalent realloc() code in C, on Linux at least. Small arrays are 1866 just as fast as GCC). 1867 - Perhaps most importantly, overall memory usage and stress on the GC 1868 is decreased significantly for demanding environments. 1869 1870 Params: 1871 newlength = new `.length` 1872 size = old `.length` 1873 Returns: new capacity for array 1874 */ 1875 size_t newCapacity(size_t newlength, size_t size) 1876 { 1877 version (none) 1878 { 1879 size_t newcap = newlength * size; 1880 } 1881 else 1882 { 1883 size_t newcap = newlength * size; 1884 size_t newext = 0; 1885 1886 if (newcap > PAGESIZE) 1887 { 1888 //double mult2 = 1.0 + (size / log10(pow(newcap * 2.0,2.0))); 1889 1890 // redo above line using only integer math 1891 1892 /*static int log2plus1(size_t c) 1893 { int i; 1894 1895 if (c == 0) 1896 i = -1; 1897 else 1898 for (i = 1; c >>= 1; i++) 1899 { 1900 } 1901 return i; 1902 }*/ 1903 1904 /* The following setting for mult sets how much bigger 1905 * the new size will be over what is actually needed. 1906 * 100 means the same size, more means proportionally more. 1907 * More means faster but more memory consumption. 1908 */ 1909 //long mult = 100 + (1000L * size) / (6 * log2plus1(newcap)); 1910 //long mult = 100 + (1000L * size) / log2plus1(newcap); 1911 import core.bitop; 1912 long mult = 100 + (1000L) / (bsr(newcap) + 1); 1913 1914 // testing shows 1.02 for large arrays is about the point of diminishing return 1915 // 1916 // Commented out because the multipler will never be < 102. In order for it to be < 2, 1917 // then 1000L / (bsr(x) + 1) must be > 2. The highest bsr(x) + 1 1918 // could be is 65 (64th bit set), and 1000L / 64 is much larger 1919 // than 2. We need 500 bit integers for 101 to be achieved :) 1920 /*if (mult < 102) 1921 mult = 102;*/ 1922 /*newext = cast(size_t)((newcap * mult) / 100); 1923 newext -= newext % size;*/ 1924 // This version rounds up to the next element, and avoids using 1925 // mod. 1926 newext = cast(size_t)((newlength * mult + 99) / 100) * size; 1927 debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size); 1928 } 1929 newcap = newext > newcap ? newext : newcap; 1930 debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size); 1931 } 1932 return newcap; 1933 } 1934 1935 1936 /** 1937 Extend an array by n elements. 1938 1939 Caller must initialize those elements. 1940 1941 Params: 1942 ti = type info of array type (not element type) 1943 px = array to append to, cast to `byte[]` while keeping the same `.length`. Will be updated. 1944 n = number of elements to append 1945 Returns: `px` after being appended to 1946 */ 1947 extern (C) 1948 byte[] _d_arrayappendcTX(const TypeInfo ti, return scope ref byte[] px, size_t n) @weak 1949 { 1950 import core.stdc.string; 1951 // This is a cut&paste job from _d_arrayappendT(). Should be refactored. 1952 1953 // only optimize array append where ti is not a shared type 1954 auto tinext = unqualify(ti.next); 1955 auto sizeelem = tinext.tsize; // array element size 1956 auto isshared = typeid(ti) is typeid(TypeInfo_Shared); 1957 auto bic = isshared ? null : __getBlkInfo(px.ptr); 1958 auto info = bic ? *bic : GC.query(px.ptr); 1959 auto length = px.length; 1960 auto newlength = length + n; 1961 auto newsize = newlength * sizeelem; 1962 auto size = length * sizeelem; 1963 size_t newcap = void; // for scratch space 1964 1965 // calculate the extent of the array given the base. 1966 size_t offset = cast(void*)px.ptr - __arrayStart(info); 1967 if (info.base && (info.attr & BlkAttr.APPENDABLE)) 1968 { 1969 if (info.size >= PAGESIZE) 1970 { 1971 // size of array is at the front of the block 1972 if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1973 { 1974 // check to see if it failed because there is not 1975 // enough space 1976 newcap = newCapacity(newlength, sizeelem); 1977 if (*(cast(size_t*)info.base) == size + offset) 1978 { 1979 // not enough space, try extending 1980 auto extendoffset = offset + LARGEPAD - info.size; 1981 auto u = GC.extend(info.base, newsize + extendoffset, newcap + extendoffset); 1982 if (u) 1983 { 1984 // extend worked, now try setting the length 1985 // again. 1986 info.size = u; 1987 if (__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 1988 { 1989 if (!isshared) 1990 __insertBlkInfoCache(info, bic); 1991 goto L1; 1992 } 1993 } 1994 } 1995 1996 // couldn't do it, reallocate 1997 goto L2; 1998 } 1999 else if (!isshared && !bic) 2000 { 2001 __insertBlkInfoCache(info, null); 2002 } 2003 } 2004 else if (!__setArrayAllocLength(info, newsize + offset, isshared, tinext, size + offset)) 2005 { 2006 // could not resize in place 2007 newcap = newCapacity(newlength, sizeelem); 2008 goto L2; 2009 } 2010 else if (!isshared && !bic) 2011 { 2012 __insertBlkInfoCache(info, null); 2013 } 2014 } 2015 else 2016 { 2017 // not appendable or is null 2018 newcap = newCapacity(newlength, sizeelem); 2019 if (info.base) 2020 { 2021 L2: 2022 if (bic) 2023 { 2024 // a chance that flags have changed since this was cached, we should fetch the most recent flags 2025 info.attr = GC.getAttr(info.base) | BlkAttr.APPENDABLE; 2026 } 2027 info = __arrayAlloc(newcap, info, ti, tinext); 2028 } 2029 else 2030 { 2031 info = __arrayAlloc(newcap, ti, tinext); 2032 } 2033 __setArrayAllocLength(info, newsize, isshared, tinext); 2034 if (!isshared) 2035 __insertBlkInfoCache(info, bic); 2036 auto newdata = cast(byte *)__arrayStart(info); 2037 memcpy(newdata, px.ptr, length * sizeelem); 2038 // do postblit processing 2039 __doPostblit(newdata, length * sizeelem, tinext); 2040 (cast(void **)(&px))[1] = newdata; 2041 } 2042 2043 L1: 2044 *cast(size_t *)&px = newlength; 2045 return px; 2046 } 2047 2048 2049 /** 2050 Append `dchar` to `char[]`, converting UTF-32 to UTF-8 2051 2052 --- 2053 void main() 2054 { 2055 char[] s; 2056 s ~= 'α'; 2057 } 2058 --- 2059 2060 Params: 2061 x = array to append to cast to `byte[]`. Will be modified. 2062 c = `dchar` to append 2063 Returns: updated `x` cast to `void[]` 2064 */ 2065 extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) @weak 2066 { 2067 // c could encode into from 1 to 4 characters 2068 char[4] buf = void; 2069 char[] appendthis; // passed to appendT 2070 if (c <= 0x7F) 2071 { 2072 buf.ptr[0] = cast(char)c; 2073 appendthis = buf[0..1]; 2074 } 2075 else if (c <= 0x7FF) 2076 { 2077 buf.ptr[0] = cast(char)(0xC0 | (c >> 6)); 2078 buf.ptr[1] = cast(char)(0x80 | (c & 0x3F)); 2079 appendthis = buf[0..2]; 2080 } 2081 else if (c <= 0xFFFF) 2082 { 2083 buf.ptr[0] = cast(char)(0xE0 | (c >> 12)); 2084 buf.ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); 2085 buf.ptr[2] = cast(char)(0x80 | (c & 0x3F)); 2086 appendthis = buf[0..3]; 2087 } 2088 else if (c <= 0x10FFFF) 2089 { 2090 buf.ptr[0] = cast(char)(0xF0 | (c >> 18)); 2091 buf.ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); 2092 buf.ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); 2093 buf.ptr[3] = cast(char)(0x80 | (c & 0x3F)); 2094 appendthis = buf[0..4]; 2095 } 2096 else 2097 { 2098 import core.exception : onUnicodeError; 2099 onUnicodeError("Invalid UTF-8 sequence", 0); // invalid utf character 2100 } 2101 2102 // 2103 // TODO: This always assumes the array type is shared, because we do not 2104 // get a typeinfo from the compiler. Assuming shared is the safest option. 2105 // Once the compiler is fixed, the proper typeinfo should be forwarded. 2106 // 2107 2108 // Hack because _d_arrayappendT takes `x` as a reference 2109 auto xx = cast(shared(char)[])x; 2110 object._d_arrayappendT(xx, cast(shared(char)[])appendthis); 2111 x = cast(byte[])xx; 2112 return x; 2113 } 2114 2115 unittest 2116 { 2117 import core.exception : UnicodeException; 2118 2119 /* Using inline try {} catch {} blocks fails to catch the UnicodeException 2120 * thrown. 2121 * https://issues.dlang.org/show_bug.cgi?id=16799 2122 */ 2123 static void assertThrown(T : Throwable = Exception, E)(lazy E expr, string msg) 2124 { 2125 try 2126 expr; 2127 catch (T e) { 2128 assert(e.msg == msg); 2129 return; 2130 } 2131 } 2132 2133 static void f() 2134 { 2135 string ret; 2136 int i = -1; 2137 ret ~= i; 2138 } 2139 2140 assertThrown!UnicodeException(f(), "Invalid UTF-8 sequence"); 2141 } 2142 2143 2144 /** 2145 Append `dchar` to `wchar[]`, converting UTF-32 to UTF-16 2146 2147 --- 2148 void main() 2149 { 2150 dchar x; 2151 wchar[] s; 2152 s ~= 'α'; 2153 } 2154 --- 2155 2156 Params: 2157 x = array to append to cast to `byte[]`. Will be modified. 2158 c = `dchar` to append 2159 2160 Returns: updated `x` cast to `void[]` 2161 */ 2162 extern (C) void[] _d_arrayappendwd(ref byte[] x, dchar c) @weak 2163 { 2164 // c could encode into from 1 to 2 w characters 2165 wchar[2] buf = void; 2166 wchar[] appendthis; // passed to appendT 2167 if (c <= 0xFFFF) 2168 { 2169 buf.ptr[0] = cast(wchar) c; 2170 appendthis = buf[0..1]; 2171 } 2172 else 2173 { 2174 buf.ptr[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); 2175 buf.ptr[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00); 2176 appendthis = buf[0..2]; 2177 } 2178 2179 // 2180 // TODO: This always assumes the array type is shared, because we do not 2181 // get a typeinfo from the compiler. Assuming shared is the safest option. 2182 // Once the compiler is fixed, the proper typeinfo should be forwarded. 2183 // 2184 2185 auto xx = (cast(shared(wchar)*)x.ptr)[0 .. x.length]; 2186 object._d_arrayappendT(xx, cast(shared(wchar)[])appendthis); 2187 x = (cast(byte*)xx.ptr)[0 .. xx.length]; 2188 return x; 2189 } 2190 2191 /** 2192 Allocate an array literal 2193 2194 Rely on the caller to do the initialization of the array. 2195 2196 --- 2197 int[] getArr() 2198 { 2199 return [10, 20]; 2200 // auto res = cast(int*) _d_arrayliteralTX(typeid(int[]), 2); 2201 // res[0] = 10; 2202 // res[1] = 20; 2203 // return res[0..2]; 2204 } 2205 --- 2206 2207 Params: 2208 ti = `TypeInfo` of resulting array type 2209 length = `.length` of array literal 2210 2211 Returns: pointer to allocated array 2212 */ 2213 extern (C) 2214 void* _d_arrayliteralTX(const TypeInfo ti, size_t length) @weak 2215 { 2216 auto tinext = unqualify(ti.next); 2217 auto sizeelem = tinext.tsize; // array element size 2218 void* result; 2219 2220 debug(PRINTF) printf("_d_arrayliteralTX(sizeelem = %d, length = %d)\n", sizeelem, length); 2221 if (length == 0 || sizeelem == 0) 2222 result = null; 2223 else 2224 { 2225 auto allocsize = length * sizeelem; 2226 auto info = __arrayAlloc(allocsize, ti, tinext); 2227 auto isshared = typeid(ti) is typeid(TypeInfo_Shared); 2228 __setArrayAllocLength(info, allocsize, isshared, tinext); 2229 result = __arrayStart(info); 2230 } 2231 return result; 2232 } 2233 2234 2235 unittest 2236 { 2237 int[] a; 2238 int[] b; 2239 int i; 2240 2241 a = new int[3]; 2242 a[0] = 1; a[1] = 2; a[2] = 3; 2243 b = a.dup; 2244 assert(b.length == 3); 2245 for (i = 0; i < 3; i++) 2246 assert(b[i] == i + 1); 2247 2248 // test slice appending 2249 b = a[0..1]; 2250 b ~= 4; 2251 for (i = 0; i < 3; i++) 2252 assert(a[i] == i + 1); 2253 2254 // test reserving 2255 char[] arr = new char[4093]; 2256 for (i = 0; i < arr.length; i++) 2257 arr[i] = cast(char)(i % 256); 2258 2259 // note that these two commands used to cause corruption, which may not be 2260 // detected. 2261 arr.reserve(4094); 2262 auto arr2 = arr ~ "123"; 2263 assert(arr2[0..arr.length] == arr); 2264 assert(arr2[arr.length..$] == "123"); 2265 2266 // test postblit on array concat, append, length, etc. 2267 static struct S 2268 { 2269 int x; 2270 int pad; 2271 this(this) 2272 { 2273 ++x; 2274 } 2275 } 2276 void testPostBlit(T)() 2277 { 2278 auto sarr = new T[1]; 2279 debug(SENTINEL) {} else 2280 assert(sarr.capacity == 1); 2281 2282 // length extend 2283 auto sarr2 = sarr; 2284 assert(sarr[0].x == 0); 2285 sarr2.length += 1; 2286 assert(sarr2[0].x == 1); 2287 assert(sarr[0].x == 0); 2288 2289 // append 2290 T s; 2291 sarr2 = sarr; 2292 sarr2 ~= s; 2293 assert(sarr2[0].x == 1); 2294 assert(sarr2[1].x == 1); 2295 assert(sarr[0].x == 0); 2296 assert(s.x == 0); 2297 2298 // concat 2299 sarr2 = sarr ~ sarr; 2300 assert(sarr2[0].x == 1); 2301 assert(sarr2[1].x == 1); 2302 assert(sarr[0].x == 0); 2303 2304 // concat multiple (calls different method) 2305 sarr2 = sarr ~ sarr ~ sarr; 2306 assert(sarr2[0].x == 1); 2307 assert(sarr2[1].x == 1); 2308 assert(sarr2[2].x == 1); 2309 assert(sarr[0].x == 0); 2310 2311 // reserve capacity 2312 sarr2 = sarr; 2313 sarr2.reserve(2); 2314 assert(sarr2[0].x == 1); 2315 assert(sarr[0].x == 0); 2316 } 2317 testPostBlit!(S)(); 2318 testPostBlit!(const(S))(); 2319 } 2320 2321 unittest 2322 { 2323 // Bugzilla 3454 - Inconsistent flag setting in GC.realloc() 2324 static void test(size_t multiplier) 2325 { 2326 auto p = GC.malloc(8 * multiplier, 0); 2327 assert(GC.getAttr(p) == 0); 2328 2329 // no move, set attr 2330 p = GC.realloc(p, 8 * multiplier + 5, BlkAttr.NO_SCAN); 2331 assert(GC.getAttr(p) == BlkAttr.NO_SCAN); 2332 2333 // shrink, copy attr 2334 p = GC.realloc(p, 2 * multiplier, 0); 2335 assert(GC.getAttr(p) == BlkAttr.NO_SCAN); 2336 2337 // extend, copy attr 2338 p = GC.realloc(p, 8 * multiplier, 0); 2339 assert(GC.getAttr(p) == BlkAttr.NO_SCAN); 2340 } 2341 test(16); 2342 test(1024 * 1024); 2343 } 2344 2345 unittest 2346 { 2347 import core.exception; 2348 try 2349 { 2350 size_t x = size_t.max; 2351 byte[] big_buf = new byte[x]; 2352 } 2353 catch (OutOfMemoryError) 2354 { 2355 } 2356 } 2357 2358 unittest 2359 { 2360 // https://issues.dlang.org/show_bug.cgi?id=13854 2361 auto arr = new ubyte[PAGESIZE]; // ensure page size 2362 auto info1 = GC.query(arr.ptr); 2363 assert(info1.base !is arr.ptr); // offset is required for page size or larger 2364 2365 auto arr2 = arr[0..1]; 2366 assert(arr2.capacity == 0); // cannot append 2367 arr2 ~= 0; // add a byte 2368 assert(arr2.ptr !is arr.ptr); // reallocated 2369 auto info2 = GC.query(arr2.ptr); 2370 assert(info2.base is arr2.ptr); // no offset, the capacity is small. 2371 2372 // do the same via setting length 2373 arr2 = arr[0..1]; 2374 assert(arr2.capacity == 0); 2375 arr2.length += 1; 2376 assert(arr2.ptr !is arr.ptr); // reallocated 2377 info2 = GC.query(arr2.ptr); 2378 assert(info2.base is arr2.ptr); // no offset, the capacity is small. 2379 2380 // do the same for char[] since we need a type with an initializer to test certain runtime functions 2381 auto carr = new char[PAGESIZE]; 2382 info1 = GC.query(carr.ptr); 2383 assert(info1.base !is carr.ptr); // offset is required for page size or larger 2384 2385 auto carr2 = carr[0..1]; 2386 assert(carr2.capacity == 0); // cannot append 2387 carr2 ~= 0; // add a byte 2388 assert(carr2.ptr !is carr.ptr); // reallocated 2389 info2 = GC.query(carr2.ptr); 2390 assert(info2.base is carr2.ptr); // no offset, the capacity is small. 2391 2392 // do the same via setting length 2393 carr2 = carr[0..1]; 2394 assert(carr2.capacity == 0); 2395 carr2.length += 1; 2396 assert(carr2.ptr !is carr.ptr); // reallocated 2397 info2 = GC.query(carr2.ptr); 2398 assert(info2.base is carr2.ptr); // no offset, the capacity is small. 2399 } 2400 2401 unittest 2402 { 2403 // https://issues.dlang.org/show_bug.cgi?id=13878 2404 auto arr = new ubyte[1]; 2405 auto info = GC.query(arr.ptr); 2406 assert(info.attr & BlkAttr.NO_SCAN); // should be NO_SCAN 2407 arr ~= 0; // ensure array is inserted into cache 2408 debug(SENTINEL) {} else 2409 assert(arr.ptr is info.base); 2410 GC.clrAttr(arr.ptr, BlkAttr.NO_SCAN); // remove the attribute 2411 auto arr2 = arr[0..1]; 2412 assert(arr2.capacity == 0); // cannot append 2413 arr2 ~= 0; 2414 assert(arr2.ptr !is arr.ptr); 2415 info = GC.query(arr2.ptr); 2416 assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks 2417 2418 // do the same via setting length 2419 arr = new ubyte[1]; 2420 arr ~= 0; // ensure array is inserted into cache 2421 GC.clrAttr(arr.ptr, BlkAttr.NO_SCAN); // remove the attribute 2422 arr2 = arr[0..1]; 2423 assert(arr2.capacity == 0); 2424 arr2.length += 1; 2425 assert(arr2.ptr !is arr.ptr); // reallocated 2426 info = GC.query(arr2.ptr); 2427 assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks 2428 2429 // do the same for char[] since we need a type with an initializer to test certain runtime functions 2430 auto carr = new char[1]; 2431 info = GC.query(carr.ptr); 2432 assert(info.attr & BlkAttr.NO_SCAN); // should be NO_SCAN 2433 carr ~= 0; // ensure array is inserted into cache 2434 debug(SENTINEL) {} else 2435 assert(carr.ptr is info.base); 2436 GC.clrAttr(carr.ptr, BlkAttr.NO_SCAN); // remove the attribute 2437 auto carr2 = carr[0..1]; 2438 assert(carr2.capacity == 0); // cannot append 2439 carr2 ~= 0; 2440 assert(carr2.ptr !is carr.ptr); 2441 info = GC.query(carr2.ptr); 2442 assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks 2443 2444 // do the same via setting length 2445 carr = new char[1]; 2446 carr ~= 0; // ensure array is inserted into cache 2447 GC.clrAttr(carr.ptr, BlkAttr.NO_SCAN); // remove the attribute 2448 carr2 = carr[0..1]; 2449 assert(carr2.capacity == 0); 2450 carr2.length += 1; 2451 assert(carr2.ptr !is carr.ptr); // reallocated 2452 info = GC.query(carr2.ptr); 2453 assert(!(info.attr & BlkAttr.NO_SCAN)); // ensure attribute sticks 2454 } 2455 2456 // test struct finalizers 2457 debug(SENTINEL) {} else 2458 deprecated unittest 2459 { 2460 __gshared int dtorCount; 2461 static struct S1 2462 { 2463 int x; 2464 2465 ~this() 2466 { 2467 dtorCount++; 2468 } 2469 } 2470 2471 dtorCount = 0; 2472 S1* s2 = new S1; 2473 GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]); 2474 assert(dtorCount == 1); 2475 GC.free(s2); 2476 2477 dtorCount = 0; 2478 const(S1)* s3 = new const(S1); 2479 GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]); 2480 assert(dtorCount == 1); 2481 GC.free(cast(void*)s3); 2482 2483 dtorCount = 0; 2484 shared(S1)* s4 = new shared(S1); 2485 GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]); 2486 assert(dtorCount == 1); 2487 GC.free(cast(void*)s4); 2488 2489 dtorCount = 0; 2490 const(S1)[] carr1 = new const(S1)[5]; 2491 BlkInfo blkinf1 = GC.query(carr1.ptr); 2492 GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]); 2493 assert(dtorCount == 5); 2494 GC.free(blkinf1.base); 2495 2496 dtorCount = 0; 2497 S1[] arr2 = new S1[10]; 2498 arr2.length = 6; 2499 arr2.assumeSafeAppend; 2500 assert(dtorCount == 4); // destructors run explicitely? 2501 2502 dtorCount = 0; 2503 BlkInfo blkinf = GC.query(arr2.ptr); 2504 GC.runFinalizers((cast(char*)(typeid(S1).xdtor))[0..1]); 2505 assert(dtorCount == 6); 2506 GC.free(blkinf.base); 2507 2508 // associative arrays 2509 import rt.aaA : entryDtor; 2510 // throw away all existing AA entries with dtor 2511 GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); 2512 2513 S1[int] aa1; 2514 aa1[0] = S1(0); 2515 aa1[1] = S1(1); 2516 dtorCount = 0; 2517 aa1 = null; 2518 GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); 2519 assert(dtorCount == 2); 2520 2521 int[S1] aa2; 2522 aa2[S1(0)] = 0; 2523 aa2[S1(1)] = 1; 2524 aa2[S1(2)] = 2; 2525 dtorCount = 0; 2526 aa2 = null; 2527 GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); 2528 assert(dtorCount == 3); 2529 2530 S1[2][int] aa3; 2531 aa3[0] = [S1(0),S1(2)]; 2532 aa3[1] = [S1(1),S1(3)]; 2533 dtorCount = 0; 2534 aa3 = null; 2535 GC.runFinalizers((cast(char*)(&entryDtor))[0..1]); 2536 assert(dtorCount == 4); 2537 } 2538 2539 // test struct dtor handling not causing false pointers 2540 unittest 2541 { 2542 // for 64-bit, allocate a struct of size 40 2543 static struct S 2544 { 2545 size_t[4] data; 2546 S* ptr4; 2547 } 2548 auto p1 = new S; 2549 auto p2 = new S; 2550 p2.ptr4 = p1; 2551 2552 // a struct with a dtor with size 32, but the dtor will cause 2553 // allocation to be larger by a pointer 2554 static struct A 2555 { 2556 size_t[3] data; 2557 S* ptr3; 2558 2559 ~this() {} 2560 } 2561 2562 GC.free(p2); 2563 auto a = new A; // reuse same memory 2564 if (cast(void*)a is cast(void*)p2) // reusage not guaranteed 2565 { 2566 auto ptr = cast(S**)(a + 1); 2567 assert(*ptr != p1); // still same data as p2.ptr4? 2568 } 2569 2570 // small array 2571 static struct SArr 2572 { 2573 void*[10] data; 2574 } 2575 auto arr1 = new SArr; 2576 arr1.data[] = p1; 2577 GC.free(arr1); 2578 2579 // allocates 2*A.sizeof + (void*).sizeof (TypeInfo) + 1 (array length) 2580 auto arr2 = new A[2]; 2581 if (cast(void*)arr1 is cast(void*)arr2.ptr) // reusage not guaranteed 2582 { 2583 auto ptr = cast(S**)(arr2.ptr + 2); 2584 assert(*ptr != p1); // still same data as p2.ptr4? 2585 } 2586 2587 // large array 2588 static struct LArr 2589 { 2590 void*[1023] data; 2591 } 2592 auto larr1 = new LArr; 2593 larr1.data[] = p1; 2594 GC.free(larr1); 2595 2596 auto larr2 = new S[255]; 2597 if (cast(void*)larr1 is cast(void*)larr2.ptr - LARGEPREFIX) // reusage not guaranteed 2598 { 2599 auto ptr = cast(S**)larr1; 2600 assert(ptr[0] != p1); // 16 bytes array header 2601 assert(ptr[1] != p1); 2602 version (D_LP64) {} else 2603 { 2604 assert(ptr[2] != p1); 2605 assert(ptr[3] != p1); 2606 } 2607 } 2608 } 2609 2610 // test class finalizers exception handling 2611 unittest 2612 { 2613 bool test(E)() 2614 { 2615 import core.exception; 2616 static class C1 2617 { 2618 E exc; 2619 this(E exc) { this.exc = exc; } 2620 ~this() { throw exc; } 2621 } 2622 2623 bool caught = false; 2624 C1 c = new C1(new E("test onFinalizeError")); 2625 try 2626 { 2627 GC.runFinalizers((cast(uint*)&C1.__dtor)[0..1]); 2628 } 2629 catch (FinalizeError err) 2630 { 2631 caught = true; 2632 } 2633 catch (E) 2634 { 2635 } 2636 GC.free(cast(void*)c); 2637 return caught; 2638 } 2639 2640 assert( test!Exception); 2641 import core.exception : InvalidMemoryOperationError; 2642 assert(!test!InvalidMemoryOperationError); 2643 } 2644 2645 // test bug 14126 2646 unittest 2647 { 2648 static struct S 2649 { 2650 S* thisptr; 2651 ~this() { assert(&this == thisptr); thisptr = null;} 2652 } 2653 2654 S[] test14126 = new S[2048]; // make sure we allocate at least a PAGE 2655 foreach (ref s; test14126) 2656 { 2657 s.thisptr = &s; 2658 } 2659 }