1 /** 2 * This module contains all functions related to an object's lifetime: 3 * allocation, resizing, deallocation, and finalization. 4 * 5 * Copyright: Copyright (C) 2004-2007 Digital Mars, www.digitalmars.com. 6 * All rights reserved. 7 * License: 8 * This software is provided 'as-is', without any express or implied 9 * warranty. In no event will the authors be held liable for any damages 10 * arising from the use of this software. 11 * 12 * Permission is granted to anyone to use this software for any purpose, 13 * including commercial applications, and to alter it and redistribute it 14 * freely, in both source and binary form, subject to the following 15 * restrictions: 16 * 17 * o The origin of this software must not be misrepresented; you must not 18 * claim that you wrote the original software. If you use this software 19 * in a product, an acknowledgment in the product documentation would be 20 * appreciated but is not required. 21 * o Altered source versions must be plainly marked as such, and must not 22 * be misrepresented as being the original software. 23 * o This notice may not be removed or altered from any source 24 * distribution. 25 * Authors: Walter Bright, Sean Kelly, Tomas Lindquist Olsen 26 */ 27 module rt.lifetime; 28 29 //debug=PRINTF; 30 //debug=PRINTF2; 31 32 private 33 { 34 import tango.stdc.string : memcpy, memset, memcmp; 35 import tango.stdc.stdlib : free, malloc; 36 debug(PRINTF) import tango.stdc.stdio : printf; 37 else debug(PRINTF2) import tango.stdc.stdio : printf; 38 } 39 40 41 private 42 { 43 enum BlkAttr : uint 44 { 45 FINALIZE = 0b0000_0001, 46 NO_SCAN = 0b0000_0010, 47 NO_MOVE = 0b0000_0100, 48 ALL_BITS = 0b1111_1111 49 } 50 51 struct BlkInfo 52 { 53 void* base; 54 size_t size; 55 uint attr; 56 } 57 58 extern (C) uint gc_getAttr( void* p ); 59 extern (C) uint gc_setAttr( void* p, uint a ); 60 extern (C) uint gc_clrAttr( void* p, uint a ); 61 62 extern (C) void* gc_malloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init); 63 extern (C) void* gc_calloc( size_t sz, uint ba = 0, PointerMap bitMask = PointerMap.init); 64 extern (C) size_t gc_extend( void* p, size_t mx, size_t sz ); 65 extern (C) void gc_free( void* p ); 66 67 extern (C) void* gc_addrOf( void* p ); 68 extern (C) size_t gc_sizeOf( void* p ); 69 extern (C) BlkInfo gc_query( void* p ); 70 71 extern (C) bool onCollectResource( Object o ); 72 extern (C) void onFinalizeError( ClassInfo c, Exception e ); 73 extern (C) void onOutOfMemoryError(); 74 75 extern (C) void _d_monitordelete(Object h, bool det = true); 76 77 enum 78 { 79 PAGESIZE = 4096 80 } 81 82 alias bool function(Object) CollectHandler; 83 CollectHandler collectHandler = null; 84 85 size_t length_adjust(size_t sizeelem, size_t newlength) 86 { 87 size_t newsize = void; 88 static if (size_t.sizeof < ulong.sizeof) 89 { 90 ulong s = cast(ulong)sizeelem * cast(ulong)newlength; 91 if (s > size_t.max) 92 onOutOfMemoryError(); 93 newsize = cast(size_t)s; 94 } 95 else 96 { 97 newsize = sizeelem * newlength; 98 if (newsize / newlength != sizeelem) 99 onOutOfMemoryError(); 100 } 101 return newsize; 102 } 103 } 104 105 106 /** 107 * 108 */ 109 extern (C) Object _d_allocclass(ClassInfo ci) 110 { 111 void* p; 112 113 debug(PRINTF2) printf("_d_allocclass(ci = %p, %s)\n", ci, cast(char *)ci.name.ptr); 114 /+ 115 if (ci.flags & 1) // if COM object 116 { /* COM objects are not garbage collected, they are reference counted 117 * using AddRef() and Release(). They get free'd by C's free() 118 * function called by Release() when Release()'s reference count goes 119 * to zero. 120 */ 121 p = tango.stdc.stdlib.malloc(ci.init.length); 122 if (!p) 123 onOutOfMemoryError(); 124 } 125 else 126 +/ 127 { 128 PointerMap pm; 129 version (D_HavePointerMap) { 130 pm = ci.pointermap; 131 } 132 p = gc_malloc(ci.init.length, 133 BlkAttr.FINALIZE | (ci.flags & 2 ? BlkAttr.NO_SCAN : 0), 134 pm); 135 debug(PRINTF2) printf(" p = %p\n", p); 136 } 137 138 debug(PRINTF2) 139 { 140 printf("p = %p\n", p); 141 printf("ci = %p, ci.init = %p, len = %d\n", ci, ci.init.ptr, ci.init.length); 142 printf("vptr = %p\n", *cast(void**) ci.init.ptr); 143 printf("vtbl[0] = %p\n", (*cast(void***) ci.init.ptr)[0]); 144 printf("vtbl[1] = %p\n", (*cast(void***) ci.init.ptr)[1]); 145 printf("init[0] = %p\n", (cast(uint**) ci.init.ptr)[0]); 146 printf("init[1] = %p\n", (cast(uint**) ci.init.ptr)[1]); 147 printf("init[2] = %p\n", (cast(uint**) ci.init.ptr)[2]); 148 printf("init[3] = %p\n", (cast(uint**) ci.init.ptr)[3]); 149 printf("init[4] = %p\n", (cast(uint**) ci.init.ptr)[4]); 150 } 151 152 // initialize it 153 // ldc does this inline 154 //(cast(byte*) p)[0 .. ci.init.length] = ci.init[]; 155 156 debug(PRINTF) printf("initialization done\n"); 157 return cast(Object) p; 158 } 159 160 /** 161 * 162 */ 163 extern (C) void _d_delinterface(void* p) 164 { 165 if (p) 166 { 167 Interface* pi = **cast(Interface ***)p; 168 Object o = cast(Object)(p - pi.offset); 169 170 _d_delclass(o); 171 //*p = null; 172 } 173 } 174 175 // used for deletion 176 private extern (D) alias void function(Object) fp_t; 177 178 179 /** 180 * 181 */ 182 extern (C) void _d_delclass(Object p) 183 { 184 if (p) 185 { 186 debug(PRINTF) printf("_d_delclass(%p)\n", p); 187 188 ClassInfo **pc = cast(ClassInfo **)p; 189 if (*pc) 190 { 191 ClassInfo c = **pc; 192 193 rt_finalize(cast(void*) p); 194 195 if (c.deallocator) 196 { 197 fp_t fp = cast(fp_t)c.deallocator; 198 (*fp)(p); // call deallocator 199 //*p = null; 200 return; 201 } 202 } 203 else 204 { 205 rt_finalize(cast(void*) p); 206 } 207 gc_free(cast(void*) p); 208 //*p = null; 209 } 210 } 211 212 /+ 213 214 /** 215 * 216 */ 217 struct Array 218 { 219 size_t length; 220 void* data; 221 } 222 223 +/ 224 225 /** 226 * Allocate a new array of length elements. 227 * ti is the type of the resulting array, or pointer to element. 228 * The resulting array is initialized to 0 229 */ 230 extern (C) void* _d_newarrayT(TypeInfo ti, size_t length) 231 { 232 void* p; 233 auto size = ti.next.tsize(); // array element size 234 235 debug(PRINTF) printf("_d_newarrayT(length = %u, size = %d)\n", length, size); 236 if (length == 0 || size == 0) 237 return null; 238 239 size = length_adjust(size, length); 240 241 PointerMap pm; 242 version (D_HavePointerMap) { 243 pm = ti.next.pointermap(); 244 } 245 p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm); 246 debug(PRINTF) printf(" p = %p\n", p); 247 memset(p, 0, size); 248 249 return p; 250 } 251 252 /** 253 * As _d_newarrayT, but 254 * for when the array has a non-zero initializer. 255 */ 256 extern (C) void* _d_newarrayiT(TypeInfo ti, size_t length) 257 { 258 void* result; 259 auto size = ti.next.tsize(); // array element size 260 261 debug(PRINTF) printf("_d_newarrayiT(length = %d, size = %d)\n", length, size); 262 263 if (length == 0 || size == 0) 264 result = null; 265 else 266 { 267 auto initializer = ti.next.init(); 268 auto isize = initializer.length; 269 auto q = initializer.ptr; 270 271 size = length_adjust(size, length); 272 273 PointerMap pm; 274 version (D_HavePointerMap) { 275 pm = ti.next.pointermap(); 276 } 277 auto p = gc_malloc(size + 1, 278 !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, 279 pm); 280 debug(PRINTF) printf(" p = %p\n", p); 281 if (isize == 1) 282 memset(p, *cast(ubyte*)q, size); 283 else if (isize == int.sizeof) 284 { 285 int init = *cast(int*)q; 286 size /= int.sizeof; 287 for (size_t u = 0; u < size; u++) 288 { 289 (cast(int*)p)[u] = init; 290 } 291 } 292 else 293 { 294 for (size_t u = 0; u < size; u += isize) 295 { 296 memcpy(p + u, q, isize); 297 } 298 } 299 result = p; 300 } 301 return result; 302 } 303 304 /** 305 * As _d_newarrayT, but without initialization 306 */ 307 extern (C) void* _d_newarrayvT(TypeInfo ti, size_t length) 308 { 309 void* p; 310 auto size = ti.next.tsize(); // array element size 311 312 debug(PRINTF) printf("_d_newarrayvT(length = %u, size = %d)\n", length, size); 313 if (length == 0 || size == 0) 314 return null; 315 316 size = length_adjust(size, length); 317 318 PointerMap pm; 319 version (D_HavePointerMap) { 320 pm = ti.next.pointermap(); 321 } 322 p = gc_malloc(size + 1, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm); 323 debug(PRINTF) printf(" p = %p\n", p); 324 return p; 325 } 326 327 /** 328 * Allocate a new array of arrays of arrays of arrays ... 329 * ti is the type of the resulting array. 330 * ndims is the number of nested arrays. 331 * dims it the array of dimensions, its size is ndims. 332 * The resulting array is initialized to 0 333 */ 334 extern (C) void* _d_newarraymT(TypeInfo ti, int ndims, size_t* dims) 335 { 336 void* result; 337 338 debug(PRINTF) printf("_d_newarraymT(ndims = %d)\n", ndims); 339 if (ndims == 0) 340 result = null; 341 else 342 { 343 static void[] foo(TypeInfo ti, size_t* pdim, int ndims) 344 { 345 size_t dim = *pdim; 346 void[] p; 347 348 debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims); 349 if (ndims == 1) 350 { 351 auto r = _d_newarrayT(ti, dim); 352 return r[0 .. dim]; 353 } 354 else 355 { 356 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim]; 357 for (int i = 0; i < dim; i++) 358 { 359 (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1); 360 } 361 } 362 return p; 363 } 364 365 result = foo(ti, dims, ndims).ptr; 366 debug(PRINTF) printf("result = %p\n", result); 367 368 version (none) 369 { 370 for (int i = 0; i < ndims; i++) 371 { 372 printf("index %d: %d\n", i, *dims++); 373 } 374 } 375 } 376 return result; 377 } 378 379 380 /** 381 * As _d_newarraymT, but 382 * for when the array has a non-zero initializer. 383 */ 384 extern (C) void* _d_newarraymiT(TypeInfo ti, int ndims, size_t* dims) 385 { 386 void* result; 387 388 debug(PRINTF) printf("_d_newarraymiT(ndims = %d)\n", ndims); 389 if (ndims == 0) 390 result = null; 391 else 392 { 393 static void[] foo(TypeInfo ti, size_t* pdim, int ndims) 394 { 395 size_t dim = *pdim; 396 void[] p; 397 398 if (ndims == 1) 399 { 400 auto r = _d_newarrayiT(ti, dim); 401 p = r[0 .. dim]; 402 } 403 else 404 { 405 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim]; 406 for (int i = 0; i < dim; i++) 407 { 408 (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1); 409 } 410 } 411 return p; 412 } 413 414 result = foo(ti, dims, ndims).ptr; 415 debug(PRINTF) printf("result = %p\n", result); 416 417 version (none) 418 { 419 for (int i = 0; i < ndims; i++) 420 { 421 printf("index %d: %d\n", i, *dims++); 422 printf("init = %d\n", *dims++); 423 } 424 } 425 } 426 return result; 427 } 428 429 /** 430 * As _d_newarraymT, but without initialization 431 */ 432 extern (C) void* _d_newarraymvT(TypeInfo ti, int ndims, size_t* dims) 433 { 434 void* result; 435 436 debug(PRINTF) printf("_d_newarraymvT(ndims = %d)\n", ndims); 437 if (ndims == 0) 438 result = null; 439 else 440 { 441 static void[] foo(TypeInfo ti, size_t* pdim, int ndims) 442 { 443 size_t dim = *pdim; 444 void[] p; 445 446 debug(PRINTF) printf("foo(ti = %p, ti.next = %p, dim = %d, ndims = %d\n", ti, ti.next, dim, ndims); 447 if (ndims == 1) 448 { 449 auto r = _d_newarrayvT(ti, dim); 450 return r[0 .. dim]; 451 } 452 else 453 { 454 p = gc_malloc(dim * (void[]).sizeof + 1)[0 .. dim]; 455 for (int i = 0; i < dim; i++) 456 { 457 (cast(void[]*)p.ptr)[i] = foo(ti.next, pdim + 1, ndims - 1); 458 } 459 } 460 return p; 461 } 462 463 result = foo(ti, dims, ndims).ptr; 464 debug(PRINTF) printf("result = %p\n", result); 465 466 version (none) 467 { 468 for (int i = 0; i < ndims; i++) 469 { 470 printf("index %d: %d\n", i, *dims++); 471 } 472 } 473 } 474 return result; 475 } 476 477 /+ 478 479 /** 480 * 481 */ 482 void* _d_allocmemory(size_t nbytes) 483 { 484 return gc_malloc(nbytes); 485 } 486 487 +/ 488 489 /** 490 * for allocating a single POD value 491 */ 492 extern (C) void* _d_allocmemoryT(TypeInfo ti) 493 { 494 PointerMap pm; 495 version (D_HavePointerMap) { 496 pm = ti.pointermap(); 497 } 498 return gc_malloc(ti.tsize(), !(ti.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm); 499 } 500 501 /** 502 * 503 */ 504 extern (C) void _d_delarray(size_t plength, void* pdata) 505 { 506 // if (p) 507 // { 508 // This assert on array consistency may fail with casts or in unions. 509 // This function still does something sensible even if plength && !pdata. 510 // assert(!plength || pdata); 511 512 if (pdata) 513 gc_free(pdata); 514 // p.data = null; 515 // p.length = 0; 516 // } 517 } 518 519 /** 520 * 521 */ 522 extern (C) void _d_delmemory(void* p) 523 { 524 if (p) 525 { 526 gc_free(p); 527 //*p = null; 528 } 529 } 530 531 /** 532 * 533 */ 534 extern (C) void _d_callinterfacefinalizer(void *p) 535 { 536 if (p) 537 { 538 Interface *pi = **cast(Interface ***)p; 539 Object o = cast(Object)(p - pi.offset); 540 rt_finalize(cast(void*)o); 541 } 542 } 543 544 /** 545 * 546 */ 547 extern (C) void _d_callfinalizer(void* p) 548 { 549 rt_finalize( p ); 550 } 551 552 553 /** 554 * 555 */ 556 extern (C) void rt_setCollectHandler(CollectHandler h) 557 { 558 collectHandler = h; 559 } 560 561 /** 562 * 563 */ 564 extern (C) void rt_finalize(void* p, bool det = true) 565 { 566 debug(PRINTF) printf("rt_finalize(p = %p)\n", p); 567 568 if (p) // not necessary if called from gc 569 { 570 if (det) 571 (cast(Object)p).dispose(); 572 573 ClassInfo** pc = cast(ClassInfo**)p; 574 575 if (*pc) 576 { 577 ClassInfo c = **pc; 578 579 try 580 { 581 if (det || collectHandler is null || collectHandler(cast(Object)p)) 582 { 583 do 584 { 585 if (c.destructor !is null) 586 { 587 debug(PRINTF) printf("calling dtor of %.*s\n", c.name.length, c.name.ptr); 588 void delegate() dg; 589 dg.ptr = p; 590 dg.funcptr = cast(void function()) c.destructor; 591 dg(); // call destructor 592 } 593 c = c.base; 594 } while (c); 595 } 596 if ((cast(void**)p)[1]) // if monitor is not null 597 _d_monitordelete(cast(Object)p, det); 598 } 599 catch (Exception e) 600 { 601 onFinalizeError(**pc, e); 602 } 603 finally 604 { 605 *pc = null; // zero vptr 606 } 607 } 608 } 609 } 610 611 /** 612 * Resize dynamic arrays with 0 initializers. 613 */ 614 extern (C) byte* _d_arraysetlengthT(TypeInfo ti, size_t newlength, size_t plength, byte* pdata) 615 in 616 { 617 assert(ti); 618 // This assert on array consistency may fail with casts or in unions. 619 // This function still does something sensible even if plength && !pdata. 620 // assert(!plength || pdata); 621 } 622 body 623 { 624 byte* newdata; 625 size_t sizeelem = ti.next.tsize(); 626 627 debug(PRINTF) 628 { 629 printf("_d_arraysetlengthT(sizeelem = %d, newlength = %d)\n", sizeelem, newlength); 630 printf("\tp.data = %p, p.length = %d\n", pdata, plength); 631 } 632 633 if (newlength) 634 { 635 size_t newsize = length_adjust(sizeelem, newlength); 636 637 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); 638 639 if (pdata) 640 { 641 newdata = pdata; 642 if (newlength > plength) 643 { 644 size_t size = plength * sizeelem; 645 auto info = gc_query(pdata); 646 647 if (info.size <= newsize || info.base != pdata) 648 { 649 if (info.size >= PAGESIZE && info.base == pdata) 650 { // Try to extend in-place 651 auto u = gc_extend(pdata, (newsize + 1) - info.size, (newsize + 1) - info.size); 652 if (u) 653 { 654 goto L1; 655 } 656 } 657 PointerMap pm; 658 version (D_HavePointerMap) { 659 pm = ti.next.pointermap(); 660 } 661 newdata = cast(byte *)gc_malloc(newsize + 1, 662 !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, 663 pm); 664 newdata[0 .. size] = pdata[0 .. size]; 665 } 666 L1: 667 newdata[size .. newsize] = 0; 668 } 669 } 670 else 671 { 672 PointerMap pm; 673 version (D_HavePointerMap) { 674 pm = ti.next.pointermap(); 675 } 676 newdata = cast(byte *)gc_calloc(newsize + 1, 677 !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, 678 pm); 679 } 680 } 681 else 682 { 683 newdata = pdata; 684 } 685 686 return newdata; 687 } 688 689 690 /** 691 * Resize arrays for non-zero initializers. 692 * p pointer to array lvalue to be updated 693 * newlength new .length property of array 694 * sizeelem size of each element of array 695 * initsize size of initializer 696 * ... initializer 697 */ 698 extern (C) byte* _d_arraysetlengthiT(TypeInfo ti, size_t newlength, size_t plength, byte* pdata) 699 in 700 { 701 // This assert on array consistency may fail with casts or in unions. 702 // This function still does something sensible even if plength && !pdata. 703 // assert(!plength || pdata); 704 } 705 body 706 { 707 byte* newdata; 708 size_t sizeelem = ti.next.tsize(); 709 void[] initializer = ti.next.init(); 710 size_t initsize = initializer.length; 711 712 assert(sizeelem); 713 assert(initsize); 714 assert(initsize <= sizeelem); 715 assert((sizeelem / initsize) * initsize == sizeelem); 716 717 debug(PRINTF) 718 { 719 printf("_d_arraysetlengthiT(sizeelem = %d, newlength = %d, initsize = %d)\n", sizeelem, newlength, initsize); 720 printf("\tp.data = %p, p.length = %d\n", pdata, plength); 721 } 722 723 if (newlength) 724 { 725 size_t newsize = length_adjust(sizeelem, newlength); 726 debug(PRINTF) printf("newsize = %x, newlength = %x\n", newsize, newlength); 727 728 size_t size = plength * sizeelem; 729 730 if (pdata) 731 { 732 newdata = pdata; 733 if (newlength > plength) 734 { 735 auto info = gc_query(pdata); 736 737 if (info.size <= newsize || info.base != pdata) 738 { 739 if (info.size >= PAGESIZE && info.base == pdata) 740 { // Try to extend in-place 741 auto u = gc_extend(pdata, (newsize + 1) - info.size, (newsize + 1) - info.size); 742 if (u) 743 { 744 goto L1; 745 } 746 } 747 PointerMap pm; 748 version (D_HavePointerMap) { 749 pm = ti.next.pointermap(); 750 } 751 newdata = cast(byte *)gc_malloc(newsize + 1, 752 !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, 753 pm); 754 newdata[0 .. size] = pdata[0 .. size]; 755 L1: ; 756 } 757 } 758 } 759 else 760 { 761 PointerMap pm; 762 version (D_HavePointerMap) { 763 pm = ti.next.pointermap(); 764 } 765 newdata = cast(byte *)gc_malloc(newsize + 1, 766 !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, 767 pm); 768 } 769 770 auto q = initializer.ptr; // pointer to initializer 771 772 if (newsize > size) 773 { 774 if (initsize == 1) 775 { 776 debug(PRINTF) printf("newdata = %p, size = %d, newsize = %d, *q = %d\n", newdata, size, newsize, *cast(byte*)q); 777 newdata[size .. newsize] = *(cast(byte*)q); 778 } 779 else 780 { 781 for (size_t u = size; u < newsize; u += initsize) 782 { 783 memcpy(newdata + u, q, initsize); 784 } 785 } 786 } 787 } 788 else 789 { 790 newdata = pdata; 791 } 792 793 return newdata; 794 795 Loverflow: 796 onOutOfMemoryError(); 797 return null; 798 } 799 800 /+ 801 802 /** 803 * Append y[] to array x[]. 804 * size is size of each array element. 805 */ 806 extern (C) long _d_arrayappendT(TypeInfo ti, Array *px, byte[] y) 807 { 808 auto sizeelem = ti.next.tsize(); // array element size 809 auto info = gc_query(px.data); 810 auto length = px.length; 811 auto newlength = length + y.length; 812 auto newsize = newlength * sizeelem; 813 814 if (info.size < newsize || info.base != px.data) 815 { byte* newdata; 816 817 if (info.size >= PAGESIZE && info.base == px.data) 818 { // Try to extend in-place 819 auto u = gc_extend(px.data, (newsize + 1) - info.size, (newsize + 1) - info.size); 820 if (u) 821 { 822 goto L1; 823 } 824 } 825 PointerMap pm; 826 version (D_HavePointerMap) { 827 pm = ti.next.pointermap(); 828 } 829 uint attr = !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0; 830 PointerMap pm; 831 version (D_HavePointerMap) { 832 pm = ti.next.pointermap(); 833 } 834 newdata = cast(byte *)gc_malloc(newCapacity(newlength, sizeelem) + 1, attr, pm); 835 memcpy(newdata, px.data, length * sizeelem); 836 px.data = newdata; 837 } 838 L1: 839 px.length = newlength; 840 memcpy(px.data + length * sizeelem, y.ptr, y.length * sizeelem); 841 return *cast(long*)px; 842 } 843 844 +/ 845 846 /** 847 * 848 */ 849 size_t newCapacity(size_t newlength, size_t size) 850 { 851 version(none) 852 { 853 size_t newcap = newlength * size; 854 } 855 else 856 { 857 /* 858 * Better version by Fawzi, inspired by the one of Dave Fladebo: 859 * This uses an inverse logorithmic algorithm to pre-allocate a bit more 860 * space for larger arrays. 861 * - Arrays smaller than PAGESIZE bytes are left as-is, so for the most 862 * common cases, memory allocation is 1 to 1. The small overhead added 863 * doesn't affect small array perf. (it's virtually the same as 864 * current). 865 * - Larger arrays have some space pre-allocated. 866 * - As the arrays grow, the relative pre-allocated space shrinks. 867 * - The logorithmic algorithm allocates relatively more space for 868 * mid-size arrays, making it very fast for medium arrays (for 869 * mid-to-large arrays, this turns out to be quite a bit faster than the 870 * equivalent realloc() code in C, on Linux at least. Small arrays are 871 * just as fast as GCC). 872 * - Perhaps most importantly, overall memory usage and stress on the GC 873 * is decreased significantly for demanding environments. 874 */ 875 size_t newcap = newlength * size; 876 size_t newext = 0; 877 878 if (newcap > PAGESIZE) 879 { 880 const size_t b=0; // flatness factor, how fast the extra space decreases with array size 881 const size_t a=100; // allocate at most a% of the requested size as extra space (rounding will change this) 882 const size_t minBits=1; // minimum bit size 883 884 885 static size_t log2plusB(size_t c) 886 { 887 // could use the bsr bit op 888 size_t i=b+1; 889 while(c >>= 1){ 890 ++i; 891 } 892 return i; 893 } 894 long mult = 100 + a*(minBits+b) / log2plusB(newlength); 895 896 newext = cast(size_t)((newcap * mult) / 100); 897 newext += size-(newext % size); // round up 898 debug(PRINTF) printf("mult: %2.2f, alloc: %2.2f\n",mult/100.0,newext / cast(double)size); 899 } 900 newcap = newext > newcap ? newext : newcap; // just to handle overflows 901 debug(PRINTF) printf("newcap = %d, newlength = %d, size = %d\n", newcap, newlength, size); 902 } 903 return newcap; 904 } 905 906 907 /** 908 * Appends a single element to an array. 909 */ 910 extern (C) byte[] _d_arrayappendcT(TypeInfo ti, void* array, void* element) 911 { 912 auto x = cast(byte[]*)array; 913 auto sizeelem = ti.next.tsize(); // array element size 914 auto info = gc_query(x.ptr); 915 auto length = x.length; 916 auto newlength = length + 1; 917 auto newsize = newlength * sizeelem; 918 919 assert(info.size == 0 || length * sizeelem <= info.size); 920 921 debug(PRINTF) printf("_d_arrayappendcT(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size); 922 923 if (info.size <= newsize || info.base != x.ptr) 924 { byte* newdata; 925 926 if (info.size >= PAGESIZE && info.base == x.ptr) 927 { // Try to extend in-place 928 auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size); 929 if (u) 930 { 931 goto L1; 932 } 933 } 934 debug(PRINTF) printf("_d_arrayappendcT(length = %d, newlength = %d, cap = %d, ti=%.*s)\n", length, newlength, info.size, ti.toString()); 935 auto newcap = newCapacity(newlength, sizeelem); 936 assert(newcap >= newlength * sizeelem); 937 PointerMap pm; 938 version (D_HavePointerMap) { 939 pm = ti.next.pointermap(); 940 } 941 uint attr = !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0; 942 newdata = cast(byte *)gc_malloc(newcap + 1, attr, pm); 943 memcpy(newdata, x.ptr, length * sizeelem); 944 (cast(void**)x)[1] = newdata; 945 } 946 L1: 947 byte *argp = cast(byte *)element; 948 949 *cast(size_t *)x = newlength; 950 x.ptr[length * sizeelem .. newsize] = argp[0 .. sizeelem]; 951 assert((cast(size_t)x.ptr & 15) == 0); 952 assert(gc_sizeOf(x.ptr) > x.length * sizeelem); 953 return *x; 954 } 955 956 957 /** 958 * Append dchar to char[] 959 */ 960 extern (C) char[] _d_arrayappendcd(ref char[] x, dchar c) 961 { 962 const sizeelem = c.sizeof; // array element size 963 auto info = gc_query(x.ptr); 964 auto length = x.length; 965 966 // c could encode into from 1 to 4 characters 967 int nchars; 968 if (c <= 0x7F) 969 nchars = 1; 970 else if (c <= 0x7FF) 971 nchars = 2; 972 else if (c <= 0xFFFF) 973 nchars = 3; 974 else if (c <= 0x10FFFF) 975 nchars = 4; 976 else 977 assert(0); // invalid utf character - should we throw an exception instead? 978 979 auto newlength = length + nchars; 980 auto newsize = newlength * sizeelem; 981 982 assert(info.size == 0 || length * sizeelem <= info.size); 983 984 debug(PRINTF) printf("_d_arrayappendcd(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size); 985 986 if (info.size <= newsize || info.base != x.ptr) 987 { byte* newdata; 988 989 if (info.size >= PAGESIZE && info.base == x.ptr) 990 { // Try to extend in-place 991 auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size); 992 if (u) 993 { 994 goto L1; 995 } 996 } 997 debug(PRINTF) printf("_d_arrayappendcd(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size); 998 auto newcap = newCapacity(newlength, sizeelem); 999 assert(newcap >= newlength * sizeelem); 1000 newdata = cast(byte *)gc_malloc(newcap + 1, BlkAttr.NO_SCAN); 1001 memcpy(newdata, x.ptr, length * sizeelem); 1002 (cast(void**)(&x))[1] = newdata; 1003 } 1004 L1: 1005 *cast(size_t *)&x = newlength; 1006 char* ptr = &x.ptr[length]; 1007 1008 if (c <= 0x7F) 1009 { 1010 ptr[0] = cast(char) c; 1011 } 1012 else if (c <= 0x7FF) 1013 { 1014 ptr[0] = cast(char)(0xC0 | (c >> 6)); 1015 ptr[1] = cast(char)(0x80 | (c & 0x3F)); 1016 } 1017 else if (c <= 0xFFFF) 1018 { 1019 ptr[0] = cast(char)(0xE0 | (c >> 12)); 1020 ptr[1] = cast(char)(0x80 | ((c >> 6) & 0x3F)); 1021 ptr[2] = cast(char)(0x80 | (c & 0x3F)); 1022 } 1023 else if (c <= 0x10FFFF) 1024 { 1025 ptr[0] = cast(char)(0xF0 | (c >> 18)); 1026 ptr[1] = cast(char)(0x80 | ((c >> 12) & 0x3F)); 1027 ptr[2] = cast(char)(0x80 | ((c >> 6) & 0x3F)); 1028 ptr[3] = cast(char)(0x80 | (c & 0x3F)); 1029 } 1030 else 1031 assert(0); 1032 1033 assert((cast(size_t)x.ptr & 15) == 0); 1034 assert(gc_sizeOf(x.ptr) > x.length * sizeelem); 1035 return x; 1036 } 1037 1038 1039 /** 1040 * Append dchar to wchar[] 1041 */ 1042 extern (C) wchar[] _d_arrayappendwd(ref wchar[] x, dchar c) 1043 { 1044 const sizeelem = c.sizeof; // array element size 1045 auto info = gc_query(x.ptr); 1046 auto length = x.length; 1047 1048 // c could encode into from 1 to 2 w characters 1049 int nchars; 1050 if (c <= 0xFFFF) 1051 nchars = 1; 1052 else 1053 nchars = 2; 1054 1055 auto newlength = length + nchars; 1056 auto newsize = newlength * sizeelem; 1057 1058 assert(info.size == 0 || length * sizeelem <= info.size); 1059 1060 debug(PRINTF) printf("_d_arrayappendwd(sizeelem = %d, ptr = %p, length = %d, cap = %d)\n", sizeelem, x.ptr, x.length, info.size); 1061 1062 if (info.size <= newsize || info.base != x.ptr) 1063 { byte* newdata; 1064 1065 if (info.size >= PAGESIZE && info.base == x.ptr) 1066 { // Try to extend in-place 1067 auto u = gc_extend(x.ptr, (newsize + 1) - info.size, (newsize + 1) - info.size); 1068 if (u) 1069 { 1070 goto L1; 1071 } 1072 } 1073 debug(PRINTF) printf("_d_arrayappendwd(length = %d, newlength = %d, cap = %d)\n", length, newlength, info.size); 1074 auto newcap = newCapacity(newlength, sizeelem); 1075 assert(newcap >= newlength * sizeelem); 1076 newdata = cast(byte *)gc_malloc(newcap + 1, BlkAttr.NO_SCAN); 1077 memcpy(newdata, x.ptr, length * sizeelem); 1078 (cast(void**)(&x))[1] = newdata; 1079 } 1080 L1: 1081 *cast(size_t *)&x = newlength; 1082 wchar* ptr = &x.ptr[length]; 1083 1084 if (c <= 0xFFFF) 1085 { 1086 ptr[0] = cast(wchar) c; 1087 } 1088 else 1089 { 1090 ptr[0] = cast(wchar) ((((c - 0x10000) >> 10) & 0x3FF) + 0xD800); 1091 ptr[1] = cast(wchar) (((c - 0x10000) & 0x3FF) + 0xDC00); 1092 } 1093 1094 assert((cast(size_t)x.ptr & 15) == 0); 1095 assert(gc_sizeOf(x.ptr) > x.length * sizeelem); 1096 return x; 1097 } 1098 1099 1100 /** 1101 * 1102 */ 1103 extern (C) byte[] _d_arraycatT(TypeInfo ti, byte[] x, byte[] y) 1104 out (result) 1105 { 1106 auto sizeelem = ti.next.tsize(); // array element size 1107 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); 1108 assert(result.length == x.length + y.length); 1109 for (size_t i = 0; i < x.length * sizeelem; i++) 1110 assert((cast(byte*)result)[i] == (cast(byte*)x)[i]); 1111 for (size_t i = 0; i < y.length * sizeelem; i++) 1112 assert((cast(byte*)result)[x.length * sizeelem + i] == (cast(byte*)y)[i]); 1113 1114 size_t cap = gc_sizeOf(result.ptr); 1115 assert(!cap || cap > result.length * sizeelem); 1116 } 1117 body 1118 { 1119 version (none) 1120 { 1121 /* Cannot use this optimization because: 1122 * char[] a, b; 1123 * char c = 'a'; 1124 * b = a ~ c; 1125 * c = 'b'; 1126 * will change the contents of b. 1127 */ 1128 if (!y.length) 1129 return x; 1130 if (!x.length) 1131 return y; 1132 } 1133 1134 debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p)\n", x.length, x.ptr, y.length, y.ptr); 1135 auto sizeelem = ti.next.tsize(); // array element size 1136 debug(PRINTF) printf("_d_arraycatT(%d,%p ~ %d,%p sizeelem = %d)\n", x.length, x.ptr, y.length, y.ptr, sizeelem); 1137 size_t xlen = x.length * sizeelem; 1138 size_t ylen = y.length * sizeelem; 1139 size_t len = xlen + ylen; 1140 1141 if (!len) 1142 return null; 1143 1144 PointerMap pm; 1145 version (D_HavePointerMap) { 1146 pm = ti.next.pointermap(); 1147 } 1148 byte* p = cast(byte*)gc_malloc(len + 1, 1149 !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, 1150 pm); 1151 memcpy(p, x.ptr, xlen); 1152 memcpy(p + xlen, y.ptr, ylen); 1153 p[len] = 0; 1154 return p[0 .. x.length + y.length]; 1155 } 1156 1157 1158 /** 1159 * 1160 */ 1161 extern (C) byte[] _d_arraycatnT(TypeInfo ti, uint n, ...) 1162 { void* a; 1163 size_t length; 1164 byte[]* p; 1165 uint i; 1166 byte[] b; 1167 auto size = ti.next.tsize(); // array element size 1168 1169 p = cast(byte[]*)(&n + 1); 1170 1171 for (i = 0; i < n; i++) 1172 { 1173 b = *p++; 1174 length += b.length; 1175 } 1176 if (!length) 1177 return null; 1178 1179 PointerMap pm; 1180 version (D_HavePointerMap) { 1181 pm = ti.next.pointermap(); 1182 } 1183 a = gc_malloc(length * size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm); 1184 p = cast(byte[]*)(&n + 1); 1185 1186 uint j = 0; 1187 for (i = 0; i < n; i++) 1188 { 1189 b = *p++; 1190 if (b.length) 1191 { 1192 memcpy(a + j, b.ptr, b.length * size); 1193 j += b.length * size; 1194 } 1195 } 1196 1197 byte[] result; 1198 *cast(size_t *)&result = length; // jam length 1199 (cast(void **)&result)[1] = a; // jam ptr 1200 return result; 1201 } 1202 1203 /+ 1204 1205 /** 1206 * 1207 */ 1208 extern (C) void* _d_arrayliteralT(TypeInfo ti, size_t length, ...) 1209 { 1210 auto sizeelem = ti.next.tsize(); // array element size 1211 void* result; 1212 1213 debug(PRINTF) printf("_d_arrayliteralT(sizeelem = %d, length = %d)\n", sizeelem, length); 1214 if (length == 0 || sizeelem == 0) 1215 result = null; 1216 else 1217 { 1218 PointerMap pm; 1219 version (D_HavePointerMap) { 1220 pm = ti.next.pointermap(); 1221 } 1222 result = gc_malloc(length * sizeelem, 1223 !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, 1224 pm); 1225 1226 va_list q; 1227 va_start!(size_t)(q, length); 1228 1229 size_t stacksize = (sizeelem + int.sizeof - 1) & ~(int.sizeof - 1); 1230 1231 if (stacksize == sizeelem) 1232 { 1233 memcpy(result, q, length * sizeelem); 1234 } 1235 else 1236 { 1237 for (size_t i = 0; i < length; i++) 1238 { 1239 memcpy(result + i * sizeelem, q, sizeelem); 1240 q += stacksize; 1241 } 1242 } 1243 1244 va_end(q); 1245 } 1246 return result; 1247 } 1248 1249 +/ 1250 1251 1252 /** 1253 * Support for array.dup property. 1254 * The actual type is painted on the return value by the frontend 1255 * Given length is number of elements 1256 * Returned length is number of elements 1257 */ 1258 1259 1260 /** 1261 * 1262 */ 1263 extern (C) void[] _adDupT(TypeInfo ti, void[] a) 1264 out (result) 1265 { 1266 auto sizeelem = ti.next.tsize(); // array element size 1267 assert(memcmp(result.ptr, a.ptr, a.length * sizeelem) == 0); 1268 } 1269 body 1270 { 1271 void* ptr; 1272 1273 if (a.length) 1274 { 1275 auto sizeelem = ti.next.tsize(); // array element size 1276 auto size = a.length * sizeelem; 1277 PointerMap pm; 1278 version (D_HavePointerMap) { 1279 pm = ti.next.pointermap(); 1280 } 1281 ptr = gc_malloc(size, !(ti.next.flags() & 1) ? BlkAttr.NO_SCAN : 0, pm); 1282 memcpy(ptr, a.ptr, size); 1283 } 1284 return ptr[0 .. a.length]; 1285 } 1286 1287 1288 unittest 1289 { 1290 int[] a; 1291 int[] b; 1292 int i; 1293 1294 a = new int[3]; 1295 a[0] = 1; a[1] = 2; a[2] = 3; 1296 b = a.dup; 1297 assert(b.length == 3); 1298 for (i = 0; i < 3; i++) 1299 assert(b[i] == i + 1); 1300 }