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