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