1 /*
2   Copied from the new std.internal.scopebuffer from Phobos.
3   The only change I made was the name of the module (so the compiler
4   would compile it), and making opSlice inout so I could use it
5   from const functions.
6  */
7 
8 /*
9  * Copyright: 2014 by Digital Mars
10  * License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0).
11  * Authors: Walter Bright
12  * Source: $(PHOBOSSRC std/internal/_scopebuffer.d)
13  */
14 
15 module cerealed.scopebuffer;
16 
17 
18 //debug=ScopeBuffer;
19 
20 private import core.exception;
21 private import core.stdc.stdlib : realloc;
22 private import std.traits;
23 
24 /**************************************
25  * ScopeBuffer encapsulates using a local array as a temporary buffer.
26  * It is initialized with the local array that should be large enough for
27  * most uses. If the need exceeds the size, ScopeBuffer will resize it
28  * using malloc() and friends.
29  *
30  * ScopeBuffer cannot contain more than (uint.max-16)/2 elements.
31  *
32  * ScopeBuffer is an OutputRange.
33  *
34  * Since ScopeBuffer potentially stores elements of type T in malloc'd memory,
35  * those elements are not scanned when the GC collects. This can cause
36  * memory corruption. Do not use ScopeBuffer when elements of type T point
37  * to the GC heap.
38  *
39  * Example:
40 ---
41 import core.stdc.stdio;
42 import std.internal.scopebuffer;
43 void main()
44 {
45     char[2] buf = void;
46     auto textbuf = ScopeBuffer!char(buf);
47     scope(exit) textbuf.free(); // necessary for cleanup
48 
49     // Put characters and strings into textbuf, verify they got there
50     textbuf.put('a');
51     textbuf.put('x');
52     textbuf.put("abc");
53     assert(textbuf.length == 5);
54     assert(textbuf[1..3] == "xa");
55     assert(textbuf[3] == 'b');
56 
57     // Can shrink it
58     textbuf.length = 3;
59     assert(textbuf[0..textbuf.length] == "axa");
60     assert(textbuf[textbuf.length - 1] == 'a');
61     assert(textbuf[1..3] == "xa");
62 
63     textbuf.put('z');
64     assert(textbuf[] == "axaz");
65 
66     // Can shrink it to 0 size, and reuse same memory
67     textbuf.length = 0;
68 }
69 ---
70  * It is invalid to access ScopeBuffer's contents when ScopeBuffer goes out of scope.
71  * Hence, copying the contents are necessary to keep them around:
72 ---
73 import std.internal.scopebuffer;
74 string cat(string s1, string s2)
75 {
76     char[10] tmpbuf = void;
77     auto textbuf = ScopeBuffer!char(tmpbuf);
78     scope(exit) textbuf.free();
79     textbuf.put(s1);
80     textbuf.put(s2);
81     textbuf.put("even more");
82     return textbuf[].idup;
83 }
84 ---
85  * ScopeBuffer is intended for high performance usages in $(D @system) and $(D @trusted) code.
86  * It is designed to fit into two 64 bit registers, again for high performance use.
87  * If used incorrectly, memory leaks and corruption can result. Be sure to use
88  * $(D scope(exit) textbuf.free();) for proper cleanup, and do not refer to a ScopeBuffer
89  * instance's contents after $(D ScopeBuffer.free()) has been called.
90  *
91  * The realloc parameter defaults to C's realloc(). Another can be supplied to override it.
92  *
93  * ScopeBuffer instances may be copied, as in:
94 ---
95 textbuf = doSomething(textbuf, args);
96 ---
97  * which can be very efficent, but these must be regarded as a move rather than a copy.
98  * Additionally, the code between passing and returning the instance must not throw
99  * exceptions, otherwise when ScopeBuffer.free() is called, memory may get corrupted.
100  */
101 
102 @system
103 struct ScopeBuffer(T, alias realloc = /*core.stdc.stdlib*/.realloc)
104           if (isAssignable!T &&
105               !hasElaborateDestructor!T &&
106               !hasElaborateCopyConstructor!T &&
107               !hasElaborateAssign!T)
108 {
109     import core.stdc..string : memcpy;
110 
111     /**************************
112      * Initialize with buf to use as scratch buffer space.
113      * Params:
114      *  buf = Scratch buffer space, must have length that is even
115      * Example:
116      * ---
117      * ubyte[10] tmpbuf = void;
118      * auto sbuf = ScopeBuffer!ubyte(tmpbuf);
119      * ---
120      * If buf was created by the same realloc passed as a parameter
121      * to ScopeBuffer, then the contents of ScopeBuffer can be extracted without needing
122      * to copy them, and ScopeBuffer.free() will not need to be called.
123      */
124     this(T[] buf)
125         in
126         {
127             assert(!(buf.length & wasResized));    // assure even length of scratch buffer space
128             assert(buf.length <= uint.max);     // because we cast to uint later
129         }
130     body
131     {
132         this.buf = buf.ptr;
133         this.bufLen = cast(uint)buf.length;
134     }
135 
136     unittest
137     {
138         ubyte[10] tmpbuf = void;
139         auto sbuf = ScopeBuffer!ubyte(tmpbuf);
140     }
141 
142     /**************************
143      * Releases any memory used.
144      * This will invalidate any references returned by the [] operator.
145      * A destructor is not used, because that would make it not POD
146      * (Plain Old Data) and it could not be placed in registers.
147      */
148     void free()
149     {
150         debug(ScopeBuffer) buf[0 .. bufLen] = 0;
151         if (bufLen & wasResized)
152             realloc(buf, 0);
153         buf = null;
154         bufLen = 0;
155         used = 0;
156     }
157 
158     /****************************
159      * Copying of ScopeBuffer is not allowed.
160      */
161     //@disable this(this);
162 
163     /************************
164      * Append element c to the buffer.
165      * This member function makes ScopeBuffer an OutputRange.
166      */
167     void put(T c)
168     {
169         /* j will get enregistered, while used will not because resize() may change used
170          */
171         const j = used;
172         if (j == bufLen)
173         {
174             assert(j <= (uint.max - 16) / 2);
175             resize(j * 2 + 16);
176         }
177         buf[j] = c;
178         used = j + 1;
179     }
180 
181     /************************
182      * Append array s to the buffer.
183      *
184      * If $(D const(T)) can be converted to $(D T), then put will accept
185      * $(D const(T)[]) as input. It will accept a $(D T[]) otherwise.
186      */
187     private alias CT = Select!(is(const(T) : T), const(T), T);
188     /// ditto
189     void put(CT[] s)
190     {
191         const newlen = used + s.length;
192         assert((cast(ulong)used + s.length) <= uint.max);
193         const len = bufLen;
194         if (newlen > len)
195         {
196             assert(len <= uint.max / 2);
197             resize(newlen <= len * 2 ? len * 2 : newlen);
198         }
199         buf[used .. newlen] = s[];
200         used = cast(uint)newlen;
201     }
202 
203     /******
204      * Retrieve a slice into the result.
205      * Returns:
206      *  A slice into the temporary buffer that is only
207      *  valid until the next put() or ScopeBuffer goes out of scope.
208      */
209     @system T[] opSlice(size_t lower, size_t upper)
210         in
211         {
212             assert(lower <= bufLen);
213             assert(upper <= bufLen);
214             assert(lower <= upper);
215         }
216     body
217     {
218         return buf[lower .. upper];
219     }
220 
221     /// ditto
222     @system inout(T)[] opSlice() inout
223     {
224         assert(used <= bufLen);
225         return buf[0 .. used];
226     }
227 
228     /*******
229      * Returns:
230      *  the element at index i.
231      */
232     ref T opIndex(size_t i)
233     {
234         assert(i < bufLen);
235         return buf[i];
236     }
237 
238     /***
239      * Returns:
240      *  the number of elements in the ScopeBuffer
241      */
242     @property size_t length() const
243     {
244         return used;
245     }
246 
247     /***
248      * Used to shrink the length of the buffer,
249      * typically to 0 so the buffer can be reused.
250      * Cannot be used to extend the length of the buffer.
251      */
252     @property void length(size_t i)
253         in
254         {
255             assert(i <= this.used);
256         }
257     body
258     {
259         this.used = cast(uint)i;
260     }
261 
262     alias opDollar = length;
263 
264   private:
265     T* buf;
266     // Using uint instead of size_t so the struct fits in 2 registers in 64 bit code
267     uint bufLen;
268     enum wasResized = 1;         // this bit is set in bufLen if we control the memory
269     uint used;
270 
271     void resize(size_t newsize)
272         in
273         {
274             assert(newsize <= uint.max);
275         }
276     body
277     {
278         //writefln("%s: oldsize %s newsize %s", id, buf.length, newsize);
279         newsize |= wasResized;
280         void *newBuf = realloc((bufLen & wasResized) ? buf : null, newsize * T.sizeof);
281         if (!newBuf)
282             core.exception.onOutOfMemoryError();
283         if (!(bufLen & wasResized))
284         {
285             memcpy(newBuf, buf, used * T.sizeof);
286             debug(ScopeBuffer) buf[0 .. bufLen] = 0;
287         }
288         buf = cast(T*)newBuf;
289         bufLen = cast(uint)newsize;
290 
291         /* This function is called only rarely,
292          * inlining results in poorer register allocation.
293          */
294         version (DigitalMars)
295             /* With dmd, a fake loop will prevent inlining.
296              * Using a hack until a language enhancement is implemented.
297              */
298             while (1) { break; }
299     }
300 }
301 
302 unittest
303 {
304     import core.stdc.stdio;
305     import std.range;
306 
307     char[2] tmpbuf = void;
308     {
309     // Exercise all the lines of code except for assert(0)'s
310     auto textbuf = ScopeBuffer!char(tmpbuf);
311     scope(exit) textbuf.free();
312 
313     static assert(isOutputRange!(ScopeBuffer!char, char));
314 
315     textbuf.put('a');
316     textbuf.put('x');
317     textbuf.put("abc");         // tickle put([])'s resize
318     assert(textbuf.length == 5);
319     assert(textbuf[1..3] == "xa");
320     assert(textbuf[3] == 'b');
321 
322     textbuf.length = textbuf.length - 1;
323     assert(textbuf[0..textbuf.length] == "axab");
324 
325     textbuf.length = 3;
326     assert(textbuf[0..textbuf.length] == "axa");
327     assert(textbuf[textbuf.length - 1] == 'a');
328     assert(textbuf[1..3] == "xa");
329 
330     textbuf.put(cast(dchar)'z');
331     assert(textbuf[] == "axaz");
332 
333     textbuf.length = 0;                 // reset for reuse
334     assert(textbuf.length == 0);
335 
336     foreach (char c; "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj")
337     {
338         textbuf.put(c); // tickle put(c)'s resize
339     }
340     assert(textbuf[] == "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj");
341     } // run destructor on textbuf here
342 
343 }
344 
345 unittest
346 {
347     string cat(string s1, string s2)
348     {
349         char[10] tmpbuf = void;
350         auto textbuf = ScopeBuffer!char(tmpbuf);
351         scope(exit) textbuf.free();
352         textbuf.put(s1);
353         textbuf.put(s2);
354         textbuf.put("even more");
355         return textbuf[].idup;
356     }
357 
358     auto s = cat("hello", "betty");
359     assert(s == "hellobettyeven more");
360 }
361 
362 /*********************************
363  * This is a slightly simpler way to create a ScopeBuffer instance
364  * that uses type deduction.
365  * Params:
366  *      tmpbuf = the initial buffer to use
367  * Returns:
368  *      an instance of ScopeBuffer
369  * Example:
370 ---
371 ubyte[10] tmpbuf = void;
372 auto sb = scopeBuffer(tmpbuf);
373 scope(exit) sp.free();
374 ---
375  */
376 
377 auto scopeBuffer(T)(T[] tmpbuf)
378 {
379     return ScopeBuffer!T(tmpbuf);
380 }
381 
382 unittest
383 {
384     ubyte[10] tmpbuf = void;
385     auto sb = scopeBuffer(tmpbuf);
386     scope(exit) sb.free();
387 }
388 
389 unittest
390 {
391     ScopeBuffer!(int*) b;
392     int*[] s;
393     b.put(s);
394 
395     ScopeBuffer!char c;
396     string s1;
397     char[] s2;
398     c.put(s1);
399     c.put(s2);
400 }