1 /**
2  * Reference-counted objects for handling large amounts of raw _data.
3  *
4  * Using the `Data` type will only place a small object in managed
5  * memory, keeping the actual bytes in unmanaged memory.
6  *
7  * A proxy class (`Memory`) is used to safely allow multiple
8  * references to the same block of unmanaged memory.
9  *
10  * When the `Memory` object is destroyed, the unmanaged memory is
11  * deallocated.
12  *
13  * This has the following advantage over using managed memory (regular
14  * D arrays):
15  *
16  * - Faster allocation and deallocation, since memory is requested from
17  *   the OS directly as whole pages.
18  *
19  * - Greatly reduced chance of memory leaks (on 32-bit platforms) due to
20  *   stray pointers.
21  *
22  * - Overall improved GC performance due to reduced size of managed heap.
23  *
24  * - Memory is immediately returned to the OS when no more references
25  *   remain.
26  *
27  * - Unlike D arrays, `Data` objects know their reference count,
28  *   enabling things like copy-on-write or safely casting away constness.
29  *
30  * License:
31  *   This Source Code Form is subject to the terms of
32  *   the Mozilla Public License, v. 2.0. If a copy of
33  *   the MPL was not distributed with this file, You
34  *   can obtain one at http://mozilla.org/MPL/2.0/.
35  *
36  * Authors:
37  *   Vladimir Panteleev <ae@cy.md>
38  */
39 
40 module ae.sys.data;
41 
42 import core.exception : OutOfMemoryError, onOutOfMemoryError;
43 
44 import std.algorithm.mutation : move;
45 import std.traits : hasIndirections, Unqual, isSomeChar;
46 
47 // https://issues.dlang.org/show_bug.cgi?id=23961
48 //import ae.utils.array : emptySlice, sliceIndex, bytes, fromBytes;
49 
50 debug(DATA) import core.stdc.stdio;
51 
52 version (ae_data_nogc)
53 	private enum useGC = false;
54 else
55 	private enum useGC = true;
56 
57 static if (useGC)
58 	import core.memory : GC;
59 
60 /**
61  * A reference to a reference-counted block of memory.
62  * Represents a slice of data, which may be backed by managed memory,
63  * unmanaged memory, memory-mapped files, etc.
64  *
65  * Params:
66  *  T = the element type. "void" has a special meaning in that memory
67  *      will not be default-initialized.
68  */
69 struct TData(T)
70 if (!hasIndirections!T)
71 {
72 private:
73 	// https://issues.dlang.org/show_bug.cgi?id=23961
74 	import ae.utils.array : emptySlice, sliceIndex, as, asBytes;
75 
76 	/// Wrapped data
77 	T[] data;
78 
79 	/// Reference to the memory of the actual data - may be null to
80 	/// indicate wrapped data in managed memory.
81 	/// Used to maintain the reference count to unmanaged data, and
82 	/// for in-place expands (for appends).
83 	Memory memory;
84 
85 	// --- Typed Memory construction helpers
86 
87 	// No pure due to https://issues.dlang.org/show_bug.cgi?id=23959
88 	static T[] allocateMemory(U)(out Memory memory, size_t initialSize, size_t capacity, scope void delegate(Unqual!U[] contents) /*pure*/ @safe nothrow @nogc fill)
89 	{
90 		if (capacity * T.sizeof < OSAllocator.pageSize)
91 			memory = unmanagedNew!CMemory(initialSize * T.sizeof, capacity * T.sizeof);
92 		else
93 			memory = unmanagedNew!OSMemory(initialSize * T.sizeof, capacity * T.sizeof);
94 		fill(cast(Unqual!U[])memory.contents);
95 		return cast(T[])memory.contents;
96 	}
97 
98 	static T[] allocateMemory(U)(out Memory memory, U[] initialData)
99 	{
100 		assert(initialData.length * U.sizeof % T.sizeof == 0, "Unaligned allocation size");
101 		auto initialSize = initialData.length * U.sizeof / T.sizeof;
102 		// @trusted to allow copying void[] to void[]
103 		return allocateMemory!U(memory, initialSize, initialSize, (contents) @trusted { contents[] = initialData[]; });
104 	}
105 
106 	static T[] allocateMemory(out Memory memory, size_t initialSize, size_t capacity)
107 	{
108 		return allocateMemory!T(memory, initialSize, capacity, (contents) {
109 			static if (!is(Unqual!T == void))
110 				contents[] = T.init;
111 		});
112 	}
113 
114 	// --- Concatenation / appending helpers
115 
116 	// No pure due to https://issues.dlang.org/show_bug.cgi?id=23959
117 	void reallocate(size_t size, size_t capacity, scope void delegate(Unqual!T[] contents) /*pure*/ @safe nothrow @nogc fill)
118 	{
119 		Memory newMemory;
120 		// @trusted to allow copying void[] to void[]
121 		auto newData = allocateMemory!T(newMemory, size, capacity, (contents) @trusted {
122 			contents[0 .. this.data.length] = this.data;
123 			fill(contents[this.data.length .. $]);
124 		});
125 
126 		clear();
127 		this.memory = newMemory;
128 		this.memory.referenceCount++;
129 		this.data = newData;
130 	}
131 
132 	// No pure due to https://issues.dlang.org/show_bug.cgi?id=23959
133 	void expand(size_t newSize, size_t newCapacity, scope void delegate(Unqual!T[] contents) /*pure*/ @safe nothrow @nogc fill)
134 	@trusted // Allow slicing data.ptr
135 	in
136 	{
137 		assert(length < newSize);
138 		assert(newSize <= newCapacity);
139 	}
140 	out
141 	{
142 		assert(length == newSize);
143 	}
144 	do
145 	{
146 		if (newCapacity <= capacity)
147 		{
148 			auto dataBytes = this.data.asBytes;
149 			auto pos = memory.contents.sliceIndex(dataBytes); // start position in memory data in bytes
150 			memory.setSize(pos + newSize * T.sizeof);
151 			auto oldSize = data.length;
152 			data = data.ptr[0..newSize];
153 			fill(cast(Unqual!T[])data[oldSize .. $]);
154 		}
155 		else
156 			reallocate(newSize, newCapacity, fill);
157 	}
158 
159 	// Maximum preallocation for append operations.
160 	enum maxPrealloc = 4*1024*1024; // must be power of 2
161 
162 	alias Appendable = const(Unqual!T)[];
163 	static assert(is(typeof((T[]).init ~ Appendable.init)));
164 
165 	static TData createConcatenation(Appendable left, Appendable right)
166 	{
167 		auto newSize = left.length + right.length;
168 		Memory newMemory;
169 		// @trusted to allow copying void[] to void[]
170 		allocateMemory!T(newMemory, newSize, newSize, (contents) @trusted {
171 			contents[0 .. left.length] = left[];
172 			contents[left.length .. $] = right[];
173 		});
174 		return TData(newMemory);
175 	}
176 
177 	TData concat(Appendable right)
178 	{
179 		if (right.length == 0)
180 			return this;
181 		return createConcatenation(this.data, right);
182 	}
183 
184 	TData prepend(Appendable left)
185 	{
186 		if (left.length == 0)
187 			return this;
188 		return createConcatenation(left, this.data);
189 	}
190 
191 	static size_t getPreallocSize(size_t length)
192 	{
193 		import ae.utils.math : isPowerOfTwo, nextPowerOfTwo;
194 		static assert(isPowerOfTwo(maxPrealloc));
195 
196 		if (length < maxPrealloc)
197 			return nextPowerOfTwo(length);
198 		else
199 			return ((length-1) | (maxPrealloc-1)) + 1;
200 	}
201 
202 	TData append(Appendable right)
203 	{
204 		if (right.length == 0)
205 			return this;
206 		size_t newLength = length + right.length;
207 		// @trusted to allow copying void[] to void[]
208 		expand(newLength, getPreallocSize(newLength), (contents) @trusted {
209 			contents[] = right[];
210 		});
211 		return this;
212 	}
213 
214 	// --- Conversion helpers
215 
216 	void assertUnique() const
217 	{
218 		assert(
219 			(data is null)
220 			||
221 			(memory && memory.referenceCount == 1)
222 		);
223 	}
224 
225 	void becomeUnique()
226 	out
227 	{
228 		assertUnique();
229 	}
230 	do
231 	{
232 		if (!memory) // null, empty, or GC-owned data
233 			this = TData(data);
234 		else
235 		if (memory.referenceCount > 1)
236 			this = TData(data);
237 	}
238 
239 	debug invariant
240 	{
241 		if (data.length == 0)
242 			assert(memory is null, "Zero-length Data pinning Memory");
243 		if (memory)
244 			assert(memory.referenceCount > 0, "Data referencing Memory with bad reference count");
245 	}
246 
247 public:
248 	// --- Lifetime - construction
249 
250 	// TODO: overload the constructor on scope/non-scope to detect when to reallocate?
251 	// https://issues.dlang.org/show_bug.cgi?id=23941
252 
253 	/**
254 	 * DWIM constructor for creating a new instance wrapping the given data.
255 	 *
256 	 * In the current implementation, `data` is always copied into the
257 	 * new instance, however past and future implementations may not
258 	 * guarantee this (`wrapGC`-like construction may be used
259 	 * opportunistically instead).
260 	 */
261 	this(U)(U[] data)
262 	if (is(typeof({ U[] u; T[] t = u.dup; })))
263 	{
264 		if (data is null)
265 			this.data = null;
266 		else
267 		if (data.length == 0)
268 			this.data = emptySlice!T;
269 		else
270 		// if (forceReallocation || GC.addrOf(data.ptr) is null)
271 		{
272 			// copy (emplace) into unmanaged memory
273 			this.data = allocateMemory(this.memory, data);
274 			this.memory.referenceCount++;
275 		}
276 		// else
277 		// {
278 		// 	// just save a reference
279 		// 	this.data = data;
280 		// }
281 
282 		assert(this.length * T.sizeof == data.length * U.sizeof);
283 	}
284 
285 	/// Create a new null-like instance.
286 	this(typeof(null) n)
287 	{
288 	}
289 
290 	deprecated this(U)(U[] data)
291 	if (is(T == ubyte) &&
292 		!is(typeof({ U[] u; T[] t = u.dup; })) &&
293 		is(typeof({ U[] u; void[] t = u.dup; })))
294 	{
295 		const(void)[] v = data;
296 		this(cast(const(ubyte)[])v);
297 	}
298 
299 	/// Create a new instance with given size/capacity. Capacity defaults to size.
300 	this(size_t size, size_t capacity = 0)
301 	in
302 	{
303 		assert(capacity == 0 || size <= capacity);
304 	}
305 	do
306 	{
307 		if (!capacity)
308 			capacity = size;
309 
310 		if (capacity)
311 		{
312 			this.data = allocateMemory(this.memory, size, capacity);
313 			this.memory.referenceCount++;
314 		}
315 		else
316 		{
317 			memory = null;
318 			this.data = null;
319 		}
320 
321 		assert(this.length == size);
322 	}
323 
324 	/// Allow assigning null to clear.
325 	void opAssign(typeof(null))
326 	{
327 		clear();
328 	}
329 
330 	/// Create a new instance which slices some range of managed (GC-owned) memory.
331 	/// Does not copy the data.
332 	static if (useGC)
333 	static TData wrapGC(T[] data)
334 	{
335 		assert(data.length == 0 || GC.addrOf(data.ptr) !is null, "wrapGC data must be GC-owned");
336 		TData result;
337 		result.data = data;
338 		return result;
339 	}
340 
341 	/// Create a new instance slicing all of the given memory's contents.
342 	this(Memory memory)
343 	{
344 		this.memory = memory;
345 		this.memory.referenceCount++;
346 		this.data = cast(T[])memory.contents;
347 	}
348 
349 	this(this) @safe
350 	{
351 		if (memory)
352 		{
353 			memory.referenceCount++;
354 			debug (DATA_REFCOUNT) debugLog("%p -> %p: Incrementing refcount to %d", cast(void*)&this, cast(void*)memory, memory.referenceCount);
355 		}
356 		else
357 			debug (DATA_REFCOUNT) debugLog("%p -> %p: this(this) with no memory", cast(void*)&this, cast(void*)memory);
358 	}
359 
360 	// --- Lifetime - destruction
361 
362 	// No pure due to https://issues.dlang.org/show_bug.cgi?id=23959
363 	~this() /*pure*/ @trusted nothrow @nogc
364 	{
365 		clear();
366 		// https://issues.dlang.org/show_bug.cgi?id=13809
367 		// (cast(void delegate() pure nothrow @nogc)&clear)();
368 	}
369 
370 	/// Unreference contents, freeing it if this was the last reference.
371 	void clear() nothrow @nogc
372 	{
373 		if (memory)
374 		{
375 			assert(memory.referenceCount > 0, "Dangling pointer to Memory");
376 			memory.referenceCount--;
377 			debug (DATA_REFCOUNT) debugLog("%p -> %p: Decrementing refcount to %d", cast(void*)&this, cast(void*)memory, memory.referenceCount);
378 			if (memory.referenceCount == 0)
379 				unmanagedDelete(memory);
380 
381 			memory = null;
382 		}
383 
384 		this.data = null;
385 	}
386 
387 	// This used to be an unsafe method which deleted the wrapped data.
388 	// Now that Data is refcounted, this simply calls clear() and
389 	// additionally asserts that this Data is the only Data holding
390 	// a reference to the memory.
391 	deprecated void deleteContents()
392 	out
393 	{
394 		assert(memory is null);
395 	}
396 	do
397 	{
398 		if (memory)
399 		{
400 			assert(memory.referenceCount == 1, "Attempting to call deleteContents with more than one reference");
401 			clear();
402 		}
403 	}
404 
405 	// --- Lifetime - conversion
406 
407 	/// Returns an instance with the same data as the current instance, and a reference count of 1.
408 	/// The current instance is cleared.
409 	/// If the current instance already has a reference count of 1, no copying is done.
410 	TData ensureUnique()
411 	{
412 		becomeUnique();
413 		return move(this);
414 	}
415 
416 	/// Soft (memory-safe) cast:
417 	/// Cast contents to another type, and returns an instance with that contents.
418 	/// Constness is preserved. No copying is done.
419 	auto asDataOf(U)()
420 	if (is(typeof(this.data.asBytes.as!(U[])) == U[]))
421 	{
422 		TData!U result;
423 		result.data = this.data.asBytes.as!(U[]);
424 		result.memory = this.memory;
425 		if (result.memory)
426 			result.memory.referenceCount++;
427 		return result;
428 	}
429 
430 	/// Hard (normally memory-unsafe) cast:
431 	/// Cast contents to another type, and returns an instance with that contents.
432 	/// The current instance is cleared. U may have an incompatible constness.
433 	/// To enforce memory safety, the current instance must be the only one
434 	/// holding a reference to the data (call `ensureUnique` first).
435 	/// No copying is done.
436 	TData!U castTo(U)()
437 	if (!hasIndirections!U)
438 	{
439 		assertUnique();
440 
441 		TData!U result;
442 		result.data = cast(U[])data;
443 		result.memory = this.memory;
444 		this.data = null;
445 		this.memory = null;
446 		return result;
447 	}
448 
449 	// --- Contents access
450 
451 	private enum enterImpl = q{
452 		// We must make a copy of ourselves to ensure that, should
453 		// `fn` overwrite the `this` instance, the passed contents
454 		// slice remains valid.
455 		auto self = this;
456 		scope data = self.data; // Add `scope` attribute
457 		return fn(data);
458 	};
459 
460 	/// Get temporary access to the data referenced by this Data instance.
461 	// This non-templated overload set exists to allow
462 	// lambda functions (anonymous function templates).
463 	void enter(scope void delegate(scope T[])                          fn)                          { mixin(enterImpl); }
464 	void enter(scope void delegate(scope T[]) @safe                    fn) @safe                    { mixin(enterImpl); } /// ditto
465 	// No pure due to https://issues.dlang.org/show_bug.cgi?id=23959
466 	// void enter(scope void delegate(scope T[])       pure               fn)       pure               { mixin(enterImpl); } /// ditto
467 	// void enter(scope void delegate(scope T[]) @safe pure               fn) @safe pure               { mixin(enterImpl); } /// ditto
468 	void enter(scope void delegate(scope T[])            nothrow       fn)            nothrow       { mixin(enterImpl); } /// ditto
469 	void enter(scope void delegate(scope T[]) @safe      nothrow       fn) @safe      nothrow       { mixin(enterImpl); } /// ditto
470 	// void enter(scope void delegate(scope T[])       pure nothrow       fn)       pure nothrow       { mixin(enterImpl); } /// ditto
471 	// void enter(scope void delegate(scope T[]) @safe pure nothrow       fn) @safe pure nothrow       { mixin(enterImpl); } /// ditto
472 	void enter(scope void delegate(scope T[])                    @nogc fn)                    @nogc { mixin(enterImpl); } /// ditto
473 	void enter(scope void delegate(scope T[]) @safe              @nogc fn) @safe              @nogc { mixin(enterImpl); } /// ditto
474 	// void enter(scope void delegate(scope T[])       pure         @nogc fn)       pure         @nogc { mixin(enterImpl); } /// ditto
475 	// void enter(scope void delegate(scope T[]) @safe pure         @nogc fn) @safe pure         @nogc { mixin(enterImpl); } /// ditto
476 	void enter(scope void delegate(scope T[])            nothrow @nogc fn)            nothrow @nogc { mixin(enterImpl); } /// ditto
477 	void enter(scope void delegate(scope T[]) @safe      nothrow @nogc fn) @safe      nothrow @nogc { mixin(enterImpl); } /// ditto
478 	// void enter(scope void delegate(scope T[])       pure nothrow @nogc fn)       pure nothrow @nogc { mixin(enterImpl); } /// ditto
479 	// void enter(scope void delegate(scope T[]) @safe pure nothrow @nogc fn) @safe pure nothrow @nogc { mixin(enterImpl); } /// ditto
480 
481 	// https://issues.dlang.org/show_bug.cgi?id=23956
482 	// void enter(scope void delegate(scope const(T)[])                          fn) const                          { mixin(enterImpl); }
483 	// void enter(scope void delegate(scope const(T)[]) @safe                    fn) const @safe                    { mixin(enterImpl); }
484 	// void enter(scope void delegate(scope const(T)[])       pure               fn) const       pure               { mixin(enterImpl); }
485 	// void enter(scope void delegate(scope const(T)[]) @safe pure               fn) const @safe pure               { mixin(enterImpl); }
486 	// void enter(scope void delegate(scope const(T)[])            nothrow       fn) const            nothrow       { mixin(enterImpl); }
487 	// void enter(scope void delegate(scope const(T)[]) @safe      nothrow       fn) const @safe      nothrow       { mixin(enterImpl); }
488 	// void enter(scope void delegate(scope const(T)[])       pure nothrow       fn) const       pure nothrow       { mixin(enterImpl); }
489 	// void enter(scope void delegate(scope const(T)[]) @safe pure nothrow       fn) const @safe pure nothrow       { mixin(enterImpl); }
490 	// void enter(scope void delegate(scope const(T)[])                    @nogc fn) const                    @nogc { mixin(enterImpl); }
491 	// void enter(scope void delegate(scope const(T)[]) @safe              @nogc fn) const @safe              @nogc { mixin(enterImpl); }
492 	// void enter(scope void delegate(scope const(T)[])       pure         @nogc fn) const       pure         @nogc { mixin(enterImpl); }
493 	// void enter(scope void delegate(scope const(T)[]) @safe pure         @nogc fn) const @safe pure         @nogc { mixin(enterImpl); }
494 	// void enter(scope void delegate(scope const(T)[])            nothrow @nogc fn) const            nothrow @nogc { mixin(enterImpl); }
495 	// void enter(scope void delegate(scope const(T)[]) @safe      nothrow @nogc fn) const @safe      nothrow @nogc { mixin(enterImpl); }
496 	// void enter(scope void delegate(scope const(T)[])       pure nothrow @nogc fn) const       pure nothrow @nogc { mixin(enterImpl); }
497 	// void enter(scope void delegate(scope const(T)[]) @safe pure nothrow @nogc fn) const @safe pure nothrow @nogc { mixin(enterImpl); }
498 
499 	// For everything else, there is a template overload.
500 	// Note: Dg is a IFTI-inferred parameter due to
501 	// https://issues.dlang.org/show_bug.cgi?id=23955
502 	auto enter(this This, Dg)(scope Dg fn) { mixin(enterImpl); }
503 
504 	/// Put a copy of the data on D's managed heap, and return it.
505 	T[] toGC() const
506 	{
507 		return data.dup;
508 	}
509 
510 	// deprecated alias toHeap = toGC;
511 	// https://issues.dlang.org/show_bug.cgi?id=23954
512 	deprecated T[] toHeap() const { return toGC(); }
513 
514 	/**
515 	   Get the referenced data. Unsafe!
516 
517 	   All operations on the returned contents must be accompanied by
518 	   a live reference to the `Data` object, in order to keep a
519 	   reference towards the Memory owning the contents.
520 
521 	   Be sure not to lose `Data` references while using their contents!
522 	   For example, avoid code like this:
523 	   ----
524 	   getSomeData()        // returns Data
525 		   .unsafeContents  // returns ubyte[]
526 		   .useContents();  // uses the ubyte[] ... while there is no Data to reference it
527 	   ----
528 	   The `Data` return value may be unreachable once `.unsafeContents` is evaluated.
529 	   Use `.toGC` instead of `.unsafeContents` in such cases to safely get a GC-owned copy,
530 	   or use `.enter(contents => ...)` to safely get a temporary reference.
531 	*/
532 	@property inout(T)[] unsafeContents() inout @system { return this.data; }
533 
534 	// deprecated alias contents = unsafeContents;
535 	// https://issues.dlang.org/show_bug.cgi?id=23954
536 	deprecated @property inout(T)[] contents() inout @system { return this.data; }
537 
538 	deprecated @property Unqual!T[] mcontents() @system
539 	{
540 		becomeUnique();
541 		return cast(Unqual!T[])data;
542 	}
543 
544 	// --- Array-like operations
545 
546 	/// 
547 	@property size_t length() const
548 	{
549 		return data.length;
550 	}
551 	alias opDollar = length; /// ditto
552 
553 	deprecated @property inout(T)* ptr() inout { return unsafeContents.ptr; }
554 
555 	deprecated @property Unqual!T* mptr() @system { return mcontents.ptr; }
556 
557 	bool opCast(T)() const
558 		if (is(T == bool))
559 	{
560 		return data !is null;
561 	} ///
562 
563 	/// Return the maximum value that can be set to `length` without causing a reallocation
564 	@property size_t capacity() const
565 	{
566 		if (memory is null)
567 			return length;
568 		// We can only safely expand if the memory slice is at the end of the used unmanaged memory block,
569 		// or, if we are the only reference.
570 		auto dataBytes = this.data.asBytes;
571 		auto pos = memory.contents.sliceIndex(dataBytes); // start position in memory data in bytes
572 		auto end = pos + dataBytes.length;                // end   position in memory data in bytes
573 		assert(end <= memory.size);
574 		if ((end == memory.size || memory.referenceCount == 1) && end < memory.capacity)
575 			return (memory.capacity - pos) / T.sizeof; // integer division truncating towards zero
576 		else
577 			return length;
578 	}
579 
580 	/// Resize contents
581 	@property void length(size_t newLength)
582 	{
583 		if (newLength == length) // no change
584 			return;
585 		if (newLength < length)  // shorten
586 		{
587 			if (!newLength)
588 				this = TData(emptySlice!T);
589 			else
590 				data = data[0..newLength];
591 		}
592 		else                 // lengthen
593 			expand(newLength, newLength, (contents) {
594 				static if (!is(Unqual!T == void))
595 					contents[] = T.init;
596 			});
597 	}
598 
599 	/// Create a copy of the data
600 	@property This dup(this This)()
601 	{
602 		return This(this.data);
603 	}
604 
605 	/// Create a new `Data` containing the concatenation of `this` and `data`.
606 	/// Does not preallocate for successive appends.
607 	template opBinary(string op) if (op == "~")
608 	{
609 		TData opBinary(Appendable data)
610 		{
611 			return concat(data);
612 		} ///
613 
614 		TData opBinary(TData data)
615 		{
616 			return concat(data.data);
617 		} ///
618 
619 		static if (!is(Unqual!T == void))
620 		TData opBinary(T value)
621 		{
622 			return concat((&value)[0..1]);
623 		} ///
624 
625 		static if (is(T == ubyte))
626 		deprecated TData opBinary(const(void)[] data)
627 		{
628 			return concat(cast(Appendable)data);
629 		}
630 	}
631 
632 	/// Create a new `Data` containing the concatenation of `data` and `this`.
633 	/// Does not preallocate for successive appends.
634 	template opBinaryRight(string op) if (op == "~")
635 	{
636 		TData opBinaryRight(Appendable data)
637 		{
638 			return prepend(data);
639 		} ///
640 
641 		static if (!is(Unqual!T == void))
642 		TData opBinaryRight(T value)
643 		{
644 			return prepend((&value)[0..1]);
645 		} ///
646 
647 		static if (is(T == ubyte))
648 		deprecated TData opBinaryRight(const(void)[] data)
649 		{
650 			return prepend(cast(Appendable)data);
651 		}
652 	}
653 
654 	/// Append data to this `Data`.
655 	/// Unlike concatenation (`a ~ b`), appending (`a ~= b`) will preallocate.
656 	template opOpAssign(string op) if (op == "~")
657 	{
658 		TData opOpAssign(Appendable data)
659 		{
660 			return append(data);
661 		} ///
662 
663 		TData opOpAssign(TData data)
664 		{
665 			return append(data.data);
666 		} ///
667 
668 		static if (!is(Unqual!T == void))
669 		TData opOpAssign(T value)
670 		{
671 			return append((&value)[0..1]);
672 		} ///
673 
674 		static if (is(T == ubyte))
675 		deprecated TData opOpAssign(const(void)[] data)
676 		{
677 			return append(cast(Appendable)data);
678 		}
679 	}
680 
681 	/// Access an individual item.
682 	static if (!is(Unqual!T == void))
683 	T opIndex(size_t index)
684 	{
685 		return data[index];
686 	}
687 
688 	/// Write an individual item.
689 	static if (is(typeof(data[0] = T.init)))
690 	T opIndexAssign(T value, size_t index)
691 	{
692 		return data[index] = value;
693 	}
694 
695 	/// Returns a `Data` pointing at a slice of this `Data`'s contents.
696 	TData opSlice()
697 	{
698 		return this;
699 	}
700 
701 	/// ditto
702 	TData opSlice(size_t x, size_t y)
703 	in
704 	{
705 		assert(x <= y);
706 		assert(y <= length);
707 	}
708 	out(result)
709 	{
710 		assert(result.length == y-x);
711 	}
712 	do
713 	{
714 		if (x == y)
715 			return TData(emptySlice!T);
716 		else
717 		{
718 			TData result = this;
719 			result.data = result.data[x .. y];
720 			return result;
721 		}
722 	}
723 
724 	package(ae) // TODO: is this a good API?
725 	sizediff_t indexOf(const(T)[] needle) const
726 	{
727 		static if (is(typeof(imported!q{ae.utils.array}.indexOf(this.data, needle))))
728 			return imported!q{ae.utils.array}.indexOf(this.data, needle);
729 		else
730 		static if (is(typeof(imported!q{std..string}.indexOf(this.data, needle))))
731 			return imported!q{std..string}.indexOf(this.data, needle);
732 		else
733 		{
734 			if (this.data.length >= needle.length)
735 				foreach (i; 0 .. this.data.length - needle.length + 1)
736 					if (this.data[i .. i + needle.length] == needle)
737 						return i;
738 			return -1;
739 		}
740 	}
741 
742 	bool opEquals(ref TData other) const { return data == other.data; }
743 	bool opEquals(TData other) const { return data == other.data; }
744 	bool opEquals(scope T[] other) const { return data == other; }
745 
746 	package(ae) // ditto
747 	static if (is(T == ubyte))
748 	deprecated sizediff_t indexOf(const(void)[] needle) const
749 	{
750 		return indexOf(cast(const(ubyte)[])needle);
751 	}
752 
753 	// --- Range operations
754 
755 	/// Range primitive.
756 	@property bool empty() const { return length == 0; }
757 	static if (!is(Unqual!T == void))
758 	T front() { return data[0]; } ///
759 	void popFront() { this = this[1 .. $]; } ///
760 
761 	// /// Return a new `Data` for the first `size` bytes, and slice this instance from size to end.
762 	// Data popFront(size_t size)
763 	// in
764 	// {
765 	// 	assert(size <= length);
766 	// }
767 	// do
768 	// {
769 	// 	Data result = this;
770 	// 	result.contents = contents[0..size];
771 	// 	this  .contents = contents[size..$];
772 	// 	return result;
773 	// }
774 }
775 
776 debug(ae_unittest) unittest
777 {
778 	import core.exception : AssertError;
779 	import core.memory : GC;
780 	import std.exception : assertThrown;
781 	import ae.utils.array : emptySlice;
782 
783 	alias AliasSeq(TList...) = TList;
784 	foreach (B; AliasSeq!(ubyte, uint, char, void))
785 	{
786 		alias Ts = AliasSeq!(B, const(B), immutable(B));
787 		foreach (T; Ts)
788 		{
789 			// Template instantiation
790 			{
791 				TData!T d;
792 				cast(void) d;
793 			}
794 			// .enter type
795 			{
796 				TData!T d;
797 				d.enter((scope contents) { T[] _ = contents; });
798 			}
799 			// .enter with functors
800 			{
801 				import ae.utils.functor.primitives : functor;
802 				TData!T d;
803 				d.enter(functor!((contents) {
804 					assert(contents == d.unsafeContents);
805 				}));
806 			}
807 			// // .enter with const
808 			// {
809 			// 	const TData!T d;
810 			// 	d.enter((scope contents) { const T[] _ = contents; });
811 			// }
812 			// .enter with return value
813 			{
814 				TData!T d;
815 				auto l = d.enter((T[] contents) => contents.length);
816 				assert(l == d.length);
817 			}
818 			// Construction from typeof(null)
819 			{
820 				auto d = TData!T(null);
821 				assert(d.length == 0);
822 				assert(d.unsafeContents.ptr is null);
823 			}
824 			// Construction from null slice
825 			{
826 				T[] arr = null;
827 				auto d = TData!T(arr);
828 				assert(d.length == 0);
829 				assert(d.unsafeContents.ptr is null);
830 			}
831 			// Construction from non-null empty
832 			{
833 				auto d = TData!T(emptySlice!T());
834 				assert(d.length == 0);
835 				assert(d.unsafeContents.ptr !is null);
836 			}
837 			// Construction from non-empty non-GC slice
838 			{
839 				T[5] arr = void;
840 				assert(GC.addrOf(arr.ptr) is null);
841 				auto d = TData!T(arr[]);
842 				assert(d.length == 5);
843 				assert(d.unsafeContents.ptr !is null);
844 				assert(GC.addrOf(d.unsafeContents.ptr) is null);
845 			}
846 			// Construction from non-empty GC slice
847 			{
848 				T[] arr = new T[5];
849 				assert(GC.addrOf(arr.ptr) !is null);
850 				auto d = TData!T(arr);
851 				assert(d.length == 5);
852 				assert(d.unsafeContents.ptr !is null);
853 				assert(GC.addrOf(d.unsafeContents.ptr) is null);
854 			}
855 			// wrapGC from GC slice
856 			static if (useGC)
857 			{{
858 				T[] arr = new T[5];
859 				auto d = TData!T.wrapGC(arr);
860 				assert(d.length == 5);
861 				assert(d.unsafeContents.ptr is arr.ptr);
862 			}}
863 			// wrapGC from non-GC slice
864 			static if (useGC)
865 			{{
866 				static T[5] arr = void;
867 				assertThrown!AssertError(TData!T.wrapGC(arr[]));
868 			}}
869 			// .capacity
870 			{
871 				T[] arr = new T[5];
872 				auto d = TData!T(arr);
873 				assert(d.capacity >= 5);
874 				auto d2 = d[0 .. 3];
875 				assert(d2.capacity == 3);
876 				d = null;
877 				assert(d2.capacity >= 5); // Sole reference; safe to expand over old data
878 			}
879 
880 			// Try a bunch of operations with different kinds of instances
881 			static T[5] arr = void;
882 			TData!T delegate()[] generators = [
883 				delegate () => TData!T(),
884 				delegate () => TData!T(null),
885 				delegate () => TData!T(T[].init),
886 				delegate () => TData!T(arr[]),
887 				delegate () => TData!T(arr[0 .. 0]),
888 				delegate () => TData!T(arr[].dup),
889 				delegate () => TData!T(arr[].dup[0 .. 0]),
890 			];
891 			static if (useGC)
892 				generators ~= [
893 					delegate () => TData!T.wrapGC(arr[].dup),
894 					delegate () => TData!T.wrapGC(arr[].dup[0 .. 0]),
895 				];
896 			static if (is(B == void))
897 				foreach (B2; AliasSeq!(ubyte, uint, char, void))
898 					foreach (T2; AliasSeq!(B2, const(B2), immutable(B2)))
899 						static if (is(typeof({ T2[] u; T[] t = u; })))
900 						{
901 							static T2[5] arr2 = void;
902 							generators ~= [
903 								delegate () => TData!T(T2[].init),
904 								delegate () => TData!T(arr2[]),
905 								delegate () => TData!T(arr2[0 .. 0]),
906 								delegate () => TData!T(arr2[].dup),
907 								delegate () => TData!T(arr2[].dup[0 .. 0]),
908 							];
909 							static if (useGC)
910 								generators ~= [
911 									delegate () => TData!T.wrapGC(arr2[].dup),
912 								//	delegate () => TData!T.wrapGC(arr2[].dup[0 .. 0]), // TODO: why not?
913 								];
914 						}
915 			foreach (generator; generators)
916 			{
917 				// General coherency
918 				{
919 					auto d = generator();
920 					auto length = d.length;
921 					auto contents = d.unsafeContents;
922 					assert(contents.length == length);
923 					size_t entered;
924 					d.enter((enteredContents) {
925 						assert(enteredContents is contents);
926 						entered++;
927 					});
928 					assert(entered == 1);
929 					assert(d.dup.unsafeContents == contents);
930 				}
931 				// Lifetime with .enter
932 				{
933 					auto d = generator();
934 					d.enter((contents) {
935 						d = typeof(d)(null);
936 						(cast(ubyte[])contents)[] = 42;
937 					});
938 				}
939 				// toGC
940 				{
941 					auto d = generator();
942 					auto contents = d.unsafeContents;
943 					assert(d.toGC() == contents);
944 				}
945 				// In-place expansion (resize to capacity)
946 				{
947 					auto d = generator();
948 					auto oldContents = d.unsafeContents;
949 					d.length = d.capacity;
950 					assert(d.unsafeContents.ptr == oldContents.ptr);
951 				}
952 				// Copying expansion (resize past capacity)
953 				{
954 					auto d = generator();
955 					auto oldContents = d.unsafeContents;
956 					d.length = d.capacity + 1;
957 					assert(d.unsafeContents.ptr != oldContents.ptr);
958 				}
959 				// Equality
960 				{
961 					auto d1 = generator();
962 					auto d2 = generator();
963 					assert(d1 == d2);
964 				}
965 				// Concatenation
966 				{
967 					void test(L, R)(L left, R right)
968 					{
969 						{
970 							auto result = left ~ right;
971 							assert(result.length == left.length + right.length);
972 							// TODO: test contents, need opEquals?
973 						}
974 						static if (!is(Unqual!T == void))
975 						{
976 							if (left.length)
977 							{
978 								auto result = left[0] ~ right;
979 								assert(result.length == 1 + right.length);
980 							}
981 							if (right.length)
982 							{
983 								auto result = left ~ right[0];
984 								assert(result.length == left.length + 1);
985 							}
986 						}
987 					}
988 
989 					foreach (generator2; generators)
990 					{
991 						test(generator(), generator2());
992 						test(generator().toGC, generator2());
993 						test(generator(), generator2().toGC);
994 					}
995 				}
996 				// Appending
997 				{
998 					void test(L, R)(L left, R right)
999 					{
1000 						{
1001 							auto result = left;
1002 							result ~= right;
1003 							assert(result.length == left.length + right.length);
1004 							// TODO: test contents, need opEquals?
1005 						}
1006 						static if (!is(Unqual!T == void))
1007 						{
1008 							if (right.length)
1009 							{
1010 								auto result = left;
1011 								result ~= right[0];
1012 								assert(result.length == left.length + 1);
1013 							}
1014 						}
1015 					}
1016 
1017 					foreach (generator2; generators)
1018 					{
1019 						test(generator(), generator2());
1020 						// test(generator().toGC, generator2());
1021 						test(generator(), generator2().toGC);
1022 					}
1023 				}
1024 				// Slicing to zero
1025 				{
1026 					auto l = generator().length;
1027 					foreach (n; [0, l/2, l])
1028 					{
1029 						auto d = generator();
1030 						d = d[n .. n];
1031 						assert(d == d);
1032 					}
1033 				}
1034 				// Shrinking to zero
1035 				{
1036 					auto d = generator();
1037 					d.length = 0;
1038 					assert(d == d);
1039 				}
1040 				// Reference count
1041 				{
1042 					auto d = generator();
1043 					if (d.memory)
1044 					{
1045 						assert(d.memory.referenceCount == 1);
1046 						{
1047 							auto s = d[1..4];
1048 							assert(d.memory.referenceCount == 2);
1049 							cast(void) s;
1050 						}
1051 						assert(d.memory.referenceCount == 1);
1052 					}
1053 				}
1054 			}
1055 
1056 			// Test effects of construction from various sources
1057 			{
1058 				void testSource(S)(S s)
1059 				{
1060 					void testData(TData!T d)
1061 					{
1062 						// Test true-ish-ness
1063 						assert(!! s == !! d);
1064 						// Test length
1065 						static if (is(typeof(s.length)))
1066 							assert(s.length * s[0].sizeof == d.length * T.sizeof);
1067 						// Test content
1068 						assert(s == d.unsafeContents);
1069 					}
1070 					// Construction
1071 					testData(TData!T(s));
1072 					// wrapGC
1073 					static if (useGC)
1074 						static if (is(typeof(*s.ptr) == T))
1075 							if (GC.addrOf(s.ptr))
1076 								testData(TData!T.wrapGC(s));
1077 					// Appending
1078 					{
1079 						TData!T d;
1080 						d ~= s;
1081 					}
1082 				}
1083 				testSource(null);
1084 				testSource(T[].init);
1085 				testSource(arr[]);
1086 				testSource(arr[0 .. 0]);
1087 				testSource(arr[].dup);
1088 				testSource(arr[].dup[0 .. 0]);
1089 				testSource(arr[].idup);
1090 				testSource(arr[].idup[0 .. 0]);
1091 				static if (is(B == void))
1092 					foreach (B2; AliasSeq!(ubyte, uint, char, void))
1093 						foreach (T2; AliasSeq!(B2, const(B2), immutable(B2)))
1094 							static if (is(typeof({ T2[] u; T[] t = u; })))
1095 							{
1096 								static T2[5] arr2 = void;
1097 								testSource(T2[].init);
1098 								testSource(arr2[]);
1099 								testSource(arr2[0 .. 0]);
1100 								testSource(arr2[].dup);
1101 								testSource(arr2[].dup[0 .. 0]);
1102 							}
1103 			}
1104 
1105 			foreach (U; Ts)
1106 			{
1107 				// Construction from compatible slice
1108 				{
1109 					U[] u;
1110 					TData!T(u);
1111 					static if (useGC)
1112 						static if (is(typeof({ T[] t = u; })))
1113 							cast(void) TData!T.wrapGC(u);
1114 				}
1115 			}
1116 		}
1117 	}
1118 }
1119 
1120 // pure/@safe/nothrow/@nogc compilation test
1121 // No pure due to https://issues.dlang.org/show_bug.cgi?id=23959
1122 debug(ae_unittest) /*pure*/ @safe nothrow @nogc unittest
1123 {
1124 	TData!ubyte d;
1125 	d.enter((scope contents) { ubyte[] _ = contents; });
1126 	d = TData!ubyte(null);
1127 	assert(d.length == 0);
1128 
1129 	ubyte[] arr1 = null;
1130 	d = TData!ubyte(arr1);
1131 
1132 	ubyte[0] arr2;
1133 	d = TData!ubyte(arr2[]);
1134 
1135 	ubyte[5] arr3 = void;
1136 	d = TData!ubyte(arr3[]);
1137 
1138 	d.enter((contents) {
1139 		d = typeof(d)(null);
1140 		(cast(ubyte[])contents)[] = 42;
1141 	});
1142 
1143 	d.length = d.length + 1;
1144 	d.length = d.capacity;
1145 
1146 	d = d ~ d;
1147 	d ~= d;
1148 	d.enter((contents) {
1149 		d = d ~ contents;
1150 		d = contents ~ d;
1151 		d ~= contents;
1152 	});
1153 }
1154 
1155 debug(ae_unittest) unittest
1156 {
1157 	import std.format : format;
1158 	assert(format!"%s"(TData!char("hello")) == "hello");
1159 }
1160 
1161 /// Get the underlying type of a `TData`.
1162 /// (For `Data`, this will be `ubyte`.)
1163 template DataElementType(D)
1164 if (is(D == TData!T, T))
1165 {
1166 	static if (is(D == TData!T, T))
1167 		alias DataElementType = T;
1168 }
1169 static assert(is(DataElementType!Data == ubyte));
1170 
1171 /// The most common use case of manipulating unmanaged memory is
1172 /// working with raw bytes, whether they're received from the network,
1173 /// read from a file, or elsewhere.
1174 alias Data = TData!ubyte;
1175 
1176 // ************************************************************************
1177 
1178 deprecated("legacy transitive import - please `import ae.sys.dataset;`.")
1179 public import ae.sys.dataset : copyTo, joinData, joinToHeap, DataVec, shift, bytes, DataSetBytes;
1180 
1181 deprecated alias DataWrapper = Memory;
1182 
1183 package(ae) // TODO: is this a good API?
1184 T as(T)(Data data)
1185 {
1186 	assert(data.length == T.sizeof, "Incorrect data size");
1187 	return data.asDataOf!T.front;
1188 }
1189 
1190 package(ae) // TODO: is this a good API?
1191 T pop(T)(ref Data data)
1192 {
1193 	assert(data.length >= T.sizeof, "Insufficient bytes in data");
1194 	auto result = data[0 .. T.sizeof].asDataOf!T.front;
1195 	data = data[T.sizeof .. $];
1196 	return result;
1197 }
1198 
1199 // ************************************************************************
1200 
1201 /// Base abstract class which owns a block of memory.
1202 abstract class Memory
1203 {
1204 	sizediff_t referenceCount = 0; /// Reference count.
1205 	abstract @property inout(ubyte)[] contents() inout pure @safe nothrow @nogc; /// The owned memory
1206 	abstract @property size_t size() const pure @safe nothrow @nogc;  /// Length of `contents`.
1207 	abstract void setSize(size_t newSize) pure @safe nothrow @nogc; /// Resize `contents` up to `capacity`.
1208 	abstract @property size_t capacity() const pure @safe nothrow @nogc; /// Maximum possible size.
1209 
1210 	debug ~this() nothrow @nogc
1211 	{
1212 		debug(DATA_REFCOUNT) debugLog("%.*s.~this, referenceCount==%d", this.classinfo.name.length, this.classinfo.name.ptr, referenceCount);
1213 		assert(referenceCount == 0, "Deleting Memory with non-zero reference count");
1214 	}
1215 }
1216 
1217 // ************************************************************************
1218 
1219 package:
1220 
1221 /// How many bytes are currently in `Data`-owned memory.
1222 static /*thread-local*/ size_t dataMemory, dataMemoryPeak;
1223 /// How many `Memory` instances there are live currently.
1224 static /*thread-local*/ uint   dataCount;
1225 /// How many allocations have been done so far.
1226 static /*thread-local*/ uint   allocCount;
1227 
1228 /// Set threshold of allocated memory to trigger a garbage collection.
1229 static if (useGC)
1230 void setGCThreshold(size_t value) { collectThreshold = value; }
1231 
1232 /// Allocate and construct a new class in `malloc`'d memory.
1233 C unmanagedNew(C, Args...)(auto ref Args args) @trusted
1234 if (is(C == class))
1235 {
1236 	import std.conv : emplace;
1237 	enum size = __traits(classInstanceSize, C);
1238 	auto p = unmanagedAlloc(size);
1239 	emplace!C(p[0..size], args);
1240 	return cast(C)p;
1241 }
1242 
1243 /// Delete a class instance created with `unmanagedNew`.
1244 void unmanagedDelete(C)(C c) nothrow @nogc
1245 if (is(C == class))
1246 {
1247 	// Add attributes to object.destroy by cast:
1248 	// - Add @nogc.
1249 	//   Object.~this is not @nogc, but allocating in a destructor crashes the GC anyway,
1250 	//   so all class destructors are already effectively @nogc.
1251 	// - Add pure as well.
1252 	//   Memory implementations may have impure destructors,
1253 	//   such as closing file descriptors for memory-mapped files.
1254 	//   However, implementations SHOULD be pure as far as the program's state is concerned.
1255 	static void callDestroy(C c) nothrow { c.destroy(); }
1256 	// No pure due to: https://issues.dlang.org/show_bug.cgi?id=23959
1257 	(cast(void function(C) /*pure*/ nothrow @nogc) &callDestroy)(c);
1258 
1259 	unmanagedFree(cast(void*)c);
1260 }
1261 
1262 void* unmanagedAlloc(size_t sz) pure nothrow @nogc
1263 {
1264 	import core.stdc.stdlib : malloc;
1265 
1266 	// Cast to add `pure` to malloc.
1267 	// Allocating with `new` is pure, and so should be malloc.
1268 	alias PureMalloc = extern (C) void* function(size_t) pure nothrow @nogc @system;
1269 	auto p = (cast(PureMalloc) &malloc)(sz);
1270 
1271 	debug(DATA_REFCOUNT) debugLog("? -> %p: Allocating via malloc (%d bytes)", p, cast(uint)sz);
1272 
1273 	if (!p)
1274 		//throw new OutOfMemoryError();
1275 		onOutOfMemoryError(); // @nogc
1276 
1277 	//GC.addRange(p, sz);
1278 	return p;
1279 }
1280 
1281 void unmanagedFree(void* p) pure nothrow @nogc
1282 {
1283 	import core.stdc.stdlib : free;
1284 
1285 	if (p)
1286 	{
1287 		debug(DATA_REFCOUNT) debugLog("? -> %p: Deleting via free", p);
1288 
1289 		//GC.removeRange(p);
1290 
1291 		// Cast to add `pure` to free.
1292 		// Same rationale as for malloc.
1293 		alias PureFree = extern (C) void function(void* ptr) pure nothrow @nogc @system;
1294 		(cast(PureFree) &free)(p);
1295 	}
1296 }
1297 
1298 version (Windows)
1299 	import core.sys.windows.windows;
1300 else
1301 {
1302 	import core.sys.posix.unistd;
1303 	import core.sys.posix.sys.mman;
1304 }
1305 
1306 static if (useGC)
1307 {
1308 	/// Threshold of allocated memory to trigger a collect.
1309 	__gshared size_t collectThreshold = 8*1024*1024; // 8MB
1310 	/// Counter towards the threshold.
1311 	/*thread-local*/ size_t allocatedThreshold;
1312 }
1313 
1314 /// Some form of dynamically-allocated memory.
1315 /// Implementation is provided by the Allocator parameter.
1316 /*private*/ class DynamicMemory(Allocator) : Memory
1317 {
1318 	/// Pointer to actual data.
1319 	ubyte* data;
1320 	/// Used size. Needed for safe appends.
1321 	size_t _size;
1322 	/// Allocated capacity.
1323 	size_t _capacity;
1324 
1325 	static if (useGC)
1326 	{
1327 		deprecated alias collectThreshold = .collectThreshold;
1328 		deprecated alias allocatedThreshold = .allocatedThreshold;
1329 	}
1330 
1331 	/// Create a new instance with given capacity.
1332 	this(size_t size, size_t capacity) pure @trusted nothrow @nogc
1333 	{
1334 		// Add attributes to the implementation by cast:
1335 		// - Add pure.
1336 		//   - The implementation is "pure" in the same way that the D
1337 		//     garbage collector is "pure", even though it has global state.
1338 		// - Add @nogc.
1339 		//   - There are a few common use cases for the @nogc attribute:
1340 		//     1. The entire program does not use the GC (and probably does not even link to one).
1341 		//     2. The program does use the GC, but some sections should not
1342 		//        (e.g. they perform only performance-sensitive computations
1343 		//        and accidental GC allocations should be caught and avoided).
1344 		//     3. The code is a library which wants to be usable by either GC or @nogc programs.
1345 		//   - The second case is the most common in my personal experience,
1346 		//     so by default we assume that a GC is present but still offer a @nogc interface.
1347 		//   - We do this to offer better memory usage and reclaim memory faster
1348 		//     when Data instances are on the D GC heap, but are unreferenced.
1349 		//   - To actually use this module without the D GC, compile with -version=ae_data_nogc.
1350 		//   - (Ideally, we would offer both a @nogc and non-@nogc interface,
1351 		//     and let the caller's @nogc-ness select which one is used,
1352 		//     in the same way that the compiler can choose between a @nogc and non-@nogc overload,
1353 		//     however this is not currently feasible to implement.)
1354 		(cast(void delegate(size_t size, size_t capacity) pure @trusted nothrow @nogc)&thisImpl)(size, capacity);
1355 	}
1356 
1357 	private final void thisImpl(size_t size, size_t capacity) @trusted nothrow
1358 	{
1359 		data = cast(ubyte*)Allocator.allocate(/*ref*/ capacity);
1360 		static if (useGC)
1361 			if (data is null)
1362 			{
1363 				debug(DATA) fprintf(stderr, "Garbage collect triggered by failed Data allocation of %llu bytes... ", cast(ulong)capacity);
1364 				GC.collect();
1365 				debug(DATA) fprintf(stderr, "Done\n");
1366 				data = cast(ubyte*)Allocator.allocate(/*ref*/ capacity);
1367 				.allocatedThreshold = 0;
1368 			}
1369 		if (data is null)
1370 			onOutOfMemoryError();
1371 
1372 		dataMemory += capacity;
1373 		if (dataMemoryPeak < dataMemory)
1374 			dataMemoryPeak = dataMemory;
1375 		dataCount ++;
1376 		allocCount ++;
1377 
1378 		this._size = size;
1379 		this._capacity = capacity;
1380 
1381 		static if (useGC)
1382 		{
1383 			// also collect
1384 			.allocatedThreshold += capacity;
1385 			if (.allocatedThreshold > .collectThreshold)
1386 			{
1387 				debug(DATA) fprintf(stderr, "Garbage collect triggered by total allocated Data exceeding threshold... ");
1388 				GC.collect();
1389 				debug(DATA) fprintf(stderr, "Done\n");
1390 				.allocatedThreshold = 0;
1391 			}
1392 		}
1393 	}
1394 
1395 	/// Destructor - destroys the wrapped data.
1396 	~this() @nogc
1397 	{
1398 		Allocator.deallocate(data, capacity);
1399 		data = null;
1400 		// If Memory is created and manually deleted, there is no need to cause frequent collections
1401 		static if (useGC)
1402 		{
1403 			if (.allocatedThreshold > capacity)
1404 				.allocatedThreshold -= capacity;
1405 			else
1406 				.allocatedThreshold = 0;
1407 		}
1408 
1409 		dataMemory -= capacity;
1410 		dataCount --;
1411 	}
1412 
1413 	@property override
1414 	size_t size() const pure @safe nothrow @nogc { return _size; }
1415 
1416 	@property override
1417 	size_t capacity() const pure @safe nothrow @nogc { return _capacity; }
1418 
1419 	override void setSize(size_t newSize) pure @safe nothrow @nogc
1420 	{
1421 		assert(newSize <= capacity);
1422 		_size = newSize;
1423 	}
1424 
1425 	@property override
1426 	inout(ubyte)[] contents() inout pure @trusted nothrow @nogc
1427 	{
1428 		return data[0 .. _size];
1429 	}
1430 }
1431 
1432 // TODO: Maybe use std.experimental.allocator, one day.
1433 // One blocker is that it needs to stop pretending the page size is 4096 everywhere.
1434 
1435 private struct OSAllocator
1436 {
1437 	static immutable size_t pageSize;
1438 
1439 	shared static this()
1440 	{
1441 		version (Windows)
1442 		{
1443 			import core.sys.windows.winbase : GetSystemInfo, SYSTEM_INFO;
1444 
1445 			SYSTEM_INFO si;
1446 			GetSystemInfo(&si);
1447 			pageSize = si.dwPageSize;
1448 		}
1449 		else
1450 		{
1451 			pageSize = sysconf(_SC_PAGE_SIZE);
1452 		}
1453 	}
1454 
1455 	static void* allocate(ref size_t size) /*pure*/ nothrow @nogc
1456 	{
1457 		if (is(typeof(pageSize)))
1458 			size = ((size-1) | (pageSize-1))+1;
1459 
1460 		version(Windows)
1461 		{
1462 			return VirtualAlloc(null, size, MEM_COMMIT, PAGE_READWRITE);
1463 		}
1464 		else
1465 		version(Posix)
1466 		{
1467 			version(linux)
1468 				import core.sys.linux.sys.mman : MAP_ANON;
1469 			auto p = mmap(null, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
1470 			return (p == MAP_FAILED) ? null : p;
1471 		}
1472 		else
1473 			return core.stdc.malloc(size);
1474 	}
1475 
1476 	static void deallocate(void* p, size_t size) @nogc
1477 	{
1478 		debug
1479 		{
1480 			(cast(ubyte*)p)[0..size] = 0xDB;
1481 		}
1482 		version(Windows)
1483 			VirtualFree(p, 0, MEM_RELEASE);
1484 		else
1485 		version(Posix)
1486 			munmap(p, size);
1487 		else
1488 			core.stdc.free(size);
1489 	}
1490 }
1491 
1492 /// Wrapper for data in RAM, allocated from the OS.
1493 alias OSMemory = DynamicMemory!OSAllocator;
1494 
1495 private struct CAllocator
1496 {
1497 	static void* allocate(ref size_t size) /*pure*/ nothrow @nogc
1498 	{
1499 		import core.stdc.stdlib : malloc;
1500 		return malloc(size);
1501 	}
1502 
1503 	static void deallocate(void* p, size_t size) @nogc
1504 	{
1505 		import core.stdc.stdlib : free;
1506 		free(p);
1507 	}
1508 }
1509 
1510 /// Wrapper for data in RAM, allocated from the C standard library.
1511 /// Used for small objects.
1512 alias CMemory = DynamicMemory!CAllocator;
1513 
1514 // ************************************************************************
1515 
1516 debug(DATA_REFCOUNT) import ae.utils.exception, ae.sys.memory, core.stdc.stdio;
1517 
1518 debug(DATA_REFCOUNT) void debugLog(Args...)(const char* s, Args args) @nogc
1519 {
1520 	fprintf(stderr, s, args);
1521 	fprintf(stderr, "\n");
1522 	if (inCollect())
1523 		fprintf(stderr, "\t(in GC collect)\n");
1524 	else
1525 		(cast(void function() @nogc)&debugStackTrace)();
1526 	fflush(core.stdc.stdio.stderr);
1527 }
1528 
1529 debug(DATA_REFCOUNT) void debugStackTrace()
1530 {
1531 	try
1532 		foreach (line; getStackTrace())
1533 			fprintf(stderr, "\t%.*s\n", cast(int)line.length, line.ptr);
1534 	catch (Throwable e)
1535 		fprintf(stderr, "\t(stacktrace error: %.*s)", cast(int)e.msg.length, e.msg.ptr);
1536 }