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