]> git.ipfire.org Git - thirdparty/gcc.git/blob - libphobos/src/std/internal/scopebuffer.d
Add D front-end, libphobos library, and D2 testsuite.
[thirdparty/gcc.git] / libphobos / src / std / internal / scopebuffer.d
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 }