1 // Written in the D programming language. 2 3 /** 4 Serialize data to `ubyte` arrays. 5 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) 10 * 11 * $(SCRIPT inhibitQuickIndex = 1;) 12 */ 13 module std.outbuffer; 14 15 import core.stdc.stdarg; 16 import std.traits : isSomeString; 17 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. 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). 28 */ 29 30 class OutBuffer 31 { 32 ubyte[] data; 33 size_t offset; 34 invariant()35 invariant() 36 { 37 assert(offset <= data.length); 38 } 39 40 pure nothrow @safe 41 { 42 /********************************* 43 * Convert to array of bytes. 44 */ inout(ubyte)45 inout(ubyte)[] toBytes() scope inout { return data[0 .. offset]; } 46 47 /*********************************** 48 * Preallocate nbytes more to the size of the internal buffer. 49 * 50 * This is a 51 * speed optimization, a good guess at the maximum size of the resulting 52 * buffer will improve performance by eliminating reallocations and copying. 53 */ reserve(size_t nbytes)54 void reserve(size_t nbytes) @trusted 55 in 56 { 57 assert(offset + nbytes >= offset); 58 } 59 out 60 { 61 assert(offset + nbytes <= data.length); 62 } 63 do 64 { 65 if (data.length < offset + nbytes) 66 { 67 void[] vdata = data; 68 vdata.length = (offset + nbytes + 7) * 2; // allocates as void[] to not set BlkAttr.NO_SCAN 69 data = cast(ubyte[]) vdata; 70 } 71 } 72 73 /********************************** 74 * put enables OutBuffer to be used as an OutputRange. 75 */ 76 alias put = write; 77 78 /************************************* 79 * Append data to the internal buffer. 80 */ 81 write(scope const (ubyte)[]bytes)82 void write(scope const(ubyte)[] bytes) 83 { 84 reserve(bytes.length); 85 data[offset .. offset + bytes.length] = bytes[]; 86 offset += bytes.length; 87 } 88 write(scope const (wchar)[]chars)89 void write(scope const(wchar)[] chars) @trusted 90 { 91 write(cast(ubyte[]) chars); 92 } 93 write(scope const (dchar)[]chars)94 void write(scope const(dchar)[] chars) @trusted 95 { 96 write(cast(ubyte[]) chars); 97 } 98 write(ubyte b)99 void write(ubyte b) /// ditto 100 { 101 reserve(ubyte.sizeof); 102 this.data[offset] = b; 103 offset += ubyte.sizeof; 104 } 105 write(byte b)106 void write(byte b) { write(cast(ubyte) b); } /// ditto write(char c)107 void write(char c) { write(cast(ubyte) c); } /// ditto write(dchar c)108 void write(dchar c) { write(cast(uint) c); } /// ditto 109 write(ushort w)110 void write(ushort w) @trusted /// ditto 111 { 112 reserve(ushort.sizeof); 113 *cast(ushort *)&data[offset] = w; 114 offset += ushort.sizeof; 115 } 116 write(short s)117 void write(short s) { write(cast(ushort) s); } /// ditto 118 write(wchar c)119 void write(wchar c) @trusted /// ditto 120 { 121 reserve(wchar.sizeof); 122 *cast(wchar *)&data[offset] = c; 123 offset += wchar.sizeof; 124 } 125 write(uint w)126 void write(uint w) @trusted /// ditto 127 { 128 reserve(uint.sizeof); 129 *cast(uint *)&data[offset] = w; 130 offset += uint.sizeof; 131 } 132 write(int i)133 void write(int i) { write(cast(uint) i); } /// ditto 134 write(ulong l)135 void write(ulong l) @trusted /// ditto 136 { 137 reserve(ulong.sizeof); 138 *cast(ulong *)&data[offset] = l; 139 offset += ulong.sizeof; 140 } 141 write(long l)142 void write(long l) { write(cast(ulong) l); } /// ditto 143 write(float f)144 void write(float f) @trusted /// ditto 145 { 146 reserve(float.sizeof); 147 *cast(float *)&data[offset] = f; 148 offset += float.sizeof; 149 } 150 write(double f)151 void write(double f) @trusted /// ditto 152 { 153 reserve(double.sizeof); 154 *cast(double *)&data[offset] = f; 155 offset += double.sizeof; 156 } 157 write(real f)158 void write(real f) @trusted /// ditto 159 { 160 reserve(real.sizeof); 161 *cast(real *)&data[offset] = f; 162 offset += real.sizeof; 163 } 164 write(scope const (char)[]s)165 void write(scope const(char)[] s) @trusted /// ditto 166 { 167 write(cast(ubyte[]) s); 168 } 169 write(scope const OutBuffer buf)170 void write(scope const OutBuffer buf) /// ditto 171 { 172 write(buf.toBytes()); 173 } 174 175 /**************************************** 176 * Append nbytes of 0 to the internal buffer. 177 */ 178 fill0(size_t nbytes)179 void fill0(size_t nbytes) 180 { 181 reserve(nbytes); 182 data[offset .. offset + nbytes] = 0; 183 offset += nbytes; 184 } 185 186 /********************************** 187 * 0-fill to align on power of 2 boundary. 188 */ 189 alignSize(size_t alignsize)190 void alignSize(size_t alignsize) 191 in 192 { 193 assert(alignsize && (alignsize & (alignsize - 1)) == 0); 194 } 195 out 196 { 197 assert((offset & (alignsize - 1)) == 0); 198 } 199 do 200 { 201 auto nbytes = offset & (alignsize - 1); 202 if (nbytes) 203 fill0(alignsize - nbytes); 204 } 205 206 /// Clear the data in the buffer clear()207 void clear() 208 { 209 offset = 0; 210 } 211 212 /**************************************** 213 * Optimize common special case alignSize(2) 214 */ 215 align2()216 void align2() 217 { 218 if (offset & 1) 219 write(cast(byte) 0); 220 } 221 222 /**************************************** 223 * Optimize common special case alignSize(4) 224 */ 225 align4()226 void align4() 227 { 228 if (offset & 3) 229 { auto nbytes = (4 - offset) & 3; 230 fill0(nbytes); 231 } 232 } 233 234 /************************************** 235 * Convert internal buffer to array of chars. 236 */ 237 toString()238 override string toString() const 239 { 240 //printf("OutBuffer.toString()\n"); 241 return cast(string) data[0 .. offset].idup; 242 } 243 } 244 245 /***************************************** 246 * Append output of C's vprintf() to internal buffer. 247 */ 248 vprintf(scope string format,va_list args)249 void vprintf(scope string format, va_list args) @trusted nothrow 250 { 251 import core.stdc.stdio : vsnprintf; 252 import core.stdc.stdlib : alloca; 253 import std.string : toStringz; 254 255 version (StdUnittest) 256 char[3] buffer = void; // trigger reallocation 257 else 258 char[128] buffer = void; 259 int count; 260 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); 264 auto p = buffer.ptr; 265 auto psize = buffer.length; 266 for (;;) 267 { 268 va_list args2; 269 va_copy(args2, args); 270 count = vsnprintf(p, psize, f, args2); 271 va_end(args2); 272 if (count == -1) 273 { 274 if (psize > psize.max / 2) assert(0); // overflow check 275 psize *= 2; 276 } 277 else if (count >= psize) 278 { 279 if (count == count.max) assert(0); // overflow check 280 psize = count + 1; 281 } 282 else 283 break; 284 285 p = cast(char *) alloca(psize); // buffer too small, try again with larger size 286 } 287 write(cast(ubyte[]) p[0 .. count]); 288 } 289 290 /***************************************** 291 * Append output of C's printf() to internal buffer. 292 */ 293 printf(scope string format,...)294 void printf(scope string format, ...) @trusted 295 { 296 va_list ap; 297 va_start(ap, format); 298 vprintf(format, ap); 299 va_end(ap); 300 } 301 302 /** 303 * Formats and writes its arguments in text format to the OutBuffer. 304 * 305 * Params: 306 * fmt = format string as described in $(REF formattedWrite, std,format) 307 * args = arguments to be formatted 308 * 309 * See_Also: 310 * $(REF _writef, std,stdio); 311 * $(REF formattedWrite, std,format); 312 */ writef(Char,A...)313 void writef(Char, A...)(scope const(Char)[] fmt, A args) 314 { 315 import std.format.write : formattedWrite; 316 formattedWrite(this, fmt, args); 317 } 318 319 /// 320 @safe unittest 321 { 322 OutBuffer b = new OutBuffer(); 323 b.writef("a%sb", 16); 324 assert(b.toString() == "a16b"); 325 } 326 327 /// ditto 328 void writef(alias fmt, A...)(A args) 329 if (isSomeString!(typeof(fmt))) 330 { 331 import std.format : checkFormatException; 332 333 alias e = checkFormatException!(fmt, A); 334 static assert(!e, e); 335 return this.writef(fmt, args); 336 } 337 338 /// 339 @safe unittest 340 { 341 OutBuffer b = new OutBuffer(); 342 b.writef!"a%sb"(16); 343 assert(b.toString() == "a16b"); 344 } 345 346 /** 347 * Formats and writes its arguments in text format to the OutBuffer, 348 * followed by a newline. 349 * 350 * Params: 351 * fmt = format string as described in $(REF formattedWrite, std,format) 352 * args = arguments to be formatted 353 * 354 * See_Also: 355 * $(REF _writefln, std,stdio); 356 * $(REF formattedWrite, std,format); 357 */ writefln(Char,A...)358 void writefln(Char, A...)(scope const(Char)[] fmt, A args) 359 { 360 import std.format.write : formattedWrite; 361 formattedWrite(this, fmt, args); 362 put('\n'); 363 } 364 365 /// 366 @safe unittest 367 { 368 OutBuffer b = new OutBuffer(); 369 b.writefln("a%sb", 16); 370 assert(b.toString() == "a16b\n"); 371 } 372 373 /// ditto 374 void writefln(alias fmt, A...)(A args) 375 if (isSomeString!(typeof(fmt))) 376 { 377 import std.format : checkFormatException; 378 379 alias e = checkFormatException!(fmt, A); 380 static assert(!e, e); 381 return this.writefln(fmt, args); 382 } 383 384 /// 385 @safe unittest 386 { 387 OutBuffer b = new OutBuffer(); 388 b.writefln!"a%sb"(16); 389 assert(b.toString() == "a16b\n"); 390 } 391 392 /***************************************** 393 * At offset index into buffer, create nbytes of space by shifting upwards 394 * all data past index. 395 */ 396 spread(size_t index,size_t nbytes)397 void spread(size_t index, size_t nbytes) pure nothrow @safe 398 in 399 { 400 assert(index <= offset); 401 } 402 do 403 { 404 reserve(nbytes); 405 406 // This is an overlapping copy - should use memmove() 407 for (size_t i = offset; i > index; ) 408 { 409 --i; 410 data[i + nbytes] = data[i]; 411 } 412 offset += nbytes; 413 } 414 } 415 416 /// 417 @safe unittest 418 { 419 import std.string : cmp; 420 421 OutBuffer buf = new OutBuffer(); 422 423 assert(buf.offset == 0); 424 buf.write("hello"); 425 buf.write(cast(byte) 0x20); 426 buf.write("world"); 427 buf.printf(" %d", 62665); 428 assert(cmp(buf.toString(), "hello world 62665") == 0); 429 430 buf.clear(); 431 assert(cmp(buf.toString(), "") == 0); 432 buf.write("New data"); 433 assert(cmp(buf.toString(),"New data") == 0); 434 } 435 436 @safe unittest 437 { 438 import std.range; 439 static assert(isOutputRange!(OutBuffer, char)); 440 441 import std.algorithm; 442 { 443 OutBuffer buf = new OutBuffer(); 444 "hello".copy(buf); 445 assert(buf.toBytes() == "hello"); 446 } 447 { 448 OutBuffer buf = new OutBuffer(); 449 "hello"w.copy(buf); 450 version (LittleEndian) 451 assert(buf.toBytes() == "h\x00e\x00l\x00l\x00o\x00"); 452 version (BigEndian) 453 assert(buf.toBytes() == "\x00h\x00e\x00l\x00l\x00o"); 454 } 455 { 456 OutBuffer buf = new OutBuffer(); 457 "hello"d.copy(buf); 458 version (LittleEndian) 459 assert(buf.toBytes() == "h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o\x00\x00\x00"); 460 version (BigEndian) 461 assert(buf.toBytes() == "\x00\x00\x00h\x00\x00\x00e\x00\x00\x00l\x00\x00\x00l\x00\x00\x00o"); 462 } 463 } 464