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 }
Suggestion Box / Bug Report