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