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