1 /****************************************************************************** 2 3 Manages an array buffer for better incremental appending performance 4 5 Manages an array buffer for better performance when elements are 6 incrementally appended to an array. 7 8 Note that, as with each dynamic array, changing the length invalidates 9 existing slices to the array, potentially turning them into dangling 10 references. 11 12 Copyright: 13 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 14 All rights reserved. 15 16 License: 17 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 18 Alternatively, this file may be distributed under the terms of the Tango 19 3-Clause BSD License (see LICENSE_BSD.txt for details). 20 21 ******************************************************************************/ 22 23 module ocean.util.container.AppendBuffer; 24 25 26 import ocean.transition; 27 28 import core.stdc.stdlib: malloc, realloc, free; 29 30 import ocean.core.ExceptionDefinitions: onOutOfMemoryError; 31 32 import ocean.meta.traits.Indirections; 33 34 import ocean.core.Verify; 35 36 version(UnitTest) import ocean.core.Test; 37 38 /****************************************************************************** 39 40 AppendBuffer Base interface. 41 42 ******************************************************************************/ 43 44 interface IAppendBufferBase 45 { 46 /************************************************************************** 47 48 Returns: 49 number of elements in content 50 51 **************************************************************************/ 52 53 size_t length ( ); 54 } 55 56 /****************************************************************************** 57 58 Read-only AppendBuffer interface. 59 60 Note that there is no strict write protection to an IAppendBufferReader 61 instance because it is still possible to modify the content an obtained 62 slice refers to. However, this is not the intention of this interface and 63 may not result in the desired or even result in undesired side effects. 64 65 ******************************************************************************/ 66 67 interface IAppendBufferReader ( T ) : IAppendBufferBase 68 { 69 alias T ElementType; 70 static immutable element_size = T.sizeof; 71 72 /************************************************************************** 73 74 Checks if T is 'void' or a static array type. 75 76 **************************************************************************/ 77 78 static if (!is (T == void)) 79 { 80 /********************************************************************** 81 82 Unless T is 'void' opIndex() is declared and the 83 static_array_element flag defined which tells whether T is a static 84 array type or not. 85 R is defined as return type of opIndex() and other methods which 86 return an array element. Normally this aliases T; however, if T is a 87 static array type which cannot be the return type, it aliases the 88 dynamic array of the same base type. For example, if T aliases 89 int[4], R aliases int[]. 90 If T is 'void', static_array_element, R and opIndex are not 91 declared/defined at all. 92 93 **********************************************************************/ 94 95 static if (is (T U : U[]) && !is (T == U[])) 96 { 97 static immutable static_array_element = true; 98 99 alias U[] R; 100 } 101 else 102 { 103 static immutable static_array_element = false; 104 105 alias T R; 106 } 107 108 /********************************************************************** 109 110 Returns the i-th element in content. 111 112 Params: 113 i = element index 114 115 Returns: 116 i-th element in content 117 118 **********************************************************************/ 119 120 R opIndex ( size_t i ); 121 } 122 else 123 { 124 static immutable static_array_element = false; 125 } 126 127 /************************************************************************** 128 129 Returns: 130 the current content 131 132 **************************************************************************/ 133 134 T[] opSlice ( ); 135 136 /************************************************************************** 137 138 Returns content[start .. end]. 139 start must be at most end and end must be at most the current content 140 length. 141 142 Params: 143 start = start index 144 end = end index (exclusive) 145 146 Returns: 147 content[start .. end] 148 149 **************************************************************************/ 150 151 T[] opSlice ( size_t start, size_t end ); 152 153 /************************************************************************** 154 155 Returns content[start .. length]. 156 157 Params: 158 start = start index 159 160 Returns: 161 content[start .. length] 162 163 **************************************************************************/ 164 165 T[] tail ( size_t start ); 166 } 167 168 /****************************************************************************** 169 170 AppendBuffer class template 171 172 Params: 173 T = array element type 174 use_malloc = true: use malloc()/realloc()/free() to (re/de)allocate 175 the content buffer 176 false: use new/dynamic array resizing/delete 177 178 ******************************************************************************/ 179 180 public template AppendBuffer ( T ) 181 { 182 alias AppendBuffer!(T, AppendBufferImpl) AppendBuffer; 183 } 184 185 /****************************************************************************** 186 187 AppendBuffer class template 188 189 Params: 190 T = array element type 191 Base = base class 192 193 ******************************************************************************/ 194 195 public class AppendBuffer ( T, Base: AppendBufferImpl ): Base, IAppendBufferReader!(T) 196 { 197 static if (hasIndirections!(T)) 198 { 199 // Cannot use `const` for types with indirections 200 alias T ParamT; 201 } 202 else 203 { 204 alias Const!(T) ParamT; 205 } 206 207 /********************************************************************** 208 209 Constructor without buffer preallocation 210 211 **********************************************************************/ 212 213 this ( ) 214 { 215 this(0); 216 } 217 218 /************************************************************************** 219 220 Constructor 221 222 Params: 223 n = content length for buffer preallocation 224 limited = true: enable size limitation, false: disable 225 226 **************************************************************************/ 227 228 this ( size_t n, bool limited = false ) 229 { 230 super(T.sizeof, n); 231 232 this.limited = limited; 233 } 234 235 /************************************************************************** 236 237 Methods overloading array operations which deal with array elements. 238 As these array operations are not available for 'void[]', they are not 239 implemented if T is 'void' . 240 241 **************************************************************************/ 242 243 static if (!is (T == void)) 244 { 245 /********************************************************************** 246 247 Returns the i-th element in content. 248 249 If T is a static array type, a slice to the element is returned. 250 251 Params: 252 i = element index 253 254 Returns: 255 i-th element in content 256 257 Out: 258 If T is a static array type, the length of the returned slice is 259 T.length. 260 261 **********************************************************************/ 262 263 R opIndex ( size_t i ) 264 out (element) 265 { 266 static if (static_array_element) 267 { 268 assert (element.length == T.length); 269 } 270 } 271 body 272 { 273 return *cast (T*) this.index_(i); 274 } 275 276 /********************************************************************** 277 278 Sets the i-th element in content. 279 280 Params: 281 val = value to set 282 i = element index 283 284 Returns: 285 element (or a slice to the element if T is a static array type). 286 287 Out: 288 If T is a static array type, the length of the returned slice is 289 T.length. 290 291 **********************************************************************/ 292 293 R opIndexAssign ( T val, size_t i ) 294 out (element) 295 { 296 static if (static_array_element) 297 { 298 assert (element.length == T.length); 299 } 300 } 301 body 302 { 303 static if (static_array_element) 304 { 305 return (*cast (T*) this.index_(i))[] = val; 306 } 307 else 308 { 309 return *cast (T*) this.index_(i) = val; 310 } 311 } 312 313 /************************************************************************** 314 315 Sets all elements in the current content to element. 316 317 Params: 318 element = element to set all elements to 319 320 Returns: 321 current content 322 323 **************************************************************************/ 324 325 T[] opSliceAssign ( ParamT element ) 326 { 327 return this.opSlice()[] = element; 328 } 329 330 /************************************************************************** 331 332 Copies chunk to the content, setting the content length to chunk.length. 333 334 Params: 335 element = chunk to copy to the content 336 start = start of the slice 337 end = end of the slice 338 339 Returns: 340 slice to chunk in the content 341 342 **************************************************************************/ 343 344 T[] opSliceAssign ( ParamT element, size_t start, size_t end ) 345 { 346 return this.opSlice(start, end)[] = element; 347 } 348 349 /************************************************************************** 350 351 Appends element to the content, extending content where required. 352 353 Params: 354 element = element to append to the content 355 356 Returns: 357 slice to element in the content 358 359 **************************************************************************/ 360 361 T[] opCatAssign ( T element ) 362 { 363 T[] dst = this.extend(1); 364 365 if (dst.length) 366 { 367 static if (static_array_element) 368 { 369 dst[0][] = element; 370 } 371 else 372 { 373 dst[0] = element; 374 } 375 } 376 377 return this[]; 378 } 379 } 380 381 /************************************************************************** 382 383 Returns: 384 the current content 385 386 **************************************************************************/ 387 388 T[] opSlice ( ) 389 { 390 return cast (T[]) this.slice_(); 391 } 392 393 /************************************************************************** 394 395 Returns content[start .. end]. 396 start must be at most end and end must be at most the current content 397 length. 398 399 Params: 400 start = start index 401 end = end index (exclusive) 402 403 Returns: 404 content[start .. end] 405 406 **************************************************************************/ 407 408 T[] opSlice ( size_t start, size_t end ) 409 { 410 return cast (T[]) this.slice_(start, end); 411 } 412 413 /************************************************************************** 414 415 Returns content[start .. length]. 416 417 Params: 418 start = start index 419 420 Returns: 421 content[start .. length] 422 423 **************************************************************************/ 424 425 T[] tail ( size_t start ) 426 { 427 return this[start .. this.length]; 428 } 429 430 /************************************************************************** 431 432 Copies chunk to the content, setting the content length to chunk.length. 433 434 Params: 435 chunk = chunk to copy to the content 436 437 Returns: 438 slice to chunk in the content 439 440 **************************************************************************/ 441 442 T[] opSliceAssign ( ParamT[] chunk ) 443 { 444 return cast (T[]) this.copy_(chunk); 445 } 446 447 /************************************************************************** 448 449 Copies chunk to content[start .. end]. 450 chunk.length must be end - start and end must be at most the current 451 content length. 452 453 Params: 454 chunk = chunk to copy to the content 455 start = start of the slice 456 end = end of the slice 457 458 Returns: 459 slice to chunk in the content 460 461 **************************************************************************/ 462 463 T[] opSliceAssign ( ParamT[] chunk, size_t start, size_t end ) 464 { 465 return cast (T[]) this.copy_(chunk, start, end); 466 } 467 468 /************************************************************************** 469 470 Appends chunk to the content, extending content where required. 471 472 Params: 473 chunk = chunk to append to the content 474 475 Returns: 476 slice to chunk in the content 477 478 **************************************************************************/ 479 480 T[] opCatAssign ( ParamT[] chunk ) 481 { 482 T[] dst = this.extend(chunk.length); 483 484 dst[] = chunk[0 .. dst.length]; 485 486 return this[]; 487 } 488 489 /************************************************************************** 490 491 Cuts the last n elements from the current content. If n is greater than 492 the current content length, all elements in the content are cut. 493 494 Params: 495 n = number of elements to cut from content, if available 496 497 Returns: 498 last n elements cut from the current content, if n is at most the 499 content length or all elements from the current content otherwise. 500 501 **************************************************************************/ 502 503 T[] cut ( size_t n ) 504 out (elements) 505 { 506 assert (elements.length <= n); 507 } 508 body 509 { 510 size_t end = this.length, 511 start = (end >= n)? end - n : 0; 512 513 scope (success) this.length = start; 514 515 return this[start .. end]; 516 } 517 518 static if (!static_array_element) 519 { 520 /********************************************************************** 521 522 Cuts the last element from the current content. 523 524 TODO: Not available if T is a static array type because a reference 525 to the removed element would be needed to be returned, but the 526 referenced element is erased and may theoretically relocated or 527 deallocated (in fact it currently stays at the same location but 528 there shouldn't be a guarantee.) 529 Should this method be available if T is a static array type? It then 530 would need to return 'void' or a struct with one member of type T. 531 (Or we wait for migration to D2.) 532 533 Returns: 534 the element cut from the current content (unless T is void). 535 536 In: 537 The content must not be empty. 538 539 **********************************************************************/ 540 541 T cut ( ) 542 { 543 verify (this.length > 0, "cannot cut last element: content is empty"); 544 545 size_t n = this.length - 1; 546 547 scope (success) this.length = n; 548 549 static if (!is (T == void)) 550 { 551 return this[n]; 552 } 553 } 554 } 555 556 /************************************************************************** 557 558 Cuts the last n elements from the current content. If n is greater than 559 the current content length, all elements in the content are cut. 560 561 Returns: 562 last n elements cut from the current content, if n is at most the 563 content length or all elements from the current content otherwise. 564 565 **************************************************************************/ 566 567 T[] dump ( ) 568 { 569 scope (exit) this.length = 0; 570 571 return this[]; 572 } 573 574 /************************************************************************** 575 576 Concatenates chunks and appends them to the content, extending the 577 content where required. 578 579 Params: 580 chunks = chunks to concatenate and append to the content 581 582 Returns: 583 slice to concatenated chunks in the content which may be shorter 584 than the chunks to concatenate if the content would have needed to 585 be extended but content length limitation is enabled. 586 587 **************************************************************************/ 588 589 T[] append ( U ... ) ( U chunks ) 590 { 591 size_t start = this.length; 592 593 Top: foreach (i, chunk; chunks) 594 { 595 static if (is (U[i] V : V[]) && is (V W : W[])) 596 { 597 foreach (chun; chunk) 598 { 599 if (!this.append(chun)) break Top; // recursive call 600 } 601 } 602 else static if (is (U[i] : T)) 603 { 604 if (!this.opCatAssign(chunk).length) break; 605 } 606 else 607 { 608 static assert (is (typeof (this.append_(chunk))), "cannot append " ~ U[i].stringof ~ " to " ~ (T[]).stringof); 609 610 if (!this.append_(chunk)) break; 611 } 612 } 613 614 return this.tail(start); 615 } 616 617 /************************************************************************** 618 619 Appends chunk to the content, extending the content where required. 620 621 Params: 622 chunks = chunk to append to the content 623 624 Returns: 625 true on success or false if the content would have needed to be 626 extended but content length limitation is enabled. 627 628 **************************************************************************/ 629 630 private bool append_ ( ParamT[] chunk ) 631 { 632 return chunk.length? this.opCatAssign(chunk).length >= chunk.length : true; 633 } 634 635 /************************************************************************** 636 637 Increases the content length by n elements. 638 639 Note that previously returned slices must not be used after this method 640 has been invoked because the content buffer may be relocated, turning 641 existing slices to it into dangling references. 642 643 Params: 644 n = number of characters to extend content by 645 646 Returns: 647 slice to the portion in content by which content has been extended 648 (last n elements in content after extension) 649 650 **************************************************************************/ 651 652 T[] extend ( size_t n ) 653 { 654 return cast (T[]) this.extend_(n); 655 } 656 657 /************************************************************************** 658 659 Returns: 660 pointer to the content 661 662 **************************************************************************/ 663 664 T* ptr ( ) 665 { 666 return cast (T*) this.index_(0); 667 } 668 669 /************************************************************************** 670 671 Sets all elements in data to the initial value of the element type. 672 data.length is guaranteed to be dividable by the element size. 673 674 Params: 675 data = data to erase 676 677 **************************************************************************/ 678 679 protected override void erase ( void[] data ) 680 { 681 static if (static_array_element && is (T U : U[]) && is (typeof (T.init) == U)) 682 { 683 // work around DMD bug 7752 684 685 static immutable t_init = [U.init]; 686 687 data[] = t_init; 688 } 689 else 690 { 691 static if (is (T == void)) 692 { 693 alias ubyte U; 694 } 695 else 696 { 697 alias T U; 698 } 699 700 (cast (U[]) data)[] = U.init; 701 } 702 } 703 } 704 705 /****************************************************************************** 706 707 Generic append buffer 708 709 ******************************************************************************/ 710 711 private abstract class AppendBufferImpl: IAppendBufferBase 712 { 713 /************************************************************************** 714 715 Content buffer. Declared as void[], but newed as ubyte[] 716 (see newContent()). 717 718 We new as ubyte[], not void[], because the GC scans void[] buffers for 719 references. (The GC determines whether a block of memory possibly 720 contains pointers or not at the point where it is newed, not based on 721 the type it is assigned to. See _d_newarrayT() and _d_newarrayiT() in 722 ocean.core.rt.compiler.dmd.rt.lifetime, lines 232 and 285, 723 "BlkAttr.NO_SCAN".) 724 725 @see http://thecybershadow.net/d/Memory_Management_in_the_D_Programming_Language.pdf 726 727 , page 30. 728 729 **************************************************************************/ 730 731 private void[] content; 732 733 /************************************************************************** 734 735 Number of elements in content 736 737 **************************************************************************/ 738 739 private size_t n = 0; 740 741 /************************************************************************** 742 743 Element size 744 745 **************************************************************************/ 746 747 private size_t e; 748 749 /************************************************************************** 750 751 Limitation flag 752 753 **************************************************************************/ 754 755 private bool limited_ = false; 756 757 /************************************************************************** 758 759 Content base pointer and length which are ensured to be invariant when 760 limitation is enabled unless the capacity is changed. 761 762 **************************************************************************/ 763 764 private struct LimitInvariants 765 { 766 private void* ptr = null; 767 size_t len; 768 } 769 770 private LimitInvariants limit_invariants; 771 772 /************************************************************************** 773 774 Consistency checks for content length and number, limitation and content 775 buffer location if limitation enabled. 776 777 **************************************************************************/ 778 779 invariant ( ) 780 { 781 assert (!(this.content.length % this.e)); 782 assert (this.n * this.e <= this.content.length); 783 784 with (this.limit_invariants) if (this.limited_) 785 { 786 assert (ptr is this.content.ptr); 787 assert (len == this.content.length); 788 } 789 else 790 { 791 assert (ptr is null); 792 } 793 } 794 795 /************************************************************************** 796 797 Constructor 798 799 Params: 800 e = element size (non-zero) 801 n = number of elements in content for preallocation (optional) 802 803 **************************************************************************/ 804 805 protected this ( size_t e, size_t n = 0 ) 806 { 807 verify (e > 0, typeof (this).stringof ~ ": element size must be at least 1"); 808 809 this.e = e; 810 811 if (n) 812 { 813 this.content = this.newContent(e * n); 814 } 815 } 816 817 /************************************************************************** 818 819 Sets the number of elements in content to 0. 820 821 Returns: 822 previous number of elements. 823 824 **************************************************************************/ 825 826 public size_t clear ( ) 827 { 828 scope (success) this.n = 0; 829 830 this.erase(this.content[0 .. this.n * this.e]); 831 832 return this.n; 833 } 834 835 /************************************************************************** 836 837 Enables or disables size limitation. 838 839 Params: 840 limited_ = true: enable size limitation, false: disable 841 842 Returns: 843 limited_ 844 845 **************************************************************************/ 846 847 public bool limited ( bool limited_ ) 848 { 849 scope (exit) this.setLimitInvariants(); 850 851 return this.limited_ = limited_; 852 } 853 854 /************************************************************************** 855 856 Returns: 857 true if size limitation is enabled or false if disabled 858 859 **************************************************************************/ 860 861 public bool limited ( ) 862 { 863 return this.limited_; 864 } 865 866 /************************************************************************** 867 868 Returns: 869 number of elements in content 870 871 **************************************************************************/ 872 873 public size_t length ( ) 874 { 875 return this.n; 876 } 877 878 /************************************************************************** 879 880 Returns: 881 size of currently allocated buffer in bytes. 882 883 **************************************************************************/ 884 885 public size_t dimension ( ) 886 { 887 return this.content.length; 888 } 889 890 /************************************************************************** 891 892 Returns: 893 available space (number of elements) in the content buffer, if 894 limitation is enabled, or size_t.max otherwise. 895 896 **************************************************************************/ 897 898 public size_t available ( ) 899 { 900 return this.limited_? this.content.length / this.e - this.n : size_t.max; 901 } 902 903 /************************************************************************** 904 905 Sets the number of elements in content (content length). If length is 906 increased, spare elements will be appended. If length is decreased, 907 elements will be removed at the end. If limitation is enabled, the 908 new number of elements is truncated to capacity(). 909 910 Note that, unless limitation is enabled, previously returned slices must 911 not be used after this method has been invoked because the content 912 buffer may be relocated, turning existing slices to it into dangling 913 references. 914 915 Params: 916 n = new number of elements in content 917 918 Returns: 919 new number of elements, will be truncated to capacity() if 920 limitation is enabled. 921 922 **************************************************************************/ 923 924 public size_t length ( size_t n ) 925 out (n_new) 926 { 927 if (this.limited_) 928 { 929 assert (n_new <= n); 930 } 931 else 932 { 933 assert (n_new == n); 934 } 935 } 936 body 937 { 938 size_t len = n * this.e; 939 940 size_t old_len = this.content.length; 941 942 if (this.content.length < len) 943 { 944 if (this.limited_) 945 { 946 len = this.content.length; 947 } 948 else 949 { 950 this.setContentLength(this.content, len); 951 } 952 } 953 954 if (old_len < len) 955 { 956 this.erase(this.content[old_len .. len]); 957 } 958 959 return this.n = len / this.e; 960 } 961 962 /************************************************************************** 963 964 Returns: 965 Actual content buffer length (number of elements). This value is 966 always at least length(). 967 968 **************************************************************************/ 969 970 public size_t capacity ( ) 971 { 972 return this.content.length / this.e; 973 } 974 975 /************************************************************************** 976 977 Returns: 978 the element size in bytes. The constructor guarantees it is > 0. 979 980 **************************************************************************/ 981 982 public size_t element_size ( ) 983 { 984 return this.e; 985 } 986 987 /************************************************************************** 988 989 Sets the content buffer length, preserving the actual content and 990 overriding/adjusting the limit if limitation is enabled. 991 If the new buffer length is less than length(), the buffer length will 992 be set to length() so that no element is removed. 993 994 Note that previously returned slices must not be used after this method 995 has been invoked because the content buffer may be relocated, turning 996 existing slices to it into dangling references. 997 998 Params: 999 capacity = new content buffer length (number of elements). 1000 1001 Returns: 1002 New content buffer length (number of elements). This value is always 1003 at least length(). 1004 1005 **************************************************************************/ 1006 1007 public size_t capacity ( size_t capacity ) 1008 { 1009 if (capacity > this.n) 1010 { 1011 this.setContentLength(this.content, capacity * this.e); 1012 1013 this.setLimitInvariants(); 1014 1015 return capacity; 1016 } 1017 else 1018 { 1019 return this.n; 1020 } 1021 } 1022 1023 /************************************************************************** 1024 1025 Sets capacity() to length(). 1026 1027 Note that previously returned slices must not be used after this method 1028 has been invoked because the content buffer may be relocated, turning 1029 existing slices to it into dangling references. 1030 1031 Returns: 1032 previous capacity(). 1033 1034 **************************************************************************/ 1035 1036 public size_t minimize ( ) 1037 { 1038 scope (success) 1039 { 1040 this.setContentLength(this.content, this.n * this.e); 1041 } 1042 1043 return this.content.length / this.e; 1044 } 1045 1046 /************************************************************************** 1047 1048 Sets all elements in data to the initial value of the element type. 1049 data.length is guaranteed to be dividable by the element size. 1050 1051 Params: 1052 data = data to erase 1053 1054 **************************************************************************/ 1055 1056 abstract protected void erase ( void[] data ); 1057 1058 /************************************************************************** 1059 1060 Returns: 1061 current content 1062 1063 **************************************************************************/ 1064 1065 protected void[] slice_ ( ) 1066 { 1067 return this.content[0 .. this.n * this.e]; 1068 } 1069 1070 /************************************************************************** 1071 1072 Slices content. start and end index content elements with element size e 1073 (as passed to the constructor). 1074 start must be at most end and end must be at most the current number 1075 of elements in content. 1076 1077 Params: 1078 start = index of start element 1079 end = index of end element (exclusive) 1080 1081 Returns: 1082 content[start * e .. end * e] 1083 1084 **************************************************************************/ 1085 1086 protected void[] slice_ ( size_t start, size_t end ) 1087 { 1088 verify (start <= end, typeof (this).stringof ~ ": slice start behind end index"); 1089 verify (end <= this.n, typeof (this).stringof ~ ": slice end out of range"); 1090 1091 return this.content[start * this.e .. end * this.e]; 1092 } 1093 1094 /************************************************************************** 1095 1096 Returns a pointer to the i-th element in content. 1097 1098 Params: 1099 i = element index 1100 1101 Returns: 1102 pointer to the i-th element in content 1103 1104 **************************************************************************/ 1105 1106 protected void* index_ ( size_t i ) 1107 { 1108 verify (i <= this.n, typeof (this).stringof ~ ": index out of range"); 1109 1110 return this.content.ptr + i * this.e; 1111 } 1112 1113 /************************************************************************** 1114 1115 Copies chunk to the content, setting the current number of elements in 1116 content to the number of elements in chunk. 1117 chunk.length must be dividable by the element size. 1118 1119 Params: 1120 chunk = chunk to copy to the content 1121 1122 Returns: 1123 slice to chunk in content 1124 1125 **************************************************************************/ 1126 1127 protected void[] copy_ ( Const!(void)[] src ) 1128 out (dst) 1129 { 1130 if (this.limited_) 1131 { 1132 assert (dst.length <= src.length); 1133 } 1134 else 1135 { 1136 assert (dst.length == src.length); 1137 } 1138 } 1139 body 1140 { 1141 verify (!(src.length % this.e), typeof (this).stringof ~ ": data alignment mismatch"); 1142 1143 this.n = 0; 1144 1145 void[] dst = this.extendBytes(src.length); 1146 1147 verify (dst.ptr is this.content.ptr); 1148 1149 return dst[] = src[0 .. dst.length]; 1150 } 1151 1152 /************************************************************************** 1153 1154 Copies chunk to content[start * e .. end * e]. 1155 chunk.length must be (end - start) * e and end must be at most the 1156 current number of elements in content. 1157 1158 Params: 1159 chunk = chunk to copy to the content 1160 1161 Returns: 1162 slice to chunk in the content 1163 1164 **************************************************************************/ 1165 1166 protected void[] copy_ ( Const!(void)[] chunk, size_t start, size_t end ) 1167 { 1168 verify (!(chunk.length % this.e), typeof (this).stringof ~ ": data alignment mismatch"); 1169 verify (start <= end, typeof (this).stringof ~ ": slice start behind end index"); 1170 verify (end <= this.n, typeof (this).stringof ~ ": slice end out of range"); 1171 verify (chunk.length == (end - start) * this.e, typeof (this).stringof ~ ": length mismatch of data to copy"); 1172 1173 return this.content[start * this.e .. end * this.e] = cast (ubyte[]) chunk[]; 1174 } 1175 1176 /************************************************************************** 1177 1178 Extends content by n elements. If limitation is enabled, n will be 1179 truncated to the number of available elements. 1180 1181 Params: 1182 n = number of elements to extend content by 1183 1184 Returns: 1185 Slice to the portion in content by which content has been extended 1186 (last n elements in content after extension). 1187 1188 **************************************************************************/ 1189 1190 protected void[] extend_ ( size_t n ) 1191 out (slice) 1192 { 1193 if (this.limited_) 1194 { 1195 assert (slice.length <= n * this.e); 1196 } 1197 else 1198 { 1199 assert (slice.length == n * this.e); 1200 } 1201 } 1202 body 1203 { 1204 return this.extendBytes(n * this.e); 1205 } 1206 1207 /************************************************************************** 1208 1209 Extends content by extent bytes. 1210 extent must be dividable by the element size e. 1211 1212 Params: 1213 extent = number of bytes to extend content by 1214 1215 Returns: 1216 slice to the portion in content by which content has been extended 1217 (last extent bytes in content after extension) 1218 1219 **************************************************************************/ 1220 1221 protected void[] extendBytes ( size_t extent ) 1222 out (slice) 1223 { 1224 assert (!(slice.length % this.e)); 1225 1226 if (this.limited_) 1227 { 1228 assert (slice.length <= extent); 1229 } 1230 else 1231 { 1232 assert (slice.length == extent); 1233 } 1234 } 1235 body 1236 { 1237 verify (!(extent % this.e)); 1238 1239 size_t oldlen = this.n * this.e, 1240 newlen = oldlen + extent; 1241 1242 if (this.content.length < newlen) 1243 { 1244 if (this.limited_) 1245 { 1246 newlen = this.content.length; 1247 } 1248 else 1249 { 1250 this.setContentLength(this.content, newlen); 1251 } 1252 } 1253 1254 this.n = newlen / this.e; 1255 1256 return this.content[oldlen .. newlen]; 1257 } 1258 1259 /************************************************************************** 1260 1261 Allocates a dynamic array of n bytes for the content. 1262 1263 Params: 1264 n = content array length 1265 1266 Returns: 1267 a newly allocated dynamic array. 1268 1269 In: 1270 n must not be zero. 1271 1272 Out: 1273 The array length must be n. 1274 1275 **************************************************************************/ 1276 1277 protected void[] newContent ( size_t n ) 1278 out (content_) 1279 { 1280 assert (content_.length == n); 1281 } 1282 body 1283 { 1284 verify (n > 0, typeof (this).stringof ~ ".newContent: attempted to allocate zero bytes"); 1285 1286 return new ubyte[n]; 1287 } 1288 1289 /************************************************************************** 1290 1291 Sets the content array length to n. 1292 1293 Params: 1294 content_ = content array, previously allocated by newContent() or 1295 modified by setContentLength() 1296 n = new content array length, may be zero 1297 1298 Out: 1299 content_.length must be n. That means, if n is 0, content_ may be 1300 null. 1301 1302 **************************************************************************/ 1303 1304 protected void setContentLength ( ref void[] content_, size_t n ) 1305 out 1306 { 1307 // FIXME assert commented out as it was failing when trying to connect 1308 // to a MySql server using drizzle probably due to a compiler bug 1309 //assert (content_.length == n, 1310 // typeof (this).stringof ~ ".setContentLength: content length mismatch"); 1311 } 1312 body 1313 { 1314 content_.length = n; 1315 enableStomping(content_); 1316 } 1317 1318 /************************************************************************** 1319 1320 Readjusts limit_invariants. 1321 1322 **************************************************************************/ 1323 1324 private void setLimitInvariants ( ) 1325 { 1326 with (this.limit_invariants) if (this.limited_) 1327 { 1328 ptr = this.content.ptr; 1329 len = this.content.length; 1330 } 1331 else 1332 { 1333 ptr = null; 1334 } 1335 } 1336 } 1337 1338 /******************************************************************************/ 1339 1340 unittest 1341 { 1342 scope ab = new AppendBuffer!(dchar)(10); 1343 1344 test (ab.length == 0); 1345 test (ab.capacity == 10); 1346 test (ab.dimension == 10 * dchar.sizeof); 1347 1348 ab[] = "Die Kotze"d; 1349 1350 test (ab.length == "Die Kotze"d.length); 1351 test (ab[] == "Die Kotze"d); 1352 test (ab.capacity == 10); 1353 test (ab.dimension == 10 * dchar.sizeof); 1354 1355 ab[5] = 'a'; 1356 test (ab.length == "Die Katze"d.length); 1357 test (ab[] == "Die Katze"d); 1358 test (ab[4 .. 9] == "Katze"d); 1359 test (ab.capacity == 10); 1360 test (ab.dimension == 10 * dchar.sizeof); 1361 1362 ab ~= ' '; 1363 1364 test (ab[] == "Die Katze "d); 1365 test (ab.length == "Die Katze "d.length); 1366 test (ab.capacity == 10); 1367 test (ab.dimension == 10 * dchar.sizeof); 1368 1369 ab ~= "tritt"d; 1370 1371 test (ab[] == "Die Katze tritt"d); 1372 test (ab.length == "Die Katze tritt"d.length); 1373 test (ab.capacity == "Die Katze tritt"d.length); 1374 test (ab.dimension == "Die Katze tritt"d.length * dchar.sizeof); 1375 1376 ab.append(" die"d[], " Treppe"d[], " krumm."d[]); 1377 1378 test (ab[] == "Die Katze tritt die Treppe krumm."d); 1379 test (ab.length == "Die Katze tritt die Treppe krumm."d.length); 1380 test (ab.capacity == "Die Katze tritt die Treppe krumm."d.length); 1381 test (ab.dimension == "Die Katze tritt die Treppe krumm."d.length * dchar.sizeof); 1382 1383 test (ab.cut(4) == "umm."d); 1384 1385 test (ab.length == "Die Katze tritt die Treppe kr"d.length); 1386 test (ab.capacity == "Die Katze tritt die Treppe krumm."d.length); 1387 test (ab.dimension == "Die Katze tritt die Treppe krumm."d.length * dchar.sizeof); 1388 1389 test (ab.cut() == 'r'); 1390 1391 test (ab.length == "Die Katze tritt die Treppe k"d.length); 1392 test (ab.capacity == "Die Katze tritt die Treppe krumm."d.length); 1393 test (ab.dimension == "Die Katze tritt die Treppe krumm."d.length * dchar.sizeof); 1394 1395 ab.clear(); 1396 1397 test (!ab.length); 1398 test (ab[] == ""d); 1399 1400 ab.extend(5); 1401 test (ab.length == 5); 1402 1403 ab[] = '~'; 1404 test (ab[] == "~~~~~"d); 1405 } 1406 1407 unittest 1408 { 1409 static struct WithIndirection 1410 { 1411 char[] value; 1412 } 1413 1414 static struct WithoutIndirection 1415 { 1416 char value; 1417 } 1418 1419 1420 scope buffer1 = new AppendBuffer!(WithIndirection); 1421 scope buffer2 = new AppendBuffer!(WithoutIndirection); 1422 1423 WithIndirection w; 1424 WithoutIndirection wo; 1425 1426 buffer1 ~= w; 1427 buffer2 ~= wo; 1428 1429 Const!(WithIndirection) cw = w; 1430 Const!(WithoutIndirection) cwo = wo; 1431 1432 static assert(!is(typeof({ buffer1 ~= cw; }))); 1433 buffer2 ~= cwo; 1434 } 1435 1436 1437 unittest 1438 { 1439 static class WithIndirection 1440 { 1441 char[] value; 1442 } 1443 1444 static class WithoutIndirection 1445 { 1446 char value; 1447 } 1448 1449 1450 scope buffer1 = new AppendBuffer!(WithIndirection); 1451 scope buffer2 = new AppendBuffer!(WithoutIndirection); 1452 1453 scope w = new WithIndirection; 1454 scope wo = new WithoutIndirection; 1455 1456 buffer1 ~= w; 1457 buffer2 ~= wo; 1458 1459 Const!(WithIndirection) cw = w; 1460 Const!(WithoutIndirection) cwo = wo; 1461 1462 static assert(!is(typeof({ buffer1 ~= cw; }))); 1463 static assert(!is(typeof({ buffer2 ~= cwo; }))); 1464 }