1 // Written in the D programming language.
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;)
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
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)`.
38 assert(offset
<= data
.length
);
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.
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
);
62 assert(offset
+ nbytes
<= data
.length
);
66 if (data
.length
< offset
+ nbytes
)
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.
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.
179 * nbytes = Number of bytes to fill.
180 * val = Value to fill, defaults to 0.
183 void fill(size_t nbytes
, ubyte val
= 0)
186 data
[offset
.. offset
+ nbytes
] = val
;
190 /****************************************
191 * Append nbytes of 0 to the internal buffer.
193 * nbytes - number of bytes to fill.
195 void fill0(size_t nbytes
)
200 /**********************************
201 * Append bytes until the buffer aligns on a power of 2 boundary.
203 * By default fills with 0 bytes.
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);
223 fill(alignsize
- nbytes
, val
);
228 OutBuffer buf
= new OutBuffer();
229 buf
.write(cast(ubyte) 1);
231 assert(buf
.toBytes() == "\x01\x00");
232 buf
.write(cast(ubyte) 2);
234 assert(buf
.toBytes() == "\x01\x00\x02\x00");
235 buf
.write(cast(ubyte) 3);
237 assert(buf
.toBytes() == "\x01\x00\x02\x00\x03\x00\x00\x00");
242 OutBuffer buf
= new OutBuffer();
243 buf
.write(cast(ubyte) 1);
245 assert(buf
.toBytes() == "\x01\x55");
246 buf
.write(cast(ubyte) 2);
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
260 /****************************************
261 * Optimize common special case alignSize(2)
263 * val = Value to fill, defaults to 0.
266 void align2(ubyte val
= 0)
269 write(cast(byte) val
);
272 /****************************************
273 * Optimize common special case alignSize(4)
275 * val = Value to fill, defaults to 0.
278 void align4(ubyte val
= 0)
281 { auto nbytes
= (4 - offset
) & 3;
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
310 char[128] buffer
= void;
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
);
317 auto psize
= buffer
.length
;
321 va_copy(args2
, args
);
322 count
= vsnprintf(p
, psize
, f
, args2
);
326 if (psize
> psize
.max
/ 2) assert(0); // overflow check
329 else if (count
>= psize
)
331 if (count
== count
.max
) assert(0); // overflow check
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
349 va_start(ap
, format
);
355 * Formats and writes its arguments in text format to the OutBuffer.
358 * fmt = format string as described in $(REF formattedWrite, std,format)
359 * args = arguments to be formatted
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
);
374 OutBuffer b
= new OutBuffer();
375 b
.writef("a%sb", 16);
376 assert(b
.toString() == "a16b");
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
);
393 OutBuffer b
= new OutBuffer();
395 assert(b
.toString() == "a16b");
399 * Formats and writes its arguments in text format to the OutBuffer,
400 * followed by a newline.
403 * fmt = format string as described in $(REF formattedWrite, std,format)
404 * args = arguments to be formatted
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
);
420 OutBuffer b
= new OutBuffer();
421 b
.writefln("a%sb", 16);
422 assert(b
.toString() == "a16b\n");
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
);
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
);
458 // This is an overlapping copy - should use memmove()
459 for (size_t i
= offset
; i
> index
; )
462 data
[i
+ nbytes
] = data
[i
];
471 import std
.string
: cmp;
473 OutBuffer buf
= new OutBuffer();
475 assert(buf
.offset
== 0);
477 buf
.write(cast(byte) 0x20);
479 buf
.writef(" %d", 62665);
480 assert(cmp(buf
.toString(), "hello world 62665") == 0);
483 assert(cmp(buf
.toString(), "") == 0);
484 buf
.write("New data");
485 assert(cmp(buf
.toString(),"New data") == 0);
491 static assert(isOutputRange
!(OutBuffer
, char));
493 import std
.algorithm
;
495 OutBuffer buf
= new OutBuffer();
497 assert(buf
.toBytes() == "hello");
500 OutBuffer buf
= new OutBuffer();
502 version (LittleEndian
)
503 assert(buf
.toBytes() == "h\x00e\x00l\x00l\x00o\x00");
505 assert(buf
.toBytes() == "\x00h\x00e\x00l\x00l\x00o");
508 OutBuffer buf
= new OutBuffer();
510 version (LittleEndian
)
511 assert(buf
.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00");
513 assert(buf
.toBytes() == "\x00\x00\x00h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o");