1 // Written in the D programming language. 2 /** 3 Utility and ancillary artifacts of `std.experimental.allocator`. This module 4 shouldn't be used directly; its functionality will be migrated into more 5 appropriate parts of `std`. 6 7 Authors: $(HTTP erdani.com, Andrei Alexandrescu), Timon Gehr (`Ternary`) 8 9 Source: $(PHOBOSSRC std/experimental/allocator/common.d) 10 */ 11 module std.experimental.allocator.common; 12 import std.algorithm.comparison, std.traits; 13 14 /** 15 Is `true` iff `A` is an allocator. 16 */ 17 enum isAllocator(A) = (is(typeof(A.allocate(size_t.init)) == void[]) && is(typeof(A.alignment) : size_t)); 18 19 /// 20 @safe @nogc nothrow pure 21 unittest 22 { 23 import std.experimental.allocator.building_blocks.null_allocator : NullAllocator; 24 import std.experimental.allocator.mallocator : Mallocator; 25 import std.experimental.allocator.gc_allocator : GCAllocator; 26 import std.experimental.allocator.mmap_allocator : MmapAllocator; 27 static assert(isAllocator!NullAllocator); 28 static assert(isAllocator!Mallocator); 29 static assert(isAllocator!GCAllocator); 30 static assert(isAllocator!MmapAllocator); 31 static assert(!isAllocator!int); 32 } 33 34 /** 35 Returns the size in bytes of the state that needs to be allocated to hold an 36 object of type `T`. `stateSize!T` is zero for `struct`s that are not 37 nested and have no nonstatic member variables. 38 */ 39 template stateSize(T) 40 { 41 static if (is(T == class) || is(T == interface)) 42 enum stateSize = __traits(classInstanceSize, T); 43 else static if (is(T == struct) || is(T == union)) 44 enum stateSize = Fields!T.length || isNested!T ? T.sizeof : 0; 45 else static if (is(T == void)) 46 enum size_t stateSize = 0; 47 else 48 enum stateSize = T.sizeof; 49 } 50 51 @safe @nogc nothrow pure 52 unittest 53 { 54 static assert(stateSize!void == 0); 55 struct A {} 56 static assert(stateSize!A == 0); 57 struct B { int x; } 58 static assert(stateSize!B == 4); 59 interface I1 {} 60 //static assert(stateSize!I1 == 2 * size_t.sizeof); 61 class C1 {} 62 static assert(stateSize!C1 == 3 * size_t.sizeof); 63 class C2 { char c; } 64 static assert(stateSize!C2 == 4 * size_t.sizeof); 65 static class C3 { char c; } 66 static assert(stateSize!C3 == 2 * size_t.sizeof + char.sizeof); 67 } 68 69 /** 70 Returns `true` if the `Allocator` has the alignment known at compile time; 71 otherwise it returns `false`. 72 */ 73 template hasStaticallyKnownAlignment(Allocator) 74 { 75 enum hasStaticallyKnownAlignment = __traits(compiles, 76 {enum x = Allocator.alignment;}); 77 } 78 79 /** 80 `chooseAtRuntime` is a compile-time constant of type `size_t` that several 81 parameterized structures in this module recognize to mean deferral to runtime of 82 the exact value. For example, $(D BitmappedBlock!(Allocator, 4096)) (described in 83 detail below) defines a block allocator with block size of 4096 bytes, whereas 84 $(D BitmappedBlock!(Allocator, chooseAtRuntime)) defines a block allocator that has a 85 field storing the block size, initialized by the user. 86 */ 87 enum chooseAtRuntime = size_t.max - 1; 88 89 /** 90 `unbounded` is a compile-time constant of type `size_t` that several 91 parameterized structures in this module recognize to mean "infinite" bounds for 92 the parameter. For example, `Freelist` (described in detail below) accepts a 93 `maxNodes` parameter limiting the number of freelist items. If `unbounded` 94 is passed for `maxNodes`, then there is no limit and no checking for the 95 number of nodes. 96 */ 97 enum unbounded = size_t.max; 98 99 /** 100 The alignment that is guaranteed to accommodate any D object allocation on the 101 current platform. 102 */ 103 enum uint platformAlignment = std.algorithm.comparison.max(double.alignof, real.alignof); 104 105 /** 106 The default good size allocation is deduced as `n` rounded up to the 107 allocator's alignment. 108 */ 109 size_t goodAllocSize(A)(auto ref A a, size_t n) 110 { 111 return n.roundUpToMultipleOf(a.alignment); 112 } 113 114 /* 115 Returns s rounded up to a multiple of base. 116 */ 117 @safe @nogc nothrow pure 118 package size_t roundUpToMultipleOf(size_t s, uint base) 119 { 120 assert(base); 121 auto rem = s % base; 122 return rem ? s + base - rem : s; 123 } 124 125 @safe @nogc nothrow pure 126 unittest 127 { 128 assert(10.roundUpToMultipleOf(11) == 11); 129 assert(11.roundUpToMultipleOf(11) == 11); 130 assert(12.roundUpToMultipleOf(11) == 22); 131 assert(118.roundUpToMultipleOf(11) == 121); 132 } 133 134 /* 135 Returns `n` rounded up to a multiple of alignment, which must be a power of 2. 136 */ 137 @safe @nogc nothrow pure 138 package size_t roundUpToAlignment(size_t n, uint alignment) 139 { 140 import std.math.traits : isPowerOf2; 141 assert(alignment.isPowerOf2); 142 immutable uint slack = cast(uint) n & (alignment - 1); 143 const result = slack 144 ? n + alignment - slack 145 : n; 146 assert(result >= n); 147 return result; 148 } 149 150 @safe @nogc nothrow pure 151 unittest 152 { 153 assert(10.roundUpToAlignment(4) == 12); 154 assert(11.roundUpToAlignment(2) == 12); 155 assert(12.roundUpToAlignment(8) == 16); 156 assert(118.roundUpToAlignment(64) == 128); 157 } 158 159 /* 160 Returns `n` rounded down to a multiple of alignment, which must be a power of 2. 161 */ 162 @safe @nogc nothrow pure 163 package size_t roundDownToAlignment(size_t n, uint alignment) 164 { 165 import std.math.traits : isPowerOf2; 166 assert(alignment.isPowerOf2); 167 return n & ~size_t(alignment - 1); 168 } 169 170 @safe @nogc nothrow pure 171 unittest 172 { 173 assert(10.roundDownToAlignment(4) == 8); 174 assert(11.roundDownToAlignment(2) == 10); 175 assert(12.roundDownToAlignment(8) == 8); 176 assert(63.roundDownToAlignment(64) == 0); 177 } 178 179 /* 180 Advances the beginning of `b` to start at alignment `a`. The resulting buffer 181 may therefore be shorter. Returns the adjusted buffer, or null if obtaining a 182 non-empty buffer is impossible. 183 */ 184 @nogc nothrow pure 185 package void[] roundUpToAlignment(void[] b, uint a) 186 { 187 auto e = b.ptr + b.length; 188 auto p = cast(void*) roundUpToAlignment(cast(size_t) b.ptr, a); 189 if (e <= p) return null; 190 return p[0 .. e - p]; 191 } 192 193 @nogc nothrow pure 194 @system unittest 195 { 196 void[] empty; 197 assert(roundUpToAlignment(empty, 4) == null); 198 char[128] buf; 199 // At least one pointer inside buf is 128-aligned 200 assert(roundUpToAlignment(buf, 128) !is null); 201 } 202 203 /* 204 Like `a / b` but rounds the result up, not down. 205 */ 206 @safe @nogc nothrow pure 207 package size_t divideRoundUp(size_t a, size_t b) 208 { 209 assert(b); 210 return (a + b - 1) / b; 211 } 212 213 /* 214 Returns `s` rounded up to a multiple of `base`. 215 */ 216 @nogc nothrow pure 217 package void[] roundStartToMultipleOf(void[] s, uint base) 218 { 219 assert(base); 220 auto p = cast(void*) roundUpToMultipleOf( 221 cast(size_t) s.ptr, base); 222 auto end = s.ptr + s.length; 223 return p[0 .. end - p]; 224 } 225 226 nothrow pure 227 @system unittest 228 { 229 void[] p; 230 assert(roundStartToMultipleOf(p, 16) is null); 231 p = new ulong[10]; 232 assert(roundStartToMultipleOf(p, 16) is p); 233 } 234 235 /* 236 Returns `s` rounded up to the nearest power of 2. 237 */ 238 @safe @nogc nothrow pure 239 package size_t roundUpToPowerOf2(size_t s) 240 { 241 import std.meta : AliasSeq; 242 assert(s <= (size_t.max >> 1) + 1); 243 --s; 244 static if (size_t.sizeof == 4) 245 alias Shifts = AliasSeq!(1, 2, 4, 8, 16); 246 else 247 alias Shifts = AliasSeq!(1, 2, 4, 8, 16, 32); 248 foreach (i; Shifts) 249 { 250 s |= s >> i; 251 } 252 return s + 1; 253 } 254 255 @safe @nogc nothrow pure 256 unittest 257 { 258 assert(0.roundUpToPowerOf2 == 0); 259 assert(1.roundUpToPowerOf2 == 1); 260 assert(2.roundUpToPowerOf2 == 2); 261 assert(3.roundUpToPowerOf2 == 4); 262 assert(7.roundUpToPowerOf2 == 8); 263 assert(8.roundUpToPowerOf2 == 8); 264 assert(10.roundUpToPowerOf2 == 16); 265 assert(11.roundUpToPowerOf2 == 16); 266 assert(12.roundUpToPowerOf2 == 16); 267 assert(118.roundUpToPowerOf2 == 128); 268 assert((size_t.max >> 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); 269 assert(((size_t.max >> 1) + 1).roundUpToPowerOf2 == (size_t.max >> 1) + 1); 270 } 271 272 /* 273 Returns the number of trailing zeros of `x`. 274 */ 275 @safe @nogc nothrow pure 276 package uint trailingZeros(ulong x) 277 { 278 import core.bitop : bsf; 279 return x == 0 ? 64 : bsf(x); 280 } 281 282 @safe @nogc nothrow pure 283 unittest 284 { 285 assert(trailingZeros(0) == 64); 286 assert(trailingZeros(1) == 0); 287 assert(trailingZeros(2) == 1); 288 assert(trailingZeros(3) == 0); 289 assert(trailingZeros(4) == 2); 290 } 291 292 /* 293 Returns `true` if `ptr` is aligned at `alignment`. 294 */ 295 @nogc nothrow pure 296 package bool alignedAt(T)(T* ptr, uint alignment) 297 { 298 return cast(size_t) ptr % alignment == 0; 299 } 300 301 /* 302 Returns the effective alignment of `ptr`, i.e. the largest power of two that is 303 a divisor of `ptr`. 304 */ 305 @nogc nothrow pure 306 package size_t effectiveAlignment(void* ptr) 307 { 308 return (cast(size_t) 1) << trailingZeros(cast(size_t) ptr); 309 } 310 311 @nogc nothrow pure 312 @system unittest 313 { 314 int x; 315 assert(effectiveAlignment(&x) >= int.alignof); 316 317 const max = (cast(size_t) 1) << (size_t.sizeof * 8 - 1); 318 assert(effectiveAlignment(cast(void*) max) == max); 319 } 320 321 /* 322 Aligns a pointer down to a specified alignment. The resulting pointer is less 323 than or equal to the given pointer. 324 */ 325 @nogc nothrow pure 326 package void* alignDownTo(return scope void* ptr, uint alignment) 327 { 328 import std.math.traits : isPowerOf2; 329 assert(alignment.isPowerOf2); 330 return cast(void*) (cast(size_t) ptr & ~(alignment - 1UL)); 331 } 332 333 /* 334 Aligns a pointer up to a specified alignment. The resulting pointer is greater 335 than or equal to the given pointer. 336 */ 337 @nogc nothrow pure 338 package void* alignUpTo(return scope void* ptr, uint alignment) 339 { 340 import std.math.traits : isPowerOf2; 341 assert(alignment.isPowerOf2); 342 immutable uint slack = cast(size_t) ptr & (alignment - 1U); 343 return slack ? ptr + alignment - slack : ptr; 344 } 345 346 @safe @nogc nothrow pure 347 package bool isGoodStaticAlignment(uint x) 348 { 349 import std.math.traits : isPowerOf2; 350 return x.isPowerOf2; 351 } 352 353 @safe @nogc nothrow pure 354 package bool isGoodDynamicAlignment(uint x) 355 { 356 import std.math.traits : isPowerOf2; 357 return x.isPowerOf2 && x >= (void*).sizeof; 358 } 359 360 /** 361 The default `reallocate` function first attempts to use `expand`. If $(D 362 Allocator.expand) is not defined or returns `false`, `reallocate` 363 allocates a new block of memory of appropriate size and copies data from the old 364 block to the new block. Finally, if `Allocator` defines `deallocate`, $(D 365 reallocate) uses it to free the old memory block. 366 367 `reallocate` does not attempt to use `Allocator.reallocate` even if 368 defined. This is deliberate so allocators may use it internally within their own 369 implementation of `reallocate`. 370 371 */ 372 bool reallocate(Allocator)(ref Allocator a, ref void[] b, size_t s) 373 { 374 if (b.length == s) return true; 375 static if (hasMember!(Allocator, "expand")) 376 { 377 if (b.length <= s && a.expand(b, s - b.length)) return true; 378 } 379 auto newB = a.allocate(s); 380 if (newB.length != s) return false; 381 if (newB.length <= b.length) newB[] = b[0 .. newB.length]; 382 else newB[0 .. b.length] = b[]; 383 static if (hasMember!(Allocator, "deallocate")) 384 a.deallocate(b); 385 b = newB; 386 return true; 387 } 388 389 /** 390 391 The default `alignedReallocate` function first attempts to use `expand`. 392 If `Allocator.expand` is not defined or returns `false`, $(D 393 alignedReallocate) allocates a new block of memory of appropriate size and 394 copies data from the old block to the new block. Finally, if `Allocator` 395 defines `deallocate`, `alignedReallocate` uses it to free the old memory 396 block. 397 398 `alignedReallocate` does not attempt to use `Allocator.reallocate` even if 399 defined. This is deliberate so allocators may use it internally within their own 400 implementation of `reallocate`. 401 402 */ 403 bool alignedReallocate(Allocator)(ref Allocator alloc, 404 ref void[] b, size_t s, uint a) 405 if (hasMember!(Allocator, "alignedAllocate")) 406 { 407 static if (hasMember!(Allocator, "expand")) 408 { 409 if (b.length <= s && b.ptr.alignedAt(a) 410 && alloc.expand(b, s - b.length)) return true; 411 } 412 else 413 { 414 if (b.length == s && b.ptr.alignedAt(a)) return true; 415 } 416 auto newB = alloc.alignedAllocate(s, a); 417 if (newB.length != s) return false; 418 if (newB.length <= b.length) newB[] = b[0 .. newB.length]; 419 else newB[0 .. b.length] = b[]; 420 static if (hasMember!(Allocator, "deallocate")) 421 alloc.deallocate(b); 422 b = newB; 423 return true; 424 } 425 426 @system unittest 427 { 428 bool called = false; 429 struct DummyAllocator 430 { 431 void[] alignedAllocate(size_t size, uint alignment) 432 { 433 called = true; 434 return null; 435 } 436 } 437 438 struct DummyAllocatorExpand 439 { 440 void[] alignedAllocate(size_t size, uint alignment) 441 { 442 return null; 443 } 444 445 bool expand(ref void[] b, size_t length) 446 { 447 called = true; 448 return true; 449 } 450 } 451 452 char[128] buf; 453 uint alignment = 32; 454 auto alignedPtr = roundUpToMultipleOf(cast(size_t) buf.ptr, alignment); 455 auto diff = alignedPtr - cast(size_t) buf.ptr; 456 457 // Align the buffer to 'alignment' 458 void[] b = cast(void[]) (buf.ptr + diff)[0 .. buf.length - diff]; 459 460 DummyAllocator a1; 461 // Ask for same length and alignment, should not call 'alignedAllocate' 462 assert(alignedReallocate(a1, b, b.length, alignment)); 463 assert(!called); 464 465 // Ask for same length, different alignment 466 // should call 'alignedAllocate' if not aligned to new value 467 alignedReallocate(a1, b, b.length, alignment + 1); 468 assert(b.ptr.alignedAt(alignment + 1) || called); 469 called = false; 470 471 DummyAllocatorExpand a2; 472 // Ask for bigger length, same alignment, should call 'expand' 473 assert(alignedReallocate(a2, b, b.length + 1, alignment)); 474 assert(called); 475 called = false; 476 477 // Ask for bigger length, different alignment 478 // should call 'alignedAllocate' if not aligned to new value 479 alignedReallocate(a2, b, b.length + 1, alignment + 1); 480 assert(b.ptr.alignedAt(alignment + 1) || !called); 481 } 482 483 /** 484 Forwards each of the methods in `funs` (if defined) to `member`. 485 */ 486 /*package*/ string forwardToMember(string member, string[] funs...) 487 { 488 string result = " import std.traits : hasMember, Parameters;\n"; 489 foreach (fun; funs) 490 { 491 result ~= " 492 static if (hasMember!(typeof("~member~"), `"~fun~"`)) 493 auto ref "~fun~"(Parameters!(typeof("~member~"."~fun~")) args) 494 { 495 return "~member~"."~fun~"(args); 496 }\n"; 497 } 498 return result; 499 } 500 501 version (StdUnittest) 502 { 503 504 package void testAllocator(alias make)() 505 { 506 import std.conv : text; 507 import std.math.traits : isPowerOf2; 508 import std.stdio : writeln, stderr; 509 import std.typecons : Ternary; 510 alias A = typeof(make()); 511 scope(failure) stderr.writeln("testAllocator failed for ", A.stringof); 512 513 auto a = make(); 514 515 // Test alignment 516 static assert(A.alignment.isPowerOf2); 517 518 // Test goodAllocSize 519 assert(a.goodAllocSize(1) >= A.alignment, 520 text(a.goodAllocSize(1), " < ", A.alignment)); 521 assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(A.alignment)); 522 assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(A.alignment)); 523 524 // Test allocate 525 assert(a.allocate(0) is null); 526 527 auto b1 = a.allocate(1); 528 assert(b1.length == 1); 529 auto b2 = a.allocate(2); 530 assert(b2.length == 2); 531 assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); 532 533 // Test allocateZeroed 534 static if (hasMember!(A, "allocateZeroed")) 535 {{ 536 auto b3 = a.allocateZeroed(8); 537 if (b3 !is null) 538 { 539 assert(b3.length == 8); 540 foreach (e; cast(ubyte[]) b3) 541 assert(e == 0); 542 } 543 }} 544 545 // Test alignedAllocate 546 static if (hasMember!(A, "alignedAllocate")) 547 {{ 548 auto b3 = a.alignedAllocate(1, 256); 549 assert(b3.length <= 1); 550 assert(b3.ptr.alignedAt(256)); 551 assert(a.alignedReallocate(b3, 2, 512)); 552 assert(b3.ptr.alignedAt(512)); 553 static if (hasMember!(A, "alignedDeallocate")) 554 { 555 a.alignedDeallocate(b3); 556 } 557 }} 558 else 559 { 560 static assert(!hasMember!(A, "alignedDeallocate")); 561 // This seems to be a bug in the compiler: 562 //static assert(!hasMember!(A, "alignedReallocate"), A.stringof); 563 } 564 565 static if (hasMember!(A, "allocateAll")) 566 {{ 567 auto aa = make(); 568 if (aa.allocateAll().ptr) 569 { 570 // Can't get any more memory 571 assert(!aa.allocate(1).ptr); 572 } 573 auto ab = make(); 574 const b4 = ab.allocateAll(); 575 assert(b4.length); 576 // Can't get any more memory 577 assert(!ab.allocate(1).ptr); 578 }} 579 580 static if (hasMember!(A, "expand")) 581 {{ 582 assert(a.expand(b1, 0)); 583 auto len = b1.length; 584 if (a.expand(b1, 102)) 585 { 586 assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); 587 } 588 auto aa = make(); 589 void[] b5 = null; 590 assert(aa.expand(b5, 0)); 591 assert(b5 is null); 592 assert(!aa.expand(b5, 1)); 593 assert(b5.length == 0); 594 }} 595 596 void[] b6 = null; 597 assert(a.reallocate(b6, 0)); 598 assert(b6.length == 0); 599 assert(a.reallocate(b6, 1)); 600 assert(b6.length == 1, text(b6.length)); 601 assert(a.reallocate(b6, 2)); 602 assert(b6.length == 2); 603 604 // Test owns 605 static if (hasMember!(A, "owns")) 606 {{ 607 assert(a.owns(null) == Ternary.no); 608 assert(a.owns(b1) == Ternary.yes); 609 assert(a.owns(b2) == Ternary.yes); 610 assert(a.owns(b6) == Ternary.yes); 611 }} 612 613 static if (hasMember!(A, "resolveInternalPointer")) 614 {{ 615 void[] p; 616 assert(a.resolveInternalPointer(null, p) == Ternary.no); 617 Ternary r = a.resolveInternalPointer(b1.ptr, p); 618 assert(p.ptr is b1.ptr && p.length >= b1.length); 619 r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); 620 assert(p.ptr is b1.ptr && p.length >= b1.length); 621 r = a.resolveInternalPointer(b2.ptr, p); 622 assert(p.ptr is b2.ptr && p.length >= b2.length); 623 r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); 624 assert(p.ptr is b2.ptr && p.length >= b2.length); 625 r = a.resolveInternalPointer(b6.ptr, p); 626 assert(p.ptr is b6.ptr && p.length >= b6.length); 627 r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); 628 assert(p.ptr is b6.ptr && p.length >= b6.length); 629 static int[10] b7 = [ 1, 2, 3 ]; 630 assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); 631 assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); 632 assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); 633 int[3] b8 = [ 1, 2, 3 ]; 634 assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); 635 assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); 636 assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); 637 }} 638 } 639 640 package void testAllocatorObject(RCAllocInterface)(RCAllocInterface a) 641 { 642 // this used to be a template constraint, but moving it inside prevents 643 // unnecessary import of std.experimental.allocator 644 import std.experimental.allocator : RCIAllocator, RCISharedAllocator; 645 static assert(is(RCAllocInterface == RCIAllocator) 646 || is (RCAllocInterface == RCISharedAllocator)); 647 648 import std.conv : text; 649 import std.math.traits : isPowerOf2; 650 import std.stdio : writeln, stderr; 651 import std.typecons : Ternary; 652 scope(failure) stderr.writeln("testAllocatorObject failed for ", 653 RCAllocInterface.stringof); 654 655 assert(!a.isNull); 656 657 // Test alignment 658 assert(a.alignment.isPowerOf2); 659 660 // Test goodAllocSize 661 assert(a.goodAllocSize(1) >= a.alignment, 662 text(a.goodAllocSize(1), " < ", a.alignment)); 663 assert(a.goodAllocSize(11) >= 11.roundUpToMultipleOf(a.alignment)); 664 assert(a.goodAllocSize(111) >= 111.roundUpToMultipleOf(a.alignment)); 665 666 // Test empty 667 assert(a.empty != Ternary.no); 668 669 // Test allocate 670 assert(a.allocate(0) is null); 671 672 auto b1 = a.allocate(1); 673 assert(b1.length == 1); 674 auto b2 = a.allocate(2); 675 assert(b2.length == 2); 676 assert(b2.ptr + b2.length <= b1.ptr || b1.ptr + b1.length <= b2.ptr); 677 678 // Test alignedAllocate 679 { 680 // If not implemented it will return null, so those should pass 681 auto b3 = a.alignedAllocate(1, 256); 682 assert(b3.length <= 1); 683 assert(b3.ptr.alignedAt(256)); 684 if (a.alignedReallocate(b3, 1, 256)) 685 { 686 // If it is false, then the wrapped allocator did not implement 687 // this 688 assert(a.alignedReallocate(b3, 2, 512)); 689 assert(b3.ptr.alignedAt(512)); 690 } 691 } 692 693 // Test allocateAll 694 { 695 auto aa = a.allocateAll(); 696 if (aa.ptr) 697 { 698 // Can't get any more memory 699 assert(!a.allocate(1).ptr); 700 a.deallocate(aa); 701 } 702 const b4 = a.allocateAll(); 703 if (b4.ptr) 704 { 705 // Can't get any more memory 706 assert(!a.allocate(1).ptr); 707 } 708 } 709 710 // Test expand 711 { 712 assert(a.expand(b1, 0)); 713 auto len = b1.length; 714 if (a.expand(b1, 102)) 715 { 716 assert(b1.length == len + 102, text(b1.length, " != ", len + 102)); 717 } 718 } 719 720 void[] b6 = null; 721 assert(a.reallocate(b6, 0)); 722 assert(b6.length == 0); 723 assert(a.reallocate(b6, 1)); 724 assert(b6.length == 1, text(b6.length)); 725 assert(a.reallocate(b6, 2)); 726 assert(b6.length == 2); 727 728 // Test owns 729 { 730 if (a.owns(null) != Ternary.unknown) 731 { 732 assert(a.owns(null) == Ternary.no); 733 assert(a.owns(b1) == Ternary.yes); 734 assert(a.owns(b2) == Ternary.yes); 735 assert(a.owns(b6) == Ternary.yes); 736 } 737 } 738 739 // Test resolveInternalPointer 740 { 741 void[] p; 742 if (a.resolveInternalPointer(null, p) != Ternary.unknown) 743 { 744 assert(a.resolveInternalPointer(null, p) == Ternary.no); 745 Ternary r = a.resolveInternalPointer(b1.ptr, p); 746 assert(p.ptr is b1.ptr && p.length >= b1.length); 747 r = a.resolveInternalPointer(b1.ptr + b1.length / 2, p); 748 assert(p.ptr is b1.ptr && p.length >= b1.length); 749 r = a.resolveInternalPointer(b2.ptr, p); 750 assert(p.ptr is b2.ptr && p.length >= b2.length); 751 r = a.resolveInternalPointer(b2.ptr + b2.length / 2, p); 752 assert(p.ptr is b2.ptr && p.length >= b2.length); 753 r = a.resolveInternalPointer(b6.ptr, p); 754 assert(p.ptr is b6.ptr && p.length >= b6.length); 755 r = a.resolveInternalPointer(b6.ptr + b6.length / 2, p); 756 assert(p.ptr is b6.ptr && p.length >= b6.length); 757 static int[10] b7 = [ 1, 2, 3 ]; 758 assert(a.resolveInternalPointer(b7.ptr, p) == Ternary.no); 759 assert(a.resolveInternalPointer(b7.ptr + b7.length / 2, p) == Ternary.no); 760 assert(a.resolveInternalPointer(b7.ptr + b7.length, p) == Ternary.no); 761 int[3] b8 = [ 1, 2, 3 ]; 762 assert(a.resolveInternalPointer(b8.ptr, p) == Ternary.no); 763 assert(a.resolveInternalPointer(b8.ptr + b8.length / 2, p) == Ternary.no); 764 assert(a.resolveInternalPointer(b8.ptr + b8.length, p) == Ternary.no); 765 } 766 } 767 768 // Test deallocateAll 769 { 770 if (a.deallocateAll()) 771 { 772 if (a.empty != Ternary.unknown) 773 { 774 assert(a.empty == Ternary.yes); 775 } 776 } 777 } 778 } 779 }