1 /*
2  * Copyright (C) 2020, 2021  Vladimir Panteleev <btdu@cy.md>
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public
6  * License v2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public
14  * License along with this program; if not, write to the
15  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16  * Boston, MA 021110-1307, USA.
17  */
18 
19 /// Memory allocation
20 module btdu.alloc;
21 
22 import core.exception : onOutOfMemoryError;
23 
24 import std.algorithm.comparison : max;
25 import std.experimental.allocator.building_blocks.allocator_list;
26 import std.experimental.allocator.building_blocks.null_allocator;
27 import std.experimental.allocator.building_blocks.region;
28 import std.experimental.allocator.mallocator;
29 import std.experimental.allocator.mmap_allocator;
30 import std.experimental.allocator;
31 
32 /// Allocator to use for objects with infinite lifetime, which will never be freed.
33 alias GrowAllocator = AllocatorList!((n) => Region!MmapAllocator(max(n, 1024 * 4096)), NullAllocator);
34 CheckedAllocator!GrowAllocator growAllocator;
35 
36 /// Casual allocator which supports deallocation.
37 alias CasualAllocator = CheckedAllocator!Mallocator;
38 
39 /// Wrapper allocator which calls a function when memory allocation fails.
40 /// Because downstream code doesn't check for nulls, this allows better error messages
41 /// should btdu run out of memory. (Mainly this is useful for testing and profiling.)
42 struct CheckedAllocator(ParentAllocator, alias onFail = onOutOfMemoryError)
43 {
44 	import std.traits : hasMember;
45 	import std.typecons : Ternary;
46 
47 	static if (stateSize!ParentAllocator)
48 		ParentAllocator parent;
49 	else
50 	{
51 		alias parent = ParentAllocator.instance;
52 		static CheckedAllocator instance;
53 	}
54 
55 	private T check(T)(T value) { if (!value) onFail(); return value; }
56 
57 	void[] allocate(size_t n) { return check(parent.allocate(n)); }
58 	bool reallocate(ref void[] b, size_t s) { return check(parent.reallocate(b, s)); }
59 
60 	// Note: we can't use `alias this` because we need to intercept allocateZeroed,
61 	// but we can't do that because it's package(std).
62 
63 	enum alignment = ParentAllocator.alignment;
64 
65 	size_t goodAllocSize(size_t n) { return parent.goodAllocSize(n); }
66 
67 	static if (hasMember!(ParentAllocator, "expand"))
68 	bool expand(ref void[] b, size_t delta) { return parent.expand(b, delta); }
69 
70 	static if (hasMember!(ParentAllocator, "owns"))
71 	Ternary owns(void[] b) { return parent.owns(b); }
72 
73 	static if (hasMember!(ParentAllocator, "deallocate"))
74 	bool deallocate(void[] b) { return parent.deallocate(b); }
75 
76 	static if (hasMember!(ParentAllocator, "deallocateAll"))
77 	bool deallocateAll() { return parent.deallocateAll(); }
78 
79 	static if (hasMember!(ParentAllocator, "empty"))
80 	pure nothrow @safe @nogc Ternary empty() const { return parent.empty; }
81 }