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