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