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 }