1 module lwdr.tracking;
2 
3 import rtoslink;
4 
5 version(LWDR_TrackMem)
6 {
7 	struct AllocationList(T, size_t MaxElements)
8 	{
9 		static assert(MaxElements > 0, "MaxElements should be at least 1!");
10 
11 		private T[MaxElements] payload;
12 		private size_t length_ = 0;
13 
14 		@property bool empty() const { return length_ == 0; }
15 		@property size_t length() const { return length_; }
16 
17 		bool add(T t) nothrow @nogc
18 		{
19 			if(length_ + 1 >= MaxElements)
20 				return false;
21 
22 			payload[length_++] = t;
23 			return true;
24 		}
25 
26 		auto getLast() nothrow @nogc
27 		{
28 			return payload[length_ - 1];
29 		}
30 
31 		void removeLast() nothrow @nogc
32 		{
33 			if(empty) return;
34 
35 			payload[length_ - 1] = T.init;
36 			length_--;
37 		}
38 
39 		bool removeLastOccurenceOf(T t) nothrow @nogc
40 		{
41 			if(empty) return false;
42 
43 			size_t index;
44 			bool found = false;
45 
46 			for(size_t i = length_ - 1; i > 0; i--)
47 			{
48 				if(payload[i] == t)
49 				{
50 					index = i;
51 					found = true;
52 				}
53 			}
54 
55 			if(!found)
56 				return false;
57 
58 			for(size_t i = index; i < length_ - 1; i++)
59 			{
60 				payload[i] = payload[i+1];
61 			}
62 			payload[length_ - 1] = T.init;
63 			length_--;
64 			return true;
65 		}
66 	}
67 
68 	version(LWDR_TLS)
69 		private AllocationList!(void*, 16) trackedAllocations;
70 	else
71 		private __gshared AllocationList!(void*, 16) trackedAllocations;
72 	
73 	struct MemAlloc
74 	{
75 		size_t allocsAtScope;
76 
77 		void free() nothrow @nogc
78 		{
79 			assert(allocsAtScope >= trackedAllocations.length);
80 
81 			auto difference = trackedAllocations.length - allocsAtScope;
82 			foreach(d; 0 .. difference)
83 			{
84 				auto ptr = trackedAllocations.getLast;
85 				trackedAllocations.removeLast;
86 				rtosbackend_heapfreealloc(ptr);
87 			}
88 		}
89 	}
90 
91 	MemAlloc enterMemoryTracking() nothrow @nogc
92 	{
93 		return MemAlloc(trackedAllocations.length);
94 	}
95 
96 	version(LWDR_TLS)
97 		private bool currentlyTracking = false;
98 	else
99 		private __gshared bool currentlyTracking = false;
100 	void enableMemoryTracking() nothrow @nogc { currentlyTracking = true; }
101 	void disableMemoryTracking() nothrow @nogc { currentlyTracking = false; }
102 
103 	void* lwdrInternal_alloc(size_t sz) nothrow @nogc
104 	{
105 		auto ptr = rtosbackend_heapalloc(sz);
106 		if(currentlyTracking)
107 		{
108 			trackedAllocations.add(ptr);
109 		}
110 		return ptr;
111 	}
112 
113 	void lwdrInternal_free(void* ptr) nothrow @nogc
114 	{
115 		trackedAllocations.removeLastOccurenceOf(ptr);
116 		rtosbackend_heapfreealloc(ptr);
117 	}
118 
119 	ubyte[] lwdrInternal_allocBytes(size_t sz) nothrow @nogc
120 	{
121 		return cast(ubyte[])lwdrInternal_alloc(sz)[0..sz];
122 	}
123 }
124 else
125 {
126 	void* lwdrInternal_alloc(size_t sz) nothrow @nogc pure
127 	{
128 		return rtosbackend_heapalloc(sz);
129 	}
130 
131 	void lwdrInternal_free(void* ptr) nothrow @nogc pure
132 	{
133 		return rtosbackend_heapfreealloc(ptr);
134 	}
135 
136 	ubyte[] lwdrInternal_allocBytes(size_t sz) nothrow pure @nogc
137 	{
138 		return cast(ubyte[])lwdrInternal_alloc(sz)[0..sz];
139 	}
140 }