]> gcc.gnu.org Git - gcc.git/blame - libphobos/src/std/experimental/allocator/building_blocks/scoped_allocator.d
d: Merge upstream dmd d579c467c1, phobos 88aa69b14.
[gcc.git] / libphobos / src / std / experimental / allocator / building_blocks / scoped_allocator.d
CommitLineData
5fee5ec3
IB
1// Written in the D programming language.
2/**
3Source: $(PHOBOSSRC std/experimental/allocator/building_blocks/scoped_allocator.d)
4*/
b4c522fa
IB
5module std.experimental.allocator.building_blocks.scoped_allocator;
6
7import std.experimental.allocator.common;
8
9/**
10
5fee5ec3
IB
11`ScopedAllocator` delegates all allocation requests to `ParentAllocator`.
12When destroyed, the `ScopedAllocator` object automatically calls $(D
b4c522fa
IB
13deallocate) for all memory allocated through its lifetime. (The $(D
14deallocateAll) function is also implemented with the same semantics.)
15
5fee5ec3
IB
16`deallocate` is also supported, which is where most implementation effort
17and overhead of `ScopedAllocator` go. If `deallocate` is not needed, a
18simpler design combining `AllocatorList` with `Region` is recommended.
b4c522fa
IB
19
20*/
21struct ScopedAllocator(ParentAllocator)
22{
5fee5ec3 23 static if (!stateSize!ParentAllocator)
b4c522fa 24 {
5fee5ec3
IB
25 // This test is available only for stateless allocators
26 version (StdUnittest)
27 @system unittest
28 {
29 testAllocator!(() => ScopedAllocator());
30 }
b4c522fa
IB
31 }
32
33 import std.experimental.allocator.building_blocks.affix_allocator
34 : AffixAllocator;
35 import std.traits : hasMember;
36 import std.typecons : Ternary;
37
38 private struct Node
39 {
40 Node* prev;
41 Node* next;
42 size_t length;
43 }
44
45 alias Allocator = AffixAllocator!(ParentAllocator, Node);
46
47 // state
48 /**
5fee5ec3
IB
49 If `ParentAllocator` is stateful, `parent` is a property giving access
50 to an `AffixAllocator!ParentAllocator`. Otherwise, `parent` is an alias for `AffixAllocator!ParentAllocator.instance`.
b4c522fa
IB
51 */
52 static if (stateSize!ParentAllocator)
53 {
54 Allocator parent;
55 }
56 else
57 {
58 alias parent = Allocator.instance;
59 }
60 private Node* root;
61
62 /**
5fee5ec3 63 `ScopedAllocator` is not copyable.
b4c522fa
IB
64 */
65 @disable this(this);
66
67 /**
5fee5ec3 68 `ScopedAllocator`'s destructor releases all memory allocated during its
b4c522fa
IB
69 lifetime.
70 */
71 ~this()
72 {
73 deallocateAll;
74 }
75
76 /// Alignment offered
77 enum alignment = Allocator.alignment;
78
79 /**
5fee5ec3 80 Forwards to `parent.goodAllocSize` (which accounts for the management
b4c522fa
IB
81 overhead).
82 */
83 size_t goodAllocSize(size_t n)
84 {
85 return parent.goodAllocSize(n);
86 }
87
5fee5ec3
IB
88 // Common code shared between allocate and allocateZeroed.
89 private enum _processAndReturnAllocateResult =
90 q{
91 if (!b.ptr) return b;
b4c522fa
IB
92 Node* toInsert = & parent.prefix(b);
93 toInsert.prev = null;
94 toInsert.next = root;
95 toInsert.length = n;
96 assert(!root || !root.prev);
97 if (root) root.prev = toInsert;
98 root = toInsert;
99 return b;
5fee5ec3
IB
100 };
101
102 /**
103 Allocates memory. For management it actually allocates extra memory from
104 the parent.
105 */
106 void[] allocate(size_t n)
107 {
108 auto b = parent.allocate(n);
109 mixin(_processAndReturnAllocateResult);
110 }
111
112 static if (hasMember!(Allocator, "allocateZeroed"))
113 package(std) void[] allocateZeroed()(size_t n)
114 {
115 auto b = parent.allocateZeroed(n);
116 mixin(_processAndReturnAllocateResult);
b4c522fa
IB
117 }
118
119 /**
120 Forwards to $(D parent.expand(b, delta)).
121 */
122 static if (hasMember!(Allocator, "expand"))
123 bool expand(ref void[] b, size_t delta)
124 {
125 auto result = parent.expand(b, delta);
5fee5ec3 126 if (result && b)
b4c522fa 127 {
5fee5ec3 128 () @trusted { parent.prefix(b).length = b.length; }();
b4c522fa
IB
129 }
130 return result;
131 }
132
133 /**
5fee5ec3 134 Reallocates `b` to new size `s`.
b4c522fa
IB
135 */
136 bool reallocate(ref void[] b, size_t s)
137 {
138 // Remove from list
139 if (b.ptr)
140 {
141 Node* n = & parent.prefix(b);
142 if (n.prev) n.prev.next = n.next;
143 else root = n.next;
144 if (n.next) n.next.prev = n.prev;
145 }
146 auto result = parent.reallocate(b, s);
147 // Add back to list
148 if (b.ptr)
149 {
150 Node* n = & parent.prefix(b);
151 n.prev = null;
152 n.next = root;
153 n.length = s;
154 if (root) root.prev = n;
155 root = n;
156 }
157 return result;
158 }
159
160 /**
5fee5ec3 161 Forwards to `parent.owns(b)`.
b4c522fa
IB
162 */
163 static if (hasMember!(Allocator, "owns"))
164 Ternary owns(void[] b)
165 {
166 return parent.owns(b);
167 }
168
169 /**
5fee5ec3 170 Deallocates `b`.
b4c522fa
IB
171 */
172 static if (hasMember!(Allocator, "deallocate"))
173 bool deallocate(void[] b)
174 {
175 // Remove from list
176 if (b.ptr)
177 {
178 Node* n = & parent.prefix(b);
179 if (n.prev) n.prev.next = n.next;
180 else root = n.next;
181 if (n.next) n.next.prev = n.prev;
182 }
183 return parent.deallocate(b);
184 }
185
186 /**
187 Deallocates all memory allocated.
188 */
189 bool deallocateAll()
190 {
191 bool result = true;
192 for (auto n = root; n; )
193 {
194 void* p = n + 1;
195 auto length = n.length;
196 n = n.next;
197 if (!parent.deallocate(p[0 .. length]))
198 result = false;
199 }
200 root = null;
201 return result;
202 }
203
204 /**
205 Returns `Ternary.yes` if this allocator is not responsible for any memory,
206 `Ternary.no` otherwise. (Never returns `Ternary.unknown`.)
207 */
5fee5ec3 208 pure nothrow @safe @nogc
b4c522fa
IB
209 Ternary empty() const
210 {
211 return Ternary(root is null);
212 }
213}
214
215///
216@system unittest
217{
218 import std.experimental.allocator.mallocator : Mallocator;
219 import std.typecons : Ternary;
220 ScopedAllocator!Mallocator alloc;
221 assert(alloc.empty == Ternary.yes);
222 const b = alloc.allocate(10);
223 assert(b.length == 10);
224 assert(alloc.empty == Ternary.no);
225}
226
5fee5ec3 227version (StdUnittest)
b4c522fa
IB
228@system unittest
229{
230 import std.experimental.allocator.gc_allocator : GCAllocator;
231 testAllocator!(() => ScopedAllocator!GCAllocator());
232}
233
234@system unittest // https://issues.dlang.org/show_bug.cgi?id=16046
235{
236 import std.exception;
237 import std.experimental.allocator;
238 import std.experimental.allocator.mallocator;
239 ScopedAllocator!Mallocator alloc;
240 auto foo = alloc.make!int(1).enforce;
241 auto bar = alloc.make!int(2).enforce;
242 alloc.dispose(foo);
243 alloc.dispose(bar); // segfault here
244}
5fee5ec3
IB
245
246@system unittest
247{
248 import std.experimental.allocator.gc_allocator : GCAllocator;
249 ScopedAllocator!GCAllocator a;
250
251 assert(__traits(compiles, (() nothrow @safe @nogc => a.goodAllocSize(0))()));
252
253 // Ensure deallocate inherits from parent allocators
254 auto b = a.allocate(42);
255 assert(b.length == 42);
256 () nothrow @nogc { a.deallocate(b); }();
257}
258
259// Test that deallocateAll infers from parent
260@system unittest
261{
c8dfa79c 262 import std.experimental.allocator.building_blocks.region : BorrowedRegion;
5fee5ec3 263
c8dfa79c
IB
264 ScopedAllocator!(BorrowedRegion!()) a;
265 a.parent.parent = BorrowedRegion!()(new ubyte[1024 * 64]);
5fee5ec3
IB
266 auto b = a.allocate(42);
267 assert(b.length == 42);
268 assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
269 assert(b.length == 64);
270 assert((() nothrow @nogc => a.reallocate(b, 100))());
271 assert(b.length == 100);
272 assert((() nothrow @nogc => a.deallocateAll())());
273}
274
275@system unittest
276{
277 import std.experimental.allocator.building_blocks.region : Region;
278 import std.experimental.allocator.mallocator : Mallocator;
279 import std.typecons : Ternary;
280
281 auto a = Region!(Mallocator)(1024 * 64);
282 auto b = a.allocate(42);
283 assert(b.length == 42);
284 assert((() pure nothrow @safe @nogc => a.expand(b, 22))());
285 assert(b.length == 64);
286 assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
287 assert((() nothrow @nogc => a.reallocate(b, 100))());
288 assert(b.length == 100);
289 assert((() pure nothrow @safe @nogc => a.owns(b))() == Ternary.yes);
290 assert((() pure nothrow @safe @nogc => a.owns(null))() == Ternary.no);
291}
292
293// Test empty
294@system unittest
295{
296 import std.experimental.allocator.mallocator : Mallocator;
297 import std.typecons : Ternary;
298 ScopedAllocator!Mallocator alloc;
299
300 assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.yes);
301 const b = alloc.allocate(10);
302 assert((() pure nothrow @safe @nogc => alloc.empty)() == Ternary.no);
303}
This page took 0.434857 seconds and 5 git commands to generate.