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 modulecerealed.scopebuffer;
16 17 18 //debug=ScopeBuffer;19 20 privateimportcore.exception;
21 privateimportcore.stdc.stdlib : realloc;
22 privateimportstd.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 @system103 structScopeBuffer(T, aliasrealloc = /*core.stdc.stdlib*/.realloc)
104 if (isAssignable!T &&
105 !hasElaborateDestructor!T &&
106 !hasElaborateCopyConstructor!T &&
107 !hasElaborateAssign!T)
108 {
109 importcore.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 in126 {
127 assert(!(buf.length & wasResized)); // assure even length of scratch buffer space128 assert(buf.length <= uint.max); // because we cast to uint later129 }
130 body131 {
132 this.buf = buf.ptr;
133 this.bufLen = cast(uint)buf.length;
134 }
135 136 unittest137 {
138 ubyte[10] tmpbuf = void;
139 autosbuf = 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 voidfree()
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 voidput(Tc)
168 {
169 /* j will get enregistered, while used will not because resize() may change used
170 */171 constj = 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 privatealiasCT = Select!(is(const(T) : T), const(T), T);
188 /// ditto189 voidput(CT[] s)
190 {
191 constnewlen = used + s.length;
192 assert((cast(ulong)used + s.length) <= uint.max);
193 constlen = 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 @systemT[] opSlice(size_tlower, size_tupper)
210 in211 {
212 assert(lower <= bufLen);
213 assert(upper <= bufLen);
214 assert(lower <= upper);
215 }
216 body217 {
218 returnbuf[lower .. upper];
219 }
220 221 /// ditto222 @systeminout(T)[] opSlice() inout223 {
224 assert(used <= bufLen);
225 returnbuf[0 .. used];
226 }
227 228 /*******
229 * Returns:
230 * the element at index i.
231 */232 refTopIndex(size_ti)
233 {
234 assert(i < bufLen);
235 returnbuf[i];
236 }
237 238 /***
239 * Returns:
240 * the number of elements in the ScopeBuffer
241 */242 @propertysize_tlength() const243 {
244 returnused;
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 @propertyvoidlength(size_ti)
253 in254 {
255 assert(i <= this.used);
256 }
257 body258 {
259 this.used = cast(uint)i;
260 }
261 262 aliasopDollar = length;
263 264 private:
265 T* buf;
266 // Using uint instead of size_t so the struct fits in 2 registers in 64 bit code267 uintbufLen;
268 enumwasResized = 1; // this bit is set in bufLen if we control the memory269 uintused;
270 271 voidresize(size_tnewsize)
272 in273 {
274 assert(newsize <= uint.max);
275 }
276 body277 {
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 unittest303 {
304 importcore.stdc.stdio;
305 importstd.range;
306 307 char[2] tmpbuf = void;
308 {
309 // Exercise all the lines of code except for assert(0)'s310 autotextbuf = ScopeBuffer!char(tmpbuf);
311 scope(exit) textbuf.free();
312 313 staticassert(isOutputRange!(ScopeBuffer!char, char));
314 315 textbuf.put('a');
316 textbuf.put('x');
317 textbuf.put("abc"); // tickle put([])'s resize318 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 reuse334 assert(textbuf.length == 0);
335 336 foreach (charc; "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj")
337 {
338 textbuf.put(c); // tickle put(c)'s resize339 }
340 assert(textbuf[] == "asdf;lasdlfaklsdjfalksdjfa;lksdjflkajsfdasdfkja;sdlfj");
341 } // run destructor on textbuf here342 343 }
344 345 unittest346 {
347 stringcat(strings1, strings2)
348 {
349 char[10] tmpbuf = void;
350 autotextbuf = ScopeBuffer!char(tmpbuf);
351 scope(exit) textbuf.free();
352 textbuf.put(s1);
353 textbuf.put(s2);
354 textbuf.put("even more");
355 returntextbuf[].idup;
356 }
357 358 autos = 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 autoscopeBuffer(T)(T[] tmpbuf)
378 {
379 returnScopeBuffer!T(tmpbuf);
380 }
381 382 unittest383 {
384 ubyte[10] tmpbuf = void;
385 autosb = scopeBuffer(tmpbuf);
386 scope(exit) sb.free();
387 }
388 389 unittest390 {
391 ScopeBuffer!(int*) b;
392 int*[] s;
393 b.put(s);
394 395 ScopeBuffer!charc;
396 strings1;
397 char[] s2;
398 c.put(s1);
399 c.put(s2);
400 }