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
25 * OutBuffer's internal buffer is allocated with the GC. Pointers
26 * stored into the buffer are scanned by the GC, but you have to
27 * ensure proper alignment, e.g. by using alignSize((void*).sizeof).
37 assert(offset
<= data
.length
);
42 /*********************************
43 * Convert to array of bytes.
45 inout(ubyte)[] toBytes() scope inout { return data
[0 .. offset
]; }
47 /***********************************
48 * Preallocate nbytes more to the size of the internal buffer.
51 * speed optimization, a good guess at the maximum size of the resulting
52 * buffer will improve performance by eliminating reallocations and copying.
54 void reserve(size_t nbytes
) @trusted
57 assert(offset
+ nbytes
>= offset
);
61 assert(offset
+ nbytes
<= data
.length
);
65 if (data
.length
< offset
+ nbytes
)
68 vdata
.length
= (offset
+ nbytes
+ 7) * 2; // allocates as void[] to not set BlkAttr.NO_SCAN
69 data
= cast(ubyte[]) vdata
;
73 /**********************************
74 * put enables OutBuffer to be used as an OutputRange.
78 /*************************************
79 * Append data to the internal buffer.
82 void write(scope const(ubyte)[] bytes
)
84 reserve(bytes
.length
);
85 data
[offset
.. offset
+ bytes
.length
] = bytes
[];
86 offset
+= bytes
.length
;
89 void write(scope const(wchar)[] chars
) @trusted
91 write(cast(ubyte[]) chars
);
94 void write(scope const(dchar)[] chars
) @trusted
96 write(cast(ubyte[]) chars
);
99 void write(ubyte b
) /// ditto
101 reserve(ubyte.sizeof
);
102 this.data
[offset
] = b
;
103 offset
+= ubyte.sizeof
;
106 void write(byte b
) { write(cast(ubyte) b
); } /// ditto
107 void write(char c
) { write(cast(ubyte) c
); } /// ditto
108 void write(dchar c
) { write(cast(uint) c
); } /// ditto
110 void write(ushort w
) @trusted /// ditto
112 reserve(ushort.sizeof
);
113 *cast(ushort *)&data
[offset
] = w
;
114 offset
+= ushort.sizeof
;
117 void write(short s
) { write(cast(ushort) s
); } /// ditto
119 void write(wchar c
) @trusted /// ditto
121 reserve(wchar.sizeof
);
122 *cast(wchar *)&data
[offset
] = c
;
123 offset
+= wchar.sizeof
;
126 void write(uint w
) @trusted /// ditto
128 reserve(uint.sizeof
);
129 *cast(uint *)&data
[offset
] = w
;
130 offset
+= uint.sizeof
;
133 void write(int i
) { write(cast(uint) i
); } /// ditto
135 void write(ulong l
) @trusted /// ditto
137 reserve(ulong.sizeof
);
138 *cast(ulong *)&data
[offset
] = l
;
139 offset
+= ulong.sizeof
;
142 void write(long l
) { write(cast(ulong) l
); } /// ditto
144 void write(float f
) @trusted /// ditto
146 reserve(float.sizeof
);
147 *cast(float *)&data
[offset
] = f
;
148 offset
+= float.sizeof
;
151 void write(double f
) @trusted /// ditto
153 reserve(double.sizeof
);
154 *cast(double *)&data
[offset
] = f
;
155 offset
+= double.sizeof
;
158 void write(real f
) @trusted /// ditto
160 reserve(real.sizeof
);
161 *cast(real *)&data
[offset
] = f
;
162 offset
+= real.sizeof
;
165 void write(scope const(char)[] s
) @trusted /// ditto
167 write(cast(ubyte[]) s
);
170 void write(scope const OutBuffer buf
) /// ditto
172 write(buf
.toBytes());
175 /****************************************
176 * Append nbytes of val to the internal buffer.
178 * nbytes = Number of bytes to fill.
179 * val = Value to fill, defaults to 0.
182 void fill(size_t nbytes
, ubyte val
= 0)
185 data
[offset
.. offset
+ nbytes
] = val
;
189 /****************************************
190 * Append nbytes of 0 to the internal buffer.
192 * nbytes - number of bytes to fill.
194 void fill0(size_t nbytes
)
199 /**********************************
200 * Append bytes until the buffer aligns on a power of 2 boundary.
202 * By default fills with 0 bytes.
205 * alignsize = Alignment value. Must be power of 2.
206 * val = Value to fill, defaults to 0.
209 void alignSize(size_t alignsize
, ubyte val
= 0)
212 assert(alignsize
&& (alignsize
& (alignsize
- 1)) == 0);
216 assert((offset
& (alignsize
- 1)) == 0);
220 auto nbytes
= offset
& (alignsize
- 1);
222 fill(alignsize
- nbytes
, val
);
227 OutBuffer buf
= new OutBuffer();
228 buf
.write(cast(ubyte) 1);
230 assert(buf
.toBytes() == "\x01\x00");
231 buf
.write(cast(ubyte) 2);
233 assert(buf
.toBytes() == "\x01\x00\x02\x00");
234 buf
.write(cast(ubyte) 3);
236 assert(buf
.toBytes() == "\x01\x00\x02\x00\x03\x00\x00\x00");
241 OutBuffer buf
= new OutBuffer();
242 buf
.write(cast(ubyte) 1);
244 assert(buf
.toBytes() == "\x01\x55");
245 buf
.write(cast(ubyte) 2);
247 assert(buf
.toBytes() == "\x01\x55\x02\x55");
248 buf
.write(cast(ubyte) 3);
249 buf
.alignSize(8, 0x55);
250 assert(buf
.toBytes() == "\x01\x55\x02\x55\x03\x55\x55\x55");
253 /// Clear the data in the buffer
259 /****************************************
260 * Optimize common special case alignSize(2)
262 * val = Value to fill, defaults to 0.
265 void align2(ubyte val
= 0)
268 write(cast(byte) val
);
271 /****************************************
272 * Optimize common special case alignSize(4)
274 * val = Value to fill, defaults to 0.
277 void align4(ubyte val
= 0)
280 { auto nbytes
= (4 - offset
) & 3;
285 /**************************************
286 * Convert internal buffer to array of chars.
289 override string
toString() const
291 //printf("OutBuffer.toString()\n");
292 return cast(string
) data
[0 .. offset
].idup
;
296 /*****************************************
297 * Append output of C's vprintf() to internal buffer.
300 void vprintf(scope string format
, va_list args
) @trusted nothrow
302 import core
.stdc
.stdio
: vsnprintf
;
303 import core
.stdc
.stdlib
: alloca
;
304 import std
.string
: toStringz
;
306 version (StdUnittest
)
307 char[3] buffer
= void; // trigger reallocation
309 char[128] buffer
= void;
312 // Can't use `tempCString()` here as it will result in compilation error:
313 // "cannot mix core.std.stdlib.alloca() and exception handling".
314 auto f
= toStringz(format
);
316 auto psize
= buffer
.length
;
320 va_copy(args2
, args
);
321 count
= vsnprintf(p
, psize
, f
, args2
);
325 if (psize
> psize
.max
/ 2) assert(0); // overflow check
328 else if (count
>= psize
)
330 if (count
== count
.max
) assert(0); // overflow check
336 p
= cast(char *) alloca(psize
); // buffer too small, try again with larger size
338 write(cast(ubyte[]) p
[0 .. count
]);
341 /*****************************************
342 * Append output of C's printf() to internal buffer.
345 void printf(scope string format
, ...) @trusted
348 va_start(ap
, format
);
354 * Formats and writes its arguments in text format to the OutBuffer.
357 * fmt = format string as described in $(REF formattedWrite, std,format)
358 * args = arguments to be formatted
361 * $(REF _writef, std,stdio);
362 * $(REF formattedWrite, std,format);
364 void writef(Char
, A
...)(scope const(Char
)[] fmt
, A args
)
366 import std
.format
.write
: formattedWrite
;
367 formattedWrite(this, fmt
, args
);
373 OutBuffer b
= new OutBuffer();
374 b
.writef("a%sb", 16);
375 assert(b
.toString() == "a16b");
379 void writef(alias fmt
, A
...)(A args
)
380 if (isSomeString
!(typeof(fmt
)))
382 import std
.format
: checkFormatException
;
384 alias e
= checkFormatException
!(fmt
, A
);
385 static assert(!e
, e
);
386 return this.writef(fmt
, args
);
392 OutBuffer b
= new OutBuffer();
394 assert(b
.toString() == "a16b");
398 * Formats and writes its arguments in text format to the OutBuffer,
399 * followed by a newline.
402 * fmt = format string as described in $(REF formattedWrite, std,format)
403 * args = arguments to be formatted
406 * $(REF _writefln, std,stdio);
407 * $(REF formattedWrite, std,format);
409 void writefln(Char
, A
...)(scope const(Char
)[] fmt
, A args
)
411 import std
.format
.write
: formattedWrite
;
412 formattedWrite(this, fmt
, args
);
419 OutBuffer b
= new OutBuffer();
420 b
.writefln("a%sb", 16);
421 assert(b
.toString() == "a16b\n");
425 void writefln(alias fmt
, A
...)(A args
)
426 if (isSomeString
!(typeof(fmt
)))
428 import std
.format
: checkFormatException
;
430 alias e
= checkFormatException
!(fmt
, A
);
431 static assert(!e
, e
);
432 return this.writefln(fmt
, args
);
438 OutBuffer b
= new OutBuffer();
439 b
.writefln
!"a%sb"(16);
440 assert(b
.toString() == "a16b\n");
443 /*****************************************
444 * At offset index into buffer, create nbytes of space by shifting upwards
445 * all data past index.
448 void spread(size_t index
, size_t nbytes
) pure nothrow @safe
451 assert(index
<= offset
);
457 // This is an overlapping copy - should use memmove()
458 for (size_t i
= offset
; i
> index
; )
461 data
[i
+ nbytes
] = data
[i
];
470 import std
.string
: cmp;
472 OutBuffer buf
= new OutBuffer();
474 assert(buf
.offset
== 0);
476 buf
.write(cast(byte) 0x20);
478 buf
.printf(" %d", 62665);
479 assert(cmp(buf
.toString(), "hello world 62665") == 0);
482 assert(cmp(buf
.toString(), "") == 0);
483 buf
.write("New data");
484 assert(cmp(buf
.toString(),"New data") == 0);
490 static assert(isOutputRange
!(OutBuffer
, char));
492 import std
.algorithm
;
494 OutBuffer buf
= new OutBuffer();
496 assert(buf
.toBytes() == "hello");
499 OutBuffer buf
= new OutBuffer();
501 version (LittleEndian
)
502 assert(buf
.toBytes() == "h\x00e\x00l\x00l\x00o\x00");
504 assert(buf
.toBytes() == "\x00h\x00e\x00l\x00l\x00o");
507 OutBuffer buf
= new OutBuffer();
509 version (LittleEndian
)
510 assert(buf
.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00");
512 assert(buf
.toBytes() == "\x00\x00\x00h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o");