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