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