1 module xbuffer.buffer; 2 3 import std.bitmanip : swapEndian; 4 import std.string : toUpper; 5 import std.system : Endian, endian; 6 import std.traits : isArray, isBoolean, isIntegral, isFloatingPoint, isSomeChar, Unqual; 7 8 import xbuffer.memory : malloc, realloc, _free = free; 9 import xbuffer.varint : isVar, Var; 10 11 //TODO remove 12 import std.stdio : writeln; 13 14 alias ForeachType(T) = typeof(T.init[0]); 15 16 enum canSwapEndianness(T) = isBoolean!T || isIntegral!T || isFloatingPoint!T || isSomeChar!T || (is(T == struct) && canSwapEndiannessImpl!T); 17 18 private bool canSwapEndiannessImpl(T)() { 19 static if(is(T == struct)) { 20 import std.traits : Fields; 21 bool ret = true; 22 foreach(field ; Fields!T) { 23 if(!canSwapEndianness!field) ret = false; 24 } 25 return ret; 26 } else { 27 return false; 28 } 29 } 30 31 unittest { 32 33 static assert(canSwapEndianness!byte); 34 static assert(canSwapEndianness!int); 35 static assert(canSwapEndianness!double); 36 static assert(canSwapEndianness!char); 37 static assert(canSwapEndianness!dchar); 38 39 } 40 41 unittest { 42 43 static struct A {} 44 45 static struct B { int a, b; } 46 47 static class C {} 48 49 static struct D { int a; B b; } 50 51 static struct E { B b; C c; } 52 53 static struct F { void a(){} float b; } 54 55 static struct G { @property Object a(){ return null; } } 56 57 static struct H { ubyte[] a; } 58 59 static struct I { int* a; } 60 61 static assert(canSwapEndianness!A); 62 static assert(canSwapEndianness!B); 63 static assert(!canSwapEndianness!C); 64 static assert(canSwapEndianness!D); 65 static assert(!canSwapEndianness!E); 66 static assert(canSwapEndianness!F); 67 static assert(canSwapEndianness!G); 68 static assert(!canSwapEndianness!H); 69 static assert(!canSwapEndianness!I); 70 71 } 72 73 private union EndianSwapper(T) if(canSwapEndianness!T) { 74 75 enum builtInSwap = T.sizeof == 2 || T.sizeof == 4 || T.sizeof == 8; 76 77 T value; 78 void[T.sizeof] data; 79 ubyte[T.sizeof] bytes; 80 81 this(T value) { 82 this.value = value; 83 } 84 85 this(void[] data) { 86 this.data = data; 87 } 88 89 static if(T.sizeof == 2) ushort _swap; 90 else static if(T.sizeof == 4) uint _swap; 91 else static if(T.sizeof == 8) ulong _swap; 92 93 void swap() { 94 static if(builtInSwap) _swap = swapEndian(_swap); 95 else static if(T.sizeof > 1) { 96 import std.algorithm.mutation : swap; 97 foreach(i ; 0..T.sizeof>>1) { 98 swap(bytes[i], bytes[T.sizeof-i-1]); 99 } 100 } 101 } 102 103 } 104 105 unittest { 106 107 static struct Test { 108 109 int a, b, c; 110 111 } 112 113 static assert(Test.sizeof == 12); 114 115 EndianSwapper!Test swapper; 116 swapper.value = Test(1, 2, 3); 117 118 version(BigEndian) assert(swapper.bytes == [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]); 119 version(LittleEndian) assert(swapper.bytes == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]); 120 121 swapper.swap(); 122 123 version(BigEndian) assert(swapper.bytes == [3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0]); 124 version(LittleEndian) assert(swapper.bytes == [0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 1]); 125 126 assert(swapper.value == Test(3 << 24, 2 << 24, 1 << 24)); 127 128 } 129 130 /** 131 * Exception thrown when the buffer cannot read the requested 132 * data. 133 */ 134 class BufferOverflowException : Exception { 135 136 this(string file=__FILE__, size_t line=__LINE__) pure nothrow @safe @nogc { 137 super("The buffer's index has exceeded its length", file, line); 138 } 139 140 } 141 142 static if(__traits(compiles, () @nogc { throw new Exception(""); })) version = DIP1008; 143 144 /** 145 * Buffer for writing and reading binary data. 146 */ 147 class Buffer { 148 149 private immutable size_t chunk; 150 151 private void[] _data; 152 private size_t _index = 0; 153 private size_t _length = 0; 154 155 /** 156 * Creates a buffer specifying the chunk size. 157 * This should be the default constructor for re-used buffers and 158 * input buffers. 159 */ 160 this(size_t chunk) pure nothrow @trusted @nogc { 161 this.chunk = chunk == 0 ? 1 : chunk; 162 //_data = malloc(this.chunk); 163 _data = realloc(_data.ptr, this.chunk); 164 } 165 166 /// 167 pure nothrow @safe unittest { 168 169 Buffer buffer = new Buffer(8); 170 171 // 8 bytes allocated by the constructor 172 assert(buffer.capacity == 8); 173 174 // writing 4 bytes does not alter the capacity 175 buffer.write(0); 176 assert(buffer.capacity == 8); 177 178 // writing 8 bytes requires a new allocation (because 4 + 8 is 179 // higher than the current capacity of 8). 180 // The new capacity is rounded up to the nearest multiple of the chunk size. 181 buffer.write(0L); 182 assert(buffer.capacity == 16); 183 184 } 185 186 /** 187 * Creates a buffer from an array of data. 188 * The chunk size is set to the size of array. 189 */ 190 this(T)(in T[] data...) pure nothrow @trusted @nogc if(canSwapEndianness!T) { 191 this(data.length * T.sizeof); 192 _length = _data.length; 193 _data[0..$] = cast(void[])data; 194 } 195 196 /// 197 pure nothrow @safe unittest { 198 199 Buffer buffer = new Buffer(cast(ubyte[])[1, 2, 3, 4]); 200 assert(buffer.index == 0); 201 assert(buffer.length == 4); 202 203 buffer = new Buffer([1, 2]); 204 assert(buffer.index == 0); 205 assert(buffer.length == 8); 206 207 } 208 209 private void resize(size_t requiredSize) pure nothrow @trusted @nogc { 210 immutable rem = requiredSize / chunk; 211 immutable size = (requiredSize + chunk - 1) / chunk * chunk; 212 _data = realloc(_data.ptr, size); 213 } 214 215 @property T[] data(T=void)() pure nothrow @trusted @nogc if((canSwapEndianness!T || is(T == void)) && T.sizeof == 1) { 216 return cast(T[])_data[_index.._length]; 217 } 218 219 @property T[] data(T)() pure nothrow @nogc if(canSwapEndianness!T && T.sizeof != 1) { 220 return cast(T[])_data[_index.._length]; 221 } 222 223 /** 224 * Sets new data and resets the index. 225 */ 226 @property auto data(T)(in T[] data) pure nothrow @trusted @nogc { 227 _index = 0; 228 _length = data.length * T.sizeof; 229 if(_length > _data.length) this.resize(_length); 230 _data[0.._length] = cast(void[])data; 231 return data; 232 } 233 234 /// 235 pure nothrow @trusted unittest { 236 237 Buffer buffer = new Buffer(2); 238 239 buffer.data = cast(ubyte[])[0, 0, 0, 1]; 240 assert(buffer.index == 0); // resetted when setting new data 241 assert(buffer.length == 4); 242 version(BigEndian) assert(buffer.data!uint == [1]); 243 version(LittleEndian) assert(buffer.data!uint == [1 << 24]); 244 245 buffer.data = "hello"; 246 assert(buffer.index == 0); 247 assert(buffer.length == 5); 248 assert(buffer.data == "hello"); 249 250 } 251 252 /** 253 * Gets the current write/read index of the buffer. 254 * The index can be set to 0 using the `reset` method. 255 */ 256 @property size_t index() pure nothrow @safe @nogc { 257 return _index; 258 } 259 260 /** 261 * Gets the length of the buffer. 262 */ 263 @property size_t length() pure nothrow @safe @nogc { 264 return _length; 265 } 266 267 /** 268 * Resets the buffer setting the index and its length to 0. 269 */ 270 void reset() pure nothrow @safe @nogc { 271 _index = 0; 272 _length = 0; 273 } 274 275 /** 276 * Gets the size of the data allocated by the buffer. 277 */ 278 @property size_t capacity() pure nothrow @safe @nogc { 279 return _data.length; 280 } 281 282 void back(size_t amount) pure nothrow @safe @nogc { 283 assert(amount <= _index); 284 _index -= amount; 285 } 286 287 // ---------- 288 // operations 289 // ---------- 290 291 /** 292 * Check whether the data of the buffer is equals to 293 * the given array. 294 */ 295 bool opEquals(T)(T[] data) pure nothrow @safe @nogc if(T.sizeof == 1) { 296 return this.data!T == data; 297 } 298 299 /// ditto 300 bool opEquals(T)(T[] data) pure nothrow @nogc if(T.sizeof != 1) { 301 return this.data!T == data; 302 } 303 304 /// 305 pure nothrow @trusted unittest { 306 307 Buffer buffer = new Buffer([1, 2, 3]); 308 309 assert(buffer == [1, 2, 3]); 310 version(BigEndian) assert(buffer == cast(ubyte[])[0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]); 311 version(LittleEndian) assert(buffer == cast(ubyte[])[1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]); 312 313 } 314 315 // ----- 316 // write 317 // ----- 318 319 private void need(size_t size) pure nothrow @safe @nogc { 320 size += _length; 321 if(size > this.capacity) this.resize(size); 322 } 323 324 private void writeDataImpl(in void[] data) pure nothrow @trusted @nogc { 325 immutable start = _length; 326 _length += data.length; 327 _data[start.._length] = data; 328 } 329 330 /** 331 * Writes data to the buffer and expands if it is not big enough. 332 */ 333 void writeData(in void[] data) pure nothrow @safe @nogc { 334 this.need(data.length); 335 this.writeDataImpl(data); 336 } 337 338 /** 339 * Writes data to buffer using the given endianness. 340 */ 341 void write(Endian endianness, T)(T value) pure nothrow @trusted @nogc if(canSwapEndianness!T) { 342 EndianSwapper!T swapper = EndianSwapper!T(value); 343 static if(endianness != endian && T.sizeof > 1) swapper.swap(); 344 this.writeData(swapper.data); 345 } 346 347 /// 348 unittest { 349 350 Buffer buffer = new Buffer(4); 351 buffer.write!(Endian.bigEndian)(4); 352 buffer.write!(Endian.littleEndian)(4); 353 assert(buffer.data!ubyte == [0, 0, 0, 4, 4, 0, 0, 0]); 354 355 } 356 357 /** 358 * Writes data to the buffer using the system's endianness. 359 */ 360 void write(T)(T value) pure nothrow @safe @nogc if(canSwapEndianness!T) { 361 this.write!(endian, T)(value); 362 } 363 364 /// 365 pure nothrow @safe unittest { 366 367 Buffer buffer = new Buffer(5); 368 buffer.write(ubyte(5)); 369 buffer.write(10); 370 version(BigEndian) assert(buffer.data!ubyte == [5, 0, 0, 0, 10]); 371 version(LittleEndian) assert(buffer.data!ubyte == [5, 10, 0, 0, 0]); 372 373 } 374 375 /** 376 * Writes an array using the given endianness. 377 */ 378 void write(Endian endianness, T)(in T value) pure nothrow @trusted @nogc if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) { 379 static if(endianness == endian || T.sizeof <= 1) { 380 this.writeData(value); 381 } else { 382 this.need(value.length * ForeachType!T.sizeof); 383 foreach(element ; value) { 384 auto swapper = EndianSwapper!(ForeachType!T)(element); 385 swapper.swap(); 386 this.writeDataImpl(swapper.data); 387 } 388 } 389 } 390 391 /// 392 pure nothrow @safe unittest { 393 394 Buffer buffer = new Buffer(8); 395 buffer.write!(Endian.bigEndian)([1, 2, 3]); 396 assert(buffer.capacity == 16); 397 assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]); 398 399 buffer.reset(); 400 buffer.write!(Endian.littleEndian)(cast(short[])[-2, 2]); 401 assert(buffer.data!ubyte == [254, 255, 2, 0]); 402 403 buffer.reset(); 404 buffer.write!(Endian.bigEndian, wstring)("test"w); 405 assert(buffer.data!ubyte == [0, 't', 0, 'e', 0, 's', 0, 't']); 406 407 } 408 409 /** 410 * Writes an array using the system's endianness. 411 */ 412 void write(T)(in T value) pure nothrow @safe @nogc if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) { 413 this.write!(endian, T)(value); 414 } 415 416 /// 417 pure nothrow @safe unittest { 418 419 Buffer buffer = new Buffer(8); 420 buffer.write(cast(ubyte[])[1, 2, 3, 4]); 421 buffer.write("test"); 422 assert(buffer.data!ubyte == [1, 2, 3, 4, 't', 'e', 's', 't']); 423 buffer.write([1, 2]); 424 version(BigEndian) assert(buffer.data!ubyte == [1, 2, 3, 4, 't', 'e', 's', 't', 0, 0, 0, 1, 0, 0, 0, 2]); 425 version(LittleEndian) assert(buffer.data!ubyte == [1, 2, 3, 4, 't', 'e', 's', 't', 1, 0, 0, 0, 2, 0, 0, 0]); 426 427 } 428 429 /** 430 * Writes a varint. 431 */ 432 void writeVar(T)(T value) pure nothrow @safe @nogc if(isIntegral!T && T.sizeof > 1) { 433 Var!T.encode(this, value); 434 } 435 436 /// ditto 437 void write(T:Var!B, B)(B value) pure nothrow @safe @nogc { 438 this.writeVar!(T.Base)(value); 439 } 440 441 /// 442 pure nothrow @safe unittest { 443 444 import xbuffer.varint; 445 446 Buffer buffer = new Buffer(8); 447 buffer.writeVar(1); 448 buffer.write!varuint(1); 449 assert(buffer.data!ubyte == [2, 1]); 450 451 } 452 453 /** 454 * Writes data at the given index. 455 */ 456 void write(alias E=endian, T)(in T value, size_t index) pure nothrow @safe if(is(typeof(E) : Endian) && (canSwapEndianness!T || is(T == void) || isArray!T && (canSwapEndianness!(ForeachType!T) || is(ForeachType!T == void))) || is(E == struct) && isVar!E) { 457 index += _index; 458 assert(index < _length); 459 Buffer tmp = new Buffer(T.sizeof); 460 tmp.write!E(value); 461 tmp.write(_data[_index+index.._length]); 462 _length = _index + index; // resets the writing index 463 this.writeData(tmp.data); 464 //tmp.destroy(); 465 } 466 467 /// ditto 468 pure nothrow @trusted unittest { 469 470 import xbuffer.varint; 471 472 Buffer buffer = new Buffer([1, 2, 3]); 473 buffer.write(0, 0); 474 assert(buffer.data!int == [0, 1, 2, 3]); 475 476 buffer.data = cast(ubyte[])[0, 1, 2, 5]; 477 buffer.write(cast(ubyte[])[3, 4], 3); 478 assert(buffer.data!ubyte == [0, 1, 2, 3, 4, 5]); 479 480 buffer.data = cast(ubyte[])[1, 2]; 481 buffer.write!varuint(118485, 1); 482 assert(buffer.data!ubyte == [1, 213, 157, 7, 2]); 483 484 buffer.data = "hellld"; 485 buffer.write('o', 4); 486 buffer.write(" wor", 5); 487 assert(buffer.data == "hello world"); 488 489 } 490 491 // ---- 492 // read 493 // ---- 494 495 /** 496 * Indicates whether an array of length `size` or the given type 497 * can be read without any exceptions thrown. 498 */ 499 bool canRead(size_t size) pure nothrow @safe @nogc { 500 return _index + size <= _length; 501 } 502 503 /// ditto 504 bool canRead(T)() pure nothrow @safe @nogc if(canSwapEndianness!T) { 505 return this.canRead(T.sizeof); 506 } 507 508 /// 509 unittest { 510 511 import xbuffer.varint; 512 513 Buffer buffer = new Buffer(cast(ubyte[])[128, 200, 3]); 514 assert(buffer.canRead(2)); 515 assert(buffer.canRead(3)); 516 assert(!buffer.canRead(4)); 517 assert(buffer.canRead!byte()); 518 assert(buffer.canRead!short()); 519 assert(!buffer.canRead!int()); 520 521 } 522 523 /** 524 * Reads the amount of data requested. 525 * Throws: BufferOverflowException if there isn't enough data to read. 526 */ 527 void[] readData(size_t size) pure @safe { 528 if(!this.canRead(size)) throw new BufferOverflowException(); 529 _index += size; 530 return _data[_index-size.._index]; 531 } 532 533 /// 534 pure @safe unittest { 535 536 Buffer buffer = new Buffer([1]); 537 assert(buffer.read!int() == 1); 538 try { 539 buffer.read!int(); assert(0); 540 } catch(BufferOverflowException ex) { 541 assert(ex.file == __FILE__); 542 } 543 544 } 545 546 /** 547 * Reads a value with the given endianness. 548 * Throws: BufferOverflowException if there isn't enough data to read. 549 */ 550 T read(Endian endianness, T)() pure @trusted if(canSwapEndianness!T) { 551 EndianSwapper!T swapper = EndianSwapper!T(this.readData(T.sizeof)); 552 static if(endianness != endian) swapper.swap(); 553 return swapper.value; 554 } 555 556 /// 557 pure @safe unittest { 558 559 Buffer buffer = new Buffer(cast(ubyte[])[0, 0, 0, 1, 1, 0]); 560 assert(buffer.read!(Endian.bigEndian, int)() == 1); 561 assert(buffer.read!(Endian.littleEndian, short)() == 1); 562 563 } 564 565 /** 566 * Reads a value using the system's endianness. 567 * Throws: BufferOverflowException if there isn't enough data to read. 568 */ 569 T read(T)() pure @safe if(canSwapEndianness!T) { 570 return this.read!(endian, T)(); 571 } 572 573 /// 574 pure @safe unittest { 575 576 version(BigEndian) Buffer buffer = new Buffer([0, 0, 0, 1]); 577 version(LittleEndian) Buffer buffer = new Buffer([1, 0, 0, 0]); 578 assert(buffer.read!int() == 1); 579 580 } 581 582 /** 583 * Reads an array using the given endianness. 584 * Throws: BufferOverflowException if there isn't enough data to read. 585 */ 586 T read(Endian endianness, T)(size_t length) pure @trusted if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) { 587 T ret = cast(T)this.readData(length * ForeachType!T.sizeof); 588 static if(endianness != endian && T.sizeof != 1) { 589 foreach(ref element ; ret) { 590 EndianSwapper!(ForeachType!T) swapper = EndianSwapper!(ForeachType!T)(element); 591 swapper.swap(); 592 element = swapper.value; 593 } 594 } 595 return ret; 596 } 597 598 /// 599 pure @safe unittest { 600 601 Buffer buffer = new Buffer(16); 602 603 buffer.write!(Endian.bigEndian)(16); 604 buffer.write!(Endian.bigEndian)(32); 605 buffer.write!(Endian.littleEndian)(32); 606 buffer.write!(Endian.littleEndian)(16); 607 assert(buffer.data!ubyte == [0, 0, 0, 16, 0, 0, 0, 32, 32, 0, 0, 0, 16, 0, 0, 0]); 608 609 assert(buffer.read!(Endian.bigEndian, int[])(2) == [16, 32]); 610 assert(buffer.read!(Endian.littleEndian, int[])(2) == [32, 16]); 611 612 } 613 614 /** 615 * Reads an array using the system's endianness. 616 * Throws: BufferOverflowException if there isn't enough data to read. 617 */ 618 T read(T)(size_t size) pure @trusted if(isArray!T && (is(ForeachType!T : void) || canSwapEndianness!(ForeachType!T))) { 619 return this.read!(endian, T)(size); 620 } 621 622 /// 623 pure @safe unittest { 624 625 Buffer buffer = new Buffer("!hello"); 626 assert(buffer.read!(ubyte[])(1) == [33]); 627 assert(buffer.read!string(5) == "hello"); 628 629 buffer.data = [1, 2, 3]; 630 version(BigEndian) assert(buffer.data!ubyte == [0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]); 631 version(LittleEndian) assert(buffer.data!ubyte == [1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0]); 632 assert(buffer.read!(int[])(3) == [1, 2, 3]); 633 634 } 635 636 /** 637 * Reads a varint. 638 * Throws: BufferOverflowException if there isn't enough data to read. 639 */ 640 T readVar(T)() pure @safe if(isIntegral!T && T.sizeof > 1) { 641 return Var!T.decode!true(this); 642 } 643 644 /// ditto 645 B read(T:Var!B, B)() pure @safe { 646 return this.readVar!B(); 647 } 648 649 /// 650 pure @safe unittest { 651 652 import xbuffer.varint; 653 654 Buffer buffer = new Buffer(cast(ubyte[])[2, 1]); 655 assert(buffer.readVar!int() == 1); 656 assert(buffer.read!varuint() == 1); 657 658 } 659 660 // ---- 661 // peek 662 // ---- 663 664 /** 665 * Peeks some data (read it but without changing the buffer's index). 666 * Throws: BufferOverflowException if there isn't enough data to read. 667 */ 668 void[] peekData(size_t size) pure { 669 if(!this.canRead(size)) throw new BufferOverflowException(); 670 return _data[_index.._index+size]; 671 } 672 673 unittest { 674 675 Buffer buffer = new Buffer([1]); 676 buffer.peekData(4); 677 assert(buffer.index == 0); 678 679 } 680 681 /** 682 * Peeks a value using the given endianness. 683 * Throws: BufferOverflowException if there isn't enough data to read. 684 */ 685 T peek(Endian endianness, T)() pure @trusted if(canSwapEndianness!T) { 686 EndianSwapper!T swapper = EndianSwapper!T(this.peekData(T.sizeof)); 687 static if(endianness != endian) swapper.swap(); 688 return swapper.value; 689 } 690 691 /// 692 pure @safe unittest { 693 694 Buffer buffer = new Buffer(cast(ubyte[])[0, 0, 0, 1]); 695 assert(buffer.peek!(Endian.bigEndian, int)() == 1); 696 assert(buffer.peek!(Endian.littleEndian, int)() == 1 << 24); 697 assert(buffer.peek!(Endian.bigEndian, short)() == 0); 698 699 } 700 701 /** 702 * Peeks some data using the system's endianness. 703 * Throws: BufferOverflowException if there isn't enough data to read. 704 */ 705 T peek(T)() pure @safe if(canSwapEndianness!T) { 706 return this.peek!(endian, T)(); 707 } 708 709 /// 710 pure @safe unittest { 711 712 Buffer buffer = new Buffer([1, 2]); 713 assert(buffer.peek!int() == 1); 714 assert(buffer.index == 0); 715 assert(buffer.peek!int() == buffer.read!int()); 716 assert(buffer.index == 4); 717 assert(buffer.peek!int() == 2); 718 719 } 720 721 /** 722 * Peeks a varint. 723 * Throws: BufferOverflowException if there isn't enough data to read. 724 */ 725 T peekVar(T)() pure @safe if(isIntegral!T && T.sizeof > 1) { 726 return Var!T.decode!false(this); 727 } 728 729 /// ditto 730 B peek(T:Var!B, B)() pure @safe { 731 return this.peekVar!B(); 732 } 733 734 /// 735 pure @safe unittest { 736 737 import xbuffer.varint; 738 739 Buffer buffer = new Buffer(cast(ubyte[])[2]); 740 assert(buffer.peekVar!int() == 1); 741 assert(buffer.peek!varuint() == 2); 742 743 } 744 745 // ----------- 746 // destruction 747 // ----------- 748 749 void free() pure nothrow @nogc { 750 _free(_data.ptr); 751 } 752 753 void __xdtor() pure nothrow @nogc { 754 this.free(); 755 } 756 757 ~this() { 758 this.free(); 759 } 760 761 } 762 763 /// 764 unittest { 765 766 import xbuffer.memory; 767 768 // a buffer can be garbage collected 769 Buffer gc = new Buffer(16); 770 771 // or manually allocated 772 // alloc is a function provided by the xbuffer.memory module 773 Buffer b = alloc!Buffer(16); 774 775 // the memory is realsed with free, which is called by the garbage 776 // collector of by the `free` function in the `xbuffer.memory` module 777 free(b); 778 779 } 780 781 unittest { 782 783 import xbuffer.memory; 784 785 void[] data = calloc(923); 786 787 auto buffer = alloc!Buffer(1024); 788 assert(buffer.length == 0); 789 790 buffer.writeData(data); 791 assert(buffer.index == 0); 792 assert(buffer.length == 923); 793 assert(buffer.capacity == 1024); 794 795 buffer.writeData(data); 796 assert(buffer.length == 1846); 797 assert(buffer.capacity == 2048); 798 799 data = realloc(data.ptr, 1); 800 801 buffer.data = data; 802 assert(buffer.length == 1); 803 assert(buffer.capacity == 2048); 804 805 data = realloc(data.ptr, 2049); 806 807 buffer.data = data; 808 assert(buffer.length == 2049); 809 assert(buffer.capacity == 3072); 810 811 free(data.ptr); 812 free(buffer); 813 814 }