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