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