Commits

Anonymous committed 6a902b5

TempAllocator

Comments (0)

Files changed (5)

 	// value after storing the size. That way we can 
 	const uint32_t HEADER_PAD_VALUE = 0xffffffffu;
 
-	// Aligns p to the specified alignment by moving it forward if necessary
-	// and returns the result.
-	inline void *align_forward(void *p, uint32_t align) {
-		uintptr_t pi = uintptr_t(p);
-		const uint32_t mod = pi % align;
-		if (mod)
-			pi += (align - mod);
-		return (void *)pi;
-	}
-
 	// Given a pointer to the header, returns a pointer to the data that follows it.
 	inline void *data_pointer(Header *header, uint32_t align) {
 		void *p = header + 1;
-		return align_forward(p, align);
+		return memory::align_forward(p, align);
 	}
 
 	// Given a pointer to the data, returns a pointer to the header before it.
 	/// Returns the total amount of memory allocated by this allocator. Note that the 
 	/// size returned can be bigger than the size of all individual allocations made,
 	/// because the allocator may keep additional structures.
+	///
+	/// If the allocator doesn't track memory, this function returns SIZE_NOT_TRACKED.
 	virtual uint32_t total_allocated() = 0;
 
 private:
 	/// Shuts down the global memory allocators created by init().
 	void shutdown();
 }
+
+namespace memory {
+	inline void *align_forward(void *p, uint32_t align);
+}
+
+// ---------------------------------------------------------------
+// Inline function implementations
+// ---------------------------------------------------------------
+
+// Aligns p to the specified alignment by moving it forward if necessary
+// and returns the result.
+inline void *memory::align_forward(void *p, uint32_t align) {
+	uintptr_t pi = uintptr_t(p);
+	const uint32_t mod = pi % align;
+	if (mod)
+		pi += (align - mod);
+	return (void *)pi;
+}
 
 COMPILER = "g++"
 EXEC = "unit_test"
-FLAGS = "-Wall -Wextra"
+FLAGS = "-Wall -Wextra -g"
 
 OBJECTS = %w(unit_test.o memory.o)
-HEADERS = %w(array.h collection_types.h memory.h memory_types.h types.h)
+HEADERS = %w(array.h collection_types.h memory.h memory_types.h types.h temp_allocator.h)
 
 # tasks
 
+#pragma once
+
+#include "memory.h"
+
+/// A temporary memory allocator that primarily allocates memory from a
+/// local stack buffer of size BUFFER_SIZE. If that memory is exhausted it will
+/// use the backing allocator (typically a scratch allocator).
+///
+/// Memory allocated with a TempAllocator does not have to be deallocated. It is
+/// automatically deallocated when the TempAllocator is destroyed.
+template <int BUFFER_SIZE>
+class TempAllocator : public Allocator
+{
+public:
+	/// Creates a new temporary allocator using the specified backing allocator.
+	TempAllocator(Allocator &backing = memory_globals::default_scratch_allocator());
+	virtual ~TempAllocator();
+
+	virtual void *allocate(uint32_t size, uint32_t align = DEFAULT_ALIGN);
+	
+	/// Deallocation is a NOP for the TempAllocator. The memory is automatically
+	/// deallocated when the TempAllocator is destroyed.
+	virtual void deallocate(void *) {}
+
+	/// Returns SIZE_NOT_TRACKED.
+	virtual uint32_t allocated_size(void *) {return SIZE_NOT_TRACKED;}
+
+	/// Returns SIZE_NOT_TRACKED.
+	virtual uint32_t total_allocated() {return SIZE_NOT_TRACKED;}
+
+private:
+	char _buffer[BUFFER_SIZE];	//< Local stack buffer for allocations.
+	Allocator &_backing;		//< Backing allocator if local memory is exhausted.
+	char *_start;				//< Start of current allocation region
+	char *_p;					//< Current allocation pointer.
+	char *_end;					//< End of current allocation region
+	unsigned _chunk_size;		//< Chunks to allocate from backing allocator
+};
+
+// If possible, use one of these predefined sizes for the TempAllocator to avoid
+// unnecessary template instantiation.
+typedef TempAllocator<64> TempAllocator64;
+typedef TempAllocator<128> TempAllocator128;
+typedef TempAllocator<256> TempAllocator256;
+typedef TempAllocator<512> TempAllocator512;
+typedef TempAllocator<1024> TempAllocator1024;
+typedef TempAllocator<2048> TempAllocator2048;
+typedef TempAllocator<4096> TempAllocator4096;
+
+// ---------------------------------------------------------------
+// Inline function implementations
+// ---------------------------------------------------------------
+
+template <int BUFFER_SIZE>
+TempAllocator<BUFFER_SIZE>::TempAllocator(Allocator &backing) : _backing(backing), _chunk_size(4*1024)
+{
+	_p = _start = _buffer;
+	_end = _start + BUFFER_SIZE;
+	*(void **)_start = 0;
+	_p += sizeof(void *);
+}
+
+template <int BUFFER_SIZE>
+TempAllocator<BUFFER_SIZE>::~TempAllocator()
+{
+	void *p = *(void **)_buffer;
+	while (p) {
+		void *next = *(void **)p;
+		_backing.deallocate(p);
+		p = next;
+	}
+}
+
+template <int BUFFER_SIZE>
+void *TempAllocator<BUFFER_SIZE>::allocate(uint32_t size, uint32_t align)
+{
+	_p = (char *)memory::align_forward(_p, align);
+	if (size > _end - _p) {
+		uint32_t to_allocate = sizeof(void *) + size + align;
+		if (to_allocate < _chunk_size)
+			to_allocate = _chunk_size;
+		_chunk_size *= 2;
+		void *p = _backing.allocate(to_allocate);
+		*(void **)_start = p;
+		_p = _start = (char *)p;
+		_end = _start + to_allocate;
+		*(void **)_start = 0;
+		_p += sizeof(void *);
+		memory::align_forward(p, align);
+	}
+	void *result = _p;
+	_p += size;
+	return result;
+}
+#include "temp_allocator.h"
 #include "array.h"
 #include "memory.h"
 
 
 		memory_globals::shutdown();
 	}
+
+	void test_temp_allocator() {
+		memory_globals::init(256*1024);
+		{
+			TempAllocator128 ta;
+			Array<int> a(ta);
+			for (int i=0; i<100; ++i)
+				array::push_back(a, i);
+			ta.allocate(2*1024);
+		}
+		memory_globals::shutdown();
+	}
 }
 
 int main(int, char **)
 	test_memory();
 	test_array();
 	test_scratch();
+	test_temp_allocator();
 	return 0;
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.