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 0 to the internal buffer.
179 void fill0(size_t nbytes
)
182 data
[offset
.. offset
+ nbytes
] = 0;
186 /**********************************
187 * 0-fill to align on power of 2 boundary.
190 void alignSize(size_t alignsize
)
193 assert(alignsize
&& (alignsize
& (alignsize
- 1)) == 0);
197 assert((offset
& (alignsize
- 1)) == 0);
201 auto nbytes
= offset
& (alignsize
- 1);
203 fill0(alignsize
- nbytes
);
206 /// Clear the data in the buffer
212 /****************************************
213 * Optimize common special case alignSize(2)
222 /****************************************
223 * Optimize common special case alignSize(4)
229 { auto nbytes
= (4 - offset
) & 3;
234 /**************************************
235 * Convert internal buffer to array of chars.
238 override string
toString() const
240 //printf("OutBuffer.toString()\n");
241 return cast(string
) data
[0 .. offset
].idup
;
245 /*****************************************
246 * Append output of C's vprintf() to internal buffer.
249 void vprintf(scope string format
, va_list args
) @trusted nothrow
251 import core
.stdc
.stdio
: vsnprintf
;
252 import core
.stdc
.stdlib
: alloca
;
253 import std
.string
: toStringz
;
255 version (StdUnittest
)
256 char[3] buffer
= void; // trigger reallocation
258 char[128] buffer
= void;
261 // Can't use `tempCString()` here as it will result in compilation error:
262 // "cannot mix core.std.stdlib.alloca() and exception handling".
263 auto f
= toStringz(format
);
265 auto psize
= buffer
.length
;
269 va_copy(args2
, args
);
270 count
= vsnprintf(p
, psize
, f
, args2
);
274 if (psize
> psize
.max
/ 2) assert(0); // overflow check
277 else if (count
>= psize
)
279 if (count
== count
.max
) assert(0); // overflow check
285 p
= cast(char *) alloca(psize
); // buffer too small, try again with larger size
287 write(cast(ubyte[]) p
[0 .. count
]);
290 /*****************************************
291 * Append output of C's printf() to internal buffer.
294 void printf(scope string format
, ...) @trusted
297 va_start(ap
, format
);
303 * Formats and writes its arguments in text format to the OutBuffer.
306 * fmt = format string as described in $(REF formattedWrite, std,format)
307 * args = arguments to be formatted
310 * $(REF _writef, std,stdio);
311 * $(REF formattedWrite, std,format);
313 void writef(Char
, A
...)(scope const(Char
)[] fmt
, A args
)
315 import std
.format
.write
: formattedWrite
;
316 formattedWrite(this, fmt
, args
);
322 OutBuffer b
= new OutBuffer();
323 b
.writef("a%sb", 16);
324 assert(b
.toString() == "a16b");
328 void writef(alias fmt
, A
...)(A args
)
329 if (isSomeString
!(typeof(fmt
)))
331 import std
.format
: checkFormatException
;
333 alias e
= checkFormatException
!(fmt
, A
);
334 static assert(!e
, e
);
335 return this.writef(fmt
, args
);
341 OutBuffer b
= new OutBuffer();
343 assert(b
.toString() == "a16b");
347 * Formats and writes its arguments in text format to the OutBuffer,
348 * followed by a newline.
351 * fmt = format string as described in $(REF formattedWrite, std,format)
352 * args = arguments to be formatted
355 * $(REF _writefln, std,stdio);
356 * $(REF formattedWrite, std,format);
358 void writefln(Char
, A
...)(scope const(Char
)[] fmt
, A args
)
360 import std
.format
.write
: formattedWrite
;
361 formattedWrite(this, fmt
, args
);
368 OutBuffer b
= new OutBuffer();
369 b
.writefln("a%sb", 16);
370 assert(b
.toString() == "a16b\n");
374 void writefln(alias fmt
, A
...)(A args
)
375 if (isSomeString
!(typeof(fmt
)))
377 import std
.format
: checkFormatException
;
379 alias e
= checkFormatException
!(fmt
, A
);
380 static assert(!e
, e
);
381 return this.writefln(fmt
, args
);
387 OutBuffer b
= new OutBuffer();
388 b
.writefln
!"a%sb"(16);
389 assert(b
.toString() == "a16b\n");
392 /*****************************************
393 * At offset index into buffer, create nbytes of space by shifting upwards
394 * all data past index.
397 void spread(size_t index
, size_t nbytes
) pure nothrow @safe
400 assert(index
<= offset
);
406 // This is an overlapping copy - should use memmove()
407 for (size_t i
= offset
; i
> index
; )
410 data
[i
+ nbytes
] = data
[i
];
419 import std
.string
: cmp;
421 OutBuffer buf
= new OutBuffer();
423 assert(buf
.offset
== 0);
425 buf
.write(cast(byte) 0x20);
427 buf
.printf(" %d", 62665);
428 assert(cmp(buf
.toString(), "hello world 62665") == 0);
431 assert(cmp(buf
.toString(), "") == 0);
432 buf
.write("New data");
433 assert(cmp(buf
.toString(),"New data") == 0);
439 static assert(isOutputRange
!(OutBuffer
, char));
441 import std
.algorithm
;
443 OutBuffer buf
= new OutBuffer();
445 assert(buf
.toBytes() == "hello");
448 OutBuffer buf
= new OutBuffer();
450 version (LittleEndian
)
451 assert(buf
.toBytes() == "h\x00e\x00l\x00l\x00o\x00");
453 assert(buf
.toBytes() == "\x00h\x00e\x00l\x00l\x00o");
456 OutBuffer buf
= new OutBuffer();
458 version (LittleEndian
)
459 assert(buf
.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00");
461 assert(buf
.toBytes() == "\x00\x00\x00h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o");