1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * This module contains buffers designed for C-style input/output APIs.
7  *
8  * Copyright: Eugene Wissner 2016-2020.
9  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
10  *                  Mozilla Public License, v. 2.0).
11  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
12  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/buffer.d,
13  *                 tanya/container/buffer.d)
14  */
15 module tanya.container.buffer;
16 
17 import std.traits : isScalarType;
18 import tanya.memory.allocator;
19 import tanya.meta.trait;
20 
21 version (unittest)
22 {
23     private int fillBuffer(ubyte[] buffer,
24                            int start = 0,
25                            int end = 10) @nogc pure nothrow
26     in
27     {
28         assert(start < end);
29     }
30     do
31     {
32         auto numberRead = end - start;
33         for (ubyte i; i < numberRead; ++i)
34         {
35             buffer[i] = cast(ubyte) (start + i);
36         }
37         return numberRead;
38     }
39 }
40 
41 /**
42  * Self-expanding buffer, that can be used with functions returning the number
43  * of the read bytes.
44  *
45  * This buffer supports asynchronous reading. It means you can pass a new chunk
46  * to an asynchronous read function during you are working with already
47  * available data. But only one asynchronous call at a time is supported. Be
48  * sure to call $(D_PSYMBOL ReadBuffer.clear()) before you append the result
49  * of the pended asynchronous call.
50  *
51  * Params:
52  *  T = Buffer type.
53  */
54 struct ReadBuffer(T = ubyte)
55 if (isScalarType!T)
56 {
57     /// Internal buffer.
58     private T[] buffer_;
59 
60     /// Filled buffer length.
61     private size_t length_;
62 
63     /// Start of available data.
64     private size_t start;
65 
66     /// Last position returned with $(D_KEYWORD []).
67     private size_t ring;
68 
69     /// Available space.
70     private size_t minAvailable = 1024;
71 
72     /// Size by which the buffer will grow.
73     private size_t blockSize = 8192;
74 
75     invariant
76     {
77         assert(this.length_ <= this.buffer_.length);
78         assert(this.blockSize > 0);
79         assert(this.minAvailable > 0);
80     }
81 
82     /**
83      * Creates a new read buffer.
84      *
85      * Params:
86      *  size         = Initial buffer size and the size by which the buffer
87      *                 will grow.
88      *  minAvailable = minimal size should be always  available to fill.
89      *                 So it will reallocate if $(D_INLINECODE
90      *                 $(D_PSYMBOL free) < $(D_PARAM minAvailable)).
91      *  allocator    = Allocator.
92      */
93     this(size_t size,
94          size_t minAvailable = 1024,
95          shared Allocator allocator = defaultAllocator) @trusted
96     {
97         this(allocator);
98         this.minAvailable = minAvailable;
99         this.blockSize = size;
100         this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
101     }
102 
103     /// ditto
104     this(shared Allocator allocator)
105     in
106     {
107         assert(allocator_ is null);
108     }
109     do
110     {
111         allocator_ = allocator;
112     }
113 
114     /**
115      * Deallocates the internal buffer.
116      */
117     ~this() @trusted
118     {
119         allocator.deallocate(this.buffer_);
120     }
121 
122     ///
123     @nogc nothrow pure @safe unittest
124     {
125         ReadBuffer!ubyte b;
126         assert(b.capacity == 0);
127         assert(b.length == 0);
128     }
129 
130     /**
131      * Returns: The size of the internal buffer.
132      */
133     @property size_t capacity() const
134     {
135         return this.buffer_.length;
136     }
137 
138     /**
139      * Returns: Data size.
140      */
141     @property size_t length() const
142     {
143         return this.length_ - start;
144     }
145 
146     /// ditto
147     alias opDollar = length;
148 
149     /**
150      * Clears the buffer.
151      *
152      * Returns: $(D_KEYWORD this).
153      */
154     void clear()
155     {
156         start = this.length_ = ring;
157     }
158 
159     /**
160      * Returns: Available space.
161      */
162     @property size_t free() const
163     {
164         return length > ring ? capacity - length : capacity - ring;
165     }
166 
167     ///
168     @nogc nothrow pure @system unittest
169     {
170         ReadBuffer!ubyte b;
171         size_t numberRead;
172 
173         assert(b.free == 0);
174 
175         // Fills the buffer with values 0..10
176         numberRead = fillBuffer(b[], 0, 10);
177         b += numberRead;
178         assert(b.free == b.blockSize - numberRead);
179         b.clear();
180         assert(b.free == b.blockSize);
181     }
182 
183     /**
184      * Appends some data to the buffer.
185      *
186      * Params:
187      *  length = Number of the bytes read.
188      *
189      * Returns: $(D_KEYWORD this).
190      */
191     ref ReadBuffer opOpAssign(string op)(size_t length)
192     if (op == "+")
193     {
194         this.length_ += length;
195         ring = start;
196         return this;
197     }
198 
199     ///
200     @nogc nothrow pure @system unittest
201     {
202         ReadBuffer!ubyte b;
203         size_t numberRead;
204         ubyte[] result;
205 
206         // Fills the buffer with values 0..10
207         numberRead = fillBuffer(b[], 0, 10);
208         b += numberRead;
209 
210         result = b[0 .. $];
211         assert(result[0] == 0);
212         assert(result[1] == 1);
213         assert(result[9] == 9);
214         b.clear();
215 
216         // It shouldn't overwrite, but append another 5 bytes to the buffer
217         numberRead = fillBuffer(b[], 0, 10);
218         b += numberRead;
219 
220         numberRead = fillBuffer(b[], 20, 25);
221         b += numberRead;
222 
223         result = b[0..$];
224         assert(result[0] == 0);
225         assert(result[1] == 1);
226         assert(result[9] == 9);
227         assert(result[10] == 20);
228         assert(result[14] == 24);
229     }
230 
231     /**
232      * Params:
233      *  start = Start position.
234      *  end   = End position.
235      *
236      * Returns: Array between $(D_PARAM start) and $(D_PARAM end).
237      */
238     T[] opSlice(size_t start, size_t end)
239     {
240         return this.buffer_[this.start + start .. this.start + end];
241     }
242 
243     /**
244      * Returns a free chunk of the buffer.
245      *
246      * Add ($(D_KEYWORD +=)) the number of the read bytes after using it.
247      *
248      * Returns: A free chunk of the buffer.
249      */
250     T[] opIndex()
251     {
252         if (start > 0)
253         {
254             auto ret = this.buffer_[0 .. start];
255             ring = 0;
256             return ret;
257         }
258         else
259         {
260             if (capacity - length < this.minAvailable)
261             {
262                 void[] buf = this.buffer_;
263                 const cap = capacity;
264                 () @trusted {
265                     allocator.reallocate(buf,
266                                          (cap + this.blockSize) * T.sizeof);
267                     this.buffer_ = cast(T[]) buf;
268                 }();
269             }
270             ring = this.length_;
271             return this.buffer_[this.length_ .. $];
272         }
273     }
274 
275     ///
276     @nogc nothrow pure @system unittest
277     {
278         ReadBuffer!ubyte b;
279         size_t numberRead;
280         ubyte[] result;
281 
282         // Fills the buffer with values 0..10
283         numberRead = fillBuffer(b[], 0, 10);
284         b += numberRead;
285 
286         assert(b.length == 10);
287         result = b[0 .. $];
288         assert(result[0] == 0);
289         assert(result[9] == 9);
290         b.clear();
291         assert(b.length == 0);
292     }
293 
294     mixin DefaultAllocator;
295 }
296 
297 /**
298  * Circular, self-expanding buffer with overflow support. Can be used with
299  * functions returning the number of the transferred bytes.
300  *
301  * The buffer is optimized for situations where you read all the data from it
302  * at once (without writing to it occasionally). It can become ineffective if
303  * you permanently keep some data in the buffer and alternate writing and
304  * reading, because it may allocate and move elements.
305  *
306  * Params:
307  *  T = Buffer type.
308  */
309 struct WriteBuffer(T = ubyte)
310 if (isScalarType!T)
311 {
312     /// Internal buffer.
313     private T[] buffer_;
314 
315     /// Buffer start position.
316     private size_t start;
317 
318     /// Buffer ring area size. After this position begins buffer overflow area.
319     private size_t ring;
320 
321     /// Size by which the buffer will grow.
322     private const size_t blockSize;
323 
324     /// The position of the free area in the buffer.
325     private size_t position;
326 
327     invariant
328     {
329         assert(this.blockSize > 0);
330         // Position can refer to an element outside the buffer if the buffer is
331         // full.
332         assert(this.position <= this.buffer_.length);
333     }
334 
335     /**
336      * Params:
337      *  size      = Initial buffer size and the size by which the buffer will
338      *              grow.
339      *  allocator = Allocator.
340      *
341      * Precondition: $(D_INLINECODE size > 0 && allocator !is null)
342      */
343     this(size_t size, shared Allocator allocator = defaultAllocator) @trusted
344     in
345     {
346         assert(size > 0);
347         assert(allocator !is null);
348     }
349     do
350     {
351         this.blockSize = size;
352         ring = size - 1;
353         allocator_ = allocator;
354         this.buffer_ = cast(T[]) allocator_.allocate(size * T.sizeof);
355     }
356 
357     @disable this();
358 
359     /**
360      * Deallocates the internal buffer.
361      */
362     ~this()
363     {
364         allocator.deallocate(this.buffer_);
365     }
366 
367     /**
368      * Returns: The size of the internal buffer.
369      */
370     @property size_t capacity() const
371     {
372         return this.buffer_.length;
373     }
374 
375     /**
376      * Note that $(D_PSYMBOL length) doesn't return the real length of the data,
377      * but only the array length that will be returned with $(D_PSYMBOL opIndex)
378      * next time. Be sure to call $(D_PSYMBOL opIndex) and set $(D_KEYWORD +=)
379      * until $(D_PSYMBOL length) returns 0.
380      *
381      * Returns: Data size.
382      */
383     @property size_t length() const
384     {
385         if (this.position > ring || this.position < start) // Buffer overflowed
386         {
387             return ring - start + 1;
388         }
389         else
390         {
391             return this.position - start;
392         }
393     }
394 
395     /// ditto
396     alias opDollar = length;
397 
398     ///
399     @nogc nothrow pure @system unittest
400     {
401         auto b = WriteBuffer!ubyte(4);
402         ubyte[3] buf = [48, 23, 255];
403 
404         b ~= buf;
405         assert(b.length == 3);
406         b += 2;
407         assert(b.length == 1);
408 
409         b ~= buf;
410         assert(b.length == 2);
411         b += 2;
412         assert(b.length == 2);
413 
414         b ~= buf;
415         assert(b.length == 5);
416         b += b.length;
417         assert(b.length == 0);
418     }
419 
420     /**
421      * Returns: Available space.
422      */
423     @property size_t free() const
424     {
425         return capacity - length;
426     }
427 
428     /**
429      * Appends data to the buffer.
430      *
431      * Params:
432      *  buffer = Buffer chunk got with $(D_PSYMBOL opIndex).
433      */
434     ref WriteBuffer opOpAssign(string op)(const T[] buffer)
435     if (op == "~")
436     {
437         size_t end, start;
438 
439         if (this.position >= this.start && this.position <= ring)
440         {
441             auto afterRing = ring + 1;
442 
443             end = this.position + buffer.length;
444             if (end > afterRing)
445             {
446                 end = afterRing;
447             }
448             start = end - this.position;
449             this.buffer_[this.position .. end] = buffer[0 .. start];
450             if (end == afterRing)
451             {
452                 this.position = this.start == 0 ? afterRing : 0;
453             }
454             else
455             {
456                 this.position = end;
457             }
458         }
459 
460         // Check if we have some free space at the beginning
461         if (start < buffer.length && this.position < this.start)
462         {
463             end = this.position + buffer.length - start;
464             if (end > this.start)
465             {
466                 end = this.start;
467             }
468             auto areaEnd = end - this.position + start;
469             this.buffer_[this.position .. end] = buffer[start .. areaEnd];
470             this.position = end == this.start ? ring + 1 : end - this.position;
471             start = areaEnd;
472         }
473 
474         // And if we still haven't found any place, save the rest in the overflow area
475         if (start < buffer.length)
476         {
477             end = this.position + buffer.length - start;
478             if (end > capacity)
479             {
480                 const newSize = end / this.blockSize * this.blockSize
481                               + this.blockSize;
482                 () @trusted {
483                     void[] buf = this.buffer_;
484                     allocator.reallocate(buf, newSize * T.sizeof);
485                     this.buffer_ = cast(T[]) buf;
486                 }();
487             }
488             this.buffer_[this.position .. end] = buffer[start .. $];
489             this.position = end;
490             if (this.start == 0)
491             {
492                 ring = capacity - 1;
493             }
494         }
495 
496         return this;
497     }
498 
499     /**
500      * Sets how many bytes were written. It will shrink the buffer
501      * appropriately. Always call it after $(D_PSYMBOL opIndex).
502      *
503      * Params:
504      *  length = Length of the written data.
505      *
506      * Returns: $(D_KEYWORD this).
507      */
508     ref WriteBuffer opOpAssign(string op)(size_t length)
509     if (op == "+")
510     in
511     {
512         assert(length <= this.length);
513     }
514     do
515     {
516         auto afterRing = ring + 1;
517         auto oldStart = start;
518 
519         if (length <= 0)
520         {
521             return this;
522         }
523         else if (this.position <= afterRing)
524         {
525             start += length;
526             if (start > 0 && this.position == afterRing)
527             {
528                 this.position = oldStart;
529             }
530         }
531         else
532         {
533             auto overflow = this.position - afterRing;
534 
535             if (overflow > length)
536             {
537                 const afterLength = afterRing + length;
538                 this.buffer_[start .. start + length] = this.buffer_[afterRing .. afterLength];
539                 this.buffer_[afterRing .. afterLength] = this.buffer_[afterLength .. this.position];
540                 this.position -= length;
541             }
542             else if (overflow == length)
543             {
544                 this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
545                 this.position -= overflow;
546             }
547             else
548             {
549                 this.buffer_[start .. start + overflow] = this.buffer_[afterRing .. this.position];
550                 this.position = overflow;
551             }
552             start += length;
553 
554             if (start == this.position)
555             {
556                 if (this.position != afterRing)
557                 {
558                     this.position = 0;
559                 }
560                 start = 0;
561                 ring = capacity - 1;
562             }
563         }
564         if (start > ring)
565         {
566             start = 0;
567         }
568         return this;
569     }
570 
571     ///
572     @nogc nothrow pure @system unittest
573     {
574         auto b = WriteBuffer!ubyte(6);
575         ubyte[6] buf = [23, 23, 255, 128, 127, 9];
576 
577         b ~= buf;
578         assert(b.length == 6);
579         b += 2;
580         assert(b.length == 4);
581         b += 4;
582         assert(b.length == 0);
583     }
584 
585     /**
586      * Returns a chunk with data.
587      *
588      * After calling it, set $(D_KEYWORD +=) to the length could be
589      * written.
590      *
591      * $(D_PSYMBOL opIndex) may return only part of the data. You may need
592      * to call it and set $(D_KEYWORD +=) several times until
593      * $(D_PSYMBOL length) is 0. If all the data can be written,
594      * maximally 3 calls are required.
595      *
596      * Returns: A chunk of data buffer.
597      */
598     T[] opSlice(size_t start, size_t end)
599     {
600         if (this.position > ring || this.position < start) // Buffer overflowed
601         {
602             return this.buffer_[this.start .. ring + 1 - length + end];
603         }
604         else
605         {
606             return this.buffer_[this.start .. this.start + end];
607         }
608     }
609 
610     ///
611     @nogc nothrow pure @system unittest
612     {
613         auto b = WriteBuffer!ubyte(6);
614         ubyte[6] buf = [23, 23, 255, 128, 127, 9];
615 
616         b ~= buf;
617         assert(b[0 .. $] == buf[0 .. 6]);
618         b += 2;
619 
620         assert(b[0 .. $] == buf[2 .. 6]);
621 
622         b ~= buf;
623         assert(b[0 .. $] == buf[2 .. 6]);
624         b += b.length;
625 
626         assert(b[0 .. $] == buf[0 .. 6]);
627         b += b.length;
628     }
629 
630     /**
631      * After calling it, set $(D_KEYWORD +=) to the length could be
632      * written.
633      *
634      * $(D_PSYMBOL opIndex) may return only part of the data. You may need
635      * to call it and set $(D_KEYWORD +=) several times until
636      * $(D_PSYMBOL length) is 0. If all the data can be written,
637      * maximally 3 calls are required.
638      *
639      * Returns: A chunk of data buffer.
640      */
641     T[] opIndex()
642     {
643         return opSlice(0, length);
644     }
645 
646     mixin DefaultAllocator;
647 }
648 
649 @nogc nothrow pure @system unittest
650 {
651     auto b = WriteBuffer!ubyte(4);
652     ubyte[3] buf = [48, 23, 255];
653 
654     b ~= buf;
655     assert(b.capacity == 4);
656     assert(b.buffer_[0] == 48 && b.buffer_[1] == 23 && b.buffer_[2] == 255);
657 
658     b += 2;
659     b ~= buf;
660     assert(b.capacity == 4);
661     assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
662         && b.buffer_[2] == 255 && b.buffer_[3] == 48);
663 
664     b += 2;
665     b ~= buf;
666     assert(b.capacity == 8);
667     assert(b.buffer_[0] == 23 && b.buffer_[1] == 255
668         && b.buffer_[2] == 48 && b.buffer_[3] == 23 && b.buffer_[4] == 255);
669 }
670 
671 @nogc nothrow pure @system unittest
672 {
673     auto b = WriteBuffer!ubyte(2);
674     ubyte[3] buf = [48, 23, 255];
675 
676     b ~= buf;
677     assert(b.start == 0);
678     assert(b.capacity == 4);
679     assert(b.ring == 3);
680     assert(b.position == 3);
681 }