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