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