c++: auto in trailing-return-type in parameter [PR117778]
[gcc.git] / libphobos / src / std / outbuffer.d
blobf6d4ba850f30f241e9241d0b5acaf7a487630704
1 // Written in the D programming language.
3 /**
4 Serialize data to `ubyte` arrays.
6 * Copyright: Copyright The D Language Foundation 2000 - 2015.
7 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
8 * Authors: $(HTTP digitalmars.com, Walter Bright)
9 * Source: $(PHOBOSSRC std/outbuffer.d)
11 * $(SCRIPT inhibitQuickIndex = 1;)
13 module std.outbuffer;
15 import core.stdc.stdarg;
16 import std.traits : isSomeString;
18 /*********************************************
19 * OutBuffer provides a way to build up an array of bytes out
20 * of raw data. It is useful for things like preparing an
21 * array of bytes to write out to a file.
22 * OutBuffer's byte order is the format native to the computer.
23 * To control the byte order (endianness), use a class derived
24 * from OutBuffer.
26 * OutBuffer's internal buffer is allocated with the GC. Pointers
27 * stored into the buffer are scanned by the GC, but you have to
28 * ensure proper alignment, e.g. by using `alignSize((void*).sizeof)`.
31 class OutBuffer
33 ubyte[] data;
34 size_t offset;
36 invariant()
38 assert(offset <= data.length);
41 pure nothrow @safe
43 /*********************************
44 * Convert to array of bytes.
46 inout(ubyte)[] toBytes() scope inout { return data[0 .. offset]; }
48 /***********************************
49 * Preallocate nbytes more to the size of the internal buffer.
51 * This is a
52 * speed optimization, a good guess at the maximum size of the resulting
53 * buffer will improve performance by eliminating reallocations and copying.
55 void reserve(size_t nbytes) @trusted
58 assert(offset + nbytes >= offset);
60 out
62 assert(offset + nbytes <= data.length);
66 if (data.length < offset + nbytes)
68 void[] vdata = data;
69 vdata.length = (offset + nbytes + 7) * 2; // allocates as void[] to not set BlkAttr.NO_SCAN
70 data = cast(ubyte[]) vdata;
74 /**********************************
75 * put enables OutBuffer to be used as an OutputRange.
77 alias put = write;
79 /*************************************
80 * Append data to the internal buffer.
83 void write(scope const(ubyte)[] bytes)
85 reserve(bytes.length);
86 data[offset .. offset + bytes.length] = bytes[];
87 offset += bytes.length;
90 void write(scope const(wchar)[] chars) @trusted
92 write(cast(ubyte[]) chars);
95 void write(scope const(dchar)[] chars) @trusted
97 write(cast(ubyte[]) chars);
100 void write(ubyte b) /// ditto
102 reserve(ubyte.sizeof);
103 this.data[offset] = b;
104 offset += ubyte.sizeof;
107 void write(byte b) { write(cast(ubyte) b); } /// ditto
108 void write(char c) { write(cast(ubyte) c); } /// ditto
109 void write(dchar c) { write(cast(uint) c); } /// ditto
111 void write(ushort w) @trusted /// ditto
113 reserve(ushort.sizeof);
114 *cast(ushort *)&data[offset] = w;
115 offset += ushort.sizeof;
118 void write(short s) { write(cast(ushort) s); } /// ditto
120 void write(wchar c) @trusted /// ditto
122 reserve(wchar.sizeof);
123 *cast(wchar *)&data[offset] = c;
124 offset += wchar.sizeof;
127 void write(uint w) @trusted /// ditto
129 reserve(uint.sizeof);
130 *cast(uint *)&data[offset] = w;
131 offset += uint.sizeof;
134 void write(int i) { write(cast(uint) i); } /// ditto
136 void write(ulong l) @trusted /// ditto
138 reserve(ulong.sizeof);
139 *cast(ulong *)&data[offset] = l;
140 offset += ulong.sizeof;
143 void write(long l) { write(cast(ulong) l); } /// ditto
145 void write(float f) @trusted /// ditto
147 reserve(float.sizeof);
148 *cast(float *)&data[offset] = f;
149 offset += float.sizeof;
152 void write(double f) @trusted /// ditto
154 reserve(double.sizeof);
155 *cast(double *)&data[offset] = f;
156 offset += double.sizeof;
159 void write(real f) @trusted /// ditto
161 reserve(real.sizeof);
162 *cast(real *)&data[offset] = f;
163 offset += real.sizeof;
166 void write(scope const(char)[] s) @trusted /// ditto
168 write(cast(ubyte[]) s);
171 void write(scope const OutBuffer buf) /// ditto
173 write(buf.toBytes());
176 /****************************************
177 * Append nbytes of val to the internal buffer.
178 * Params:
179 * nbytes = Number of bytes to fill.
180 * val = Value to fill, defaults to 0.
183 void fill(size_t nbytes, ubyte val = 0)
185 reserve(nbytes);
186 data[offset .. offset + nbytes] = val;
187 offset += nbytes;
190 /****************************************
191 * Append nbytes of 0 to the internal buffer.
192 * Param:
193 * nbytes - number of bytes to fill.
195 void fill0(size_t nbytes)
197 fill(nbytes);
200 /**********************************
201 * Append bytes until the buffer aligns on a power of 2 boundary.
203 * By default fills with 0 bytes.
205 * Params:
206 * alignsize = Alignment value. Must be power of 2.
207 * val = Value to fill, defaults to 0.
210 void alignSize(size_t alignsize, ubyte val = 0)
213 assert(alignsize && (alignsize & (alignsize - 1)) == 0);
217 assert((offset & (alignsize - 1)) == 0);
221 auto nbytes = offset & (alignsize - 1);
222 if (nbytes)
223 fill(alignsize - nbytes, val);
226 @safe unittest
228 OutBuffer buf = new OutBuffer();
229 buf.write(cast(ubyte) 1);
230 buf.align2();
231 assert(buf.toBytes() == "\x01\x00");
232 buf.write(cast(ubyte) 2);
233 buf.align4();
234 assert(buf.toBytes() == "\x01\x00\x02\x00");
235 buf.write(cast(ubyte) 3);
236 buf.alignSize(8);
237 assert(buf.toBytes() == "\x01\x00\x02\x00\x03\x00\x00\x00");
239 /// ditto
240 @safe unittest
242 OutBuffer buf = new OutBuffer();
243 buf.write(cast(ubyte) 1);
244 buf.align2(0x55);
245 assert(buf.toBytes() == "\x01\x55");
246 buf.write(cast(ubyte) 2);
247 buf.align4(0x55);
248 assert(buf.toBytes() == "\x01\x55\x02\x55");
249 buf.write(cast(ubyte) 3);
250 buf.alignSize(8, 0x55);
251 assert(buf.toBytes() == "\x01\x55\x02\x55\x03\x55\x55\x55");
254 /// Clear the data in the buffer
255 void clear()
257 offset = 0;
260 /****************************************
261 * Optimize common special case alignSize(2)
262 * Params:
263 * val = Value to fill, defaults to 0.
266 void align2(ubyte val = 0)
268 if (offset & 1)
269 write(cast(byte) val);
272 /****************************************
273 * Optimize common special case alignSize(4)
274 * Params:
275 * val = Value to fill, defaults to 0.
278 void align4(ubyte val = 0)
280 if (offset & 3)
281 { auto nbytes = (4 - offset) & 3;
282 fill(nbytes, val);
286 /**************************************
287 * Convert internal buffer to array of chars.
290 override string toString() const
292 //printf("OutBuffer.toString()\n");
293 return cast(string) data[0 .. offset].idup;
297 /*****************************************
298 * Append output of C's vprintf() to internal buffer.
301 void vprintf(scope string format, va_list args) @system nothrow
303 import core.stdc.stdio : vsnprintf;
304 import core.stdc.stdlib : alloca;
305 import std.string : toStringz;
307 version (StdUnittest)
308 char[3] buffer = void; // trigger reallocation
309 else
310 char[128] buffer = void;
311 int count;
313 // Can't use `tempCString()` here as it will result in compilation error:
314 // "cannot mix core.std.stdlib.alloca() and exception handling".
315 auto f = toStringz(format);
316 auto p = buffer.ptr;
317 auto psize = buffer.length;
318 for (;;)
320 va_list args2;
321 va_copy(args2, args);
322 count = vsnprintf(p, psize, f, args2);
323 va_end(args2);
324 if (count == -1)
326 if (psize > psize.max / 2) assert(0); // overflow check
327 psize *= 2;
329 else if (count >= psize)
331 if (count == count.max) assert(0); // overflow check
332 psize = count + 1;
334 else
335 break;
337 p = cast(char *) alloca(psize); // buffer too small, try again with larger size
339 write(cast(ubyte[]) p[0 .. count]);
342 /*****************************************
343 * Append output of C's printf() to internal buffer.
346 void printf(scope string format, ...) @system
348 va_list ap;
349 va_start(ap, format);
350 vprintf(format, ap);
351 va_end(ap);
355 * Formats and writes its arguments in text format to the OutBuffer.
357 * Params:
358 * fmt = format string as described in $(REF formattedWrite, std,format)
359 * args = arguments to be formatted
361 * See_Also:
362 * $(REF _writef, std,stdio);
363 * $(REF formattedWrite, std,format);
365 void writef(Char, A...)(scope const(Char)[] fmt, A args)
367 import std.format.write : formattedWrite;
368 formattedWrite(this, fmt, args);
372 @safe unittest
374 OutBuffer b = new OutBuffer();
375 b.writef("a%sb", 16);
376 assert(b.toString() == "a16b");
379 /// ditto
380 void writef(alias fmt, A...)(A args)
381 if (isSomeString!(typeof(fmt)))
383 import std.format : checkFormatException;
385 alias e = checkFormatException!(fmt, A);
386 static assert(!e, e);
387 return this.writef(fmt, args);
391 @safe unittest
393 OutBuffer b = new OutBuffer();
394 b.writef!"a%sb"(16);
395 assert(b.toString() == "a16b");
399 * Formats and writes its arguments in text format to the OutBuffer,
400 * followed by a newline.
402 * Params:
403 * fmt = format string as described in $(REF formattedWrite, std,format)
404 * args = arguments to be formatted
406 * See_Also:
407 * $(REF _writefln, std,stdio);
408 * $(REF formattedWrite, std,format);
410 void writefln(Char, A...)(scope const(Char)[] fmt, A args)
412 import std.format.write : formattedWrite;
413 formattedWrite(this, fmt, args);
414 put('\n');
418 @safe unittest
420 OutBuffer b = new OutBuffer();
421 b.writefln("a%sb", 16);
422 assert(b.toString() == "a16b\n");
425 /// ditto
426 void writefln(alias fmt, A...)(A args)
427 if (isSomeString!(typeof(fmt)))
429 import std.format : checkFormatException;
431 alias e = checkFormatException!(fmt, A);
432 static assert(!e, e);
433 return this.writefln(fmt, args);
437 @safe unittest
439 OutBuffer b = new OutBuffer();
440 b.writefln!"a%sb"(16);
441 assert(b.toString() == "a16b\n");
444 /*****************************************
445 * At offset index into buffer, create nbytes of space by shifting upwards
446 * all data past index.
449 void spread(size_t index, size_t nbytes) pure nothrow @safe
452 assert(index <= offset);
456 reserve(nbytes);
458 // This is an overlapping copy - should use memmove()
459 for (size_t i = offset; i > index; )
461 --i;
462 data[i + nbytes] = data[i];
464 offset += nbytes;
469 @safe unittest
471 import std.string : cmp;
473 OutBuffer buf = new OutBuffer();
475 assert(buf.offset == 0);
476 buf.write("hello");
477 buf.write(cast(byte) 0x20);
478 buf.write("world");
479 buf.writef(" %d", 62665);
480 assert(cmp(buf.toString(), "hello world 62665") == 0);
482 buf.clear();
483 assert(cmp(buf.toString(), "") == 0);
484 buf.write("New data");
485 assert(cmp(buf.toString(),"New data") == 0);
488 @safe unittest
490 import std.range;
491 static assert(isOutputRange!(OutBuffer, char));
493 import std.algorithm;
495 OutBuffer buf = new OutBuffer();
496 "hello".copy(buf);
497 assert(buf.toBytes() == "hello");
500 OutBuffer buf = new OutBuffer();
501 "hello"w.copy(buf);
502 version (LittleEndian)
503 assert(buf.toBytes() == "h\x00e\x00l\x00l\x00o\x00");
504 version (BigEndian)
505 assert(buf.toBytes() == "\x00h\x00e\x00l\x00l\x00o");
508 OutBuffer buf = new OutBuffer();
509 "hello"d.copy(buf);
510 version (LittleEndian)
511 assert(buf.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00");
512 version (BigEndian)
513 assert(buf.toBytes() == "\x00\x00\x00h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o");