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