Commits

Anonymous committed cca00e3

basic memory system

Comments (0)

Files changed (5)

+#include "memory.h"
+
+#include <assert.h>
+#include <new>
+
+namespace {
+	class MallocAllocator : public Allocator
+	{
+		struct Header {
+			uint32_t size;
+		};
+
+		static const uint32_t HEADER_PAD_VALUE = 0xffffffffu;
+
+		uint32_t _total_allocated;
+
+		static inline uint32_t size_with_header(uint32_t size, uint32_t align) {
+			return size + align + sizeof(Header);
+		}
+
+		static inline void *data_pointer(Header *header, uint32_t align) {
+			const void *p = header + 1;
+			uintptr_t pi = uintptr_t(p);
+			const uint32_t mod = pi % align;
+			if (mod)
+				pi += (align - mod);
+			return (void *)pi;
+		}
+
+		static inline void fill(Header *header, void *data, uint32_t size)
+		{
+			header->size = size;
+			uint32_t *p = (uint32_t *)(header + 1);
+			while (p < data)
+				*p++ = HEADER_PAD_VALUE;
+		}
+
+		static inline Header *header(void *data)
+		{
+			uint32_t *p = (uint32_t *)data;
+			while (p[-1] == HEADER_PAD_VALUE)
+				--p;
+			return (Header *)p - 1;
+		}
+
+	public:
+		MallocAllocator() : _total_allocated(0) {}
+
+		~MallocAllocator() {
+			assert(_total_allocated == 0);
+		}
+
+		virtual void *allocate(uint32_t size, uint32_t align) {
+			const uint32_t ts = size_with_header(size, align);
+			Header *h = (Header *)malloc(ts);
+			void *p = data_pointer(h, align);
+			fill(h, p, ts);
+			_total_allocated += ts;
+			return p;
+		}
+
+		virtual void deallocate(void *p) {
+			Header *h = header(p);
+			_total_allocated -= h->size;
+			free(h);
+		}
+
+		virtual uint32_t allocated_size(void *p) {
+			return header(p)->size;
+		}
+
+		virtual uint32_t total_allocated() {
+			return _total_allocated;
+		}
+	};
+
+	struct MemoryGlobals {
+		static const int ALLOCATOR_MEMORY = sizeof(MallocAllocator);
+		uint8_t buffer[ALLOCATOR_MEMORY];
+
+		MallocAllocator *default_allocator;
+
+		MemoryGlobals() : default_allocator(0) {}
+	};
+
+	MemoryGlobals _memory_globals;
+}
+
+namespace memory_globals {
+	void init() {
+		uint8_t *p = _memory_globals.buffer;
+		_memory_globals.default_allocator = new (p) MallocAllocator();
+	}
+
+	Allocator &default_allocator() {
+		return *_memory_globals.default_allocator;
+	}
+
+	void shutdown() {
+		_memory_globals.default_allocator->~MallocAllocator();
+		_memory_globals = MemoryGlobals();
+	}
+}
+
+
+#include "memory_types.h"
+#include <stdint.h>
+
+/// Base class for memory allocators.
+///
+/// Note: Regardless of which allocator is used, prefer to allocate memory in larger chunks
+/// instead of in many small allocations. This helps with data locality, fragmentation,
+/// memory usage tracking, etc.
+class Allocator
+{
+public:
+	/// Default alignment for memory allocations.
+	static const uint32_t DEFAULT_ALIGN = 4;
+
+	virtual ~Allocator() {}
+	
+	virtual void *allocate(uint32_t size, uint32_t align = DEFAULT_ALIGN) = 0;
+	virtual void deallocate(void *p) = 0;
+	virtual uint32_t allocated_size(void *p) = 0;
+	virtual uint32_t total_allocated() = 0;
+};
+
+#define MAKE_NEW(a, T, ...)		(new ((a).allocate(sizeof(T), alignof(T))) T(__VA_ARGS__))
+#define MAKE_DELETE(a, T, p)	do {if (p) {(p)->~T(); a.deallocate(p);}} while (0)
+
+namespace memory_globals {
+	void init();
+	Allocator &default_allocator();
+	void shutdown();
+}
+class Allocator;
+# constants
+
+COMPILER = "g++"
+EXEC = "unit_test"
+FLAGS = "-Wall -Wextra"
+
+OBJECTS = ['unit_test.o', 'memory.o']
+
+# tasks
+
+task :build => [EXEC]
+
+task :test => :build do
+	sh "./#{EXEC}"
+end
+
+task :default => :test
+
+desc "Clean stuff"
+task :clean do
+	files = (Dir["*.o"] + Dir["#{EXEC}"]).uniq
+	rm_f files unless files.empty?
+end
+
+# rules
+
+rule '.o' => '.cpp' do |target|
+	sh "#{COMPILER} #{FLAGS} -c -o #{target.name} #{target.source}"
+end
+
+file EXEC => OBJECTS do
+	sh "#{COMPILER} #{FLAGS} #{OBJECTS.join(" ")} -o #{EXEC}"
+end
+#include "memory.h"
+
+#include <stdio.h>
+#include <assert.h>
+
+#define CHECK(x) assert(x)
+
+namespace {
+	void test_memory() {
+		memory_globals::init();
+		Allocator &a = memory_globals::default_allocator();
+
+		void *p = a.allocate(100);
+		CHECK(a.allocated_size(p) >= 100);
+		CHECK(a.total_allocated() >= 100);
+		void *q = a.allocate(100);
+		CHECK(a.allocated_size(q) >= 100);
+		CHECK(a.total_allocated() >= 200);
+
+		a.deallocate(p);
+		a.deallocate(q);
+
+		CHECK(a.total_allocated() == 0);
+
+		memory_globals::shutdown();
+	}
+}
+
+int main(int, char **)
+{
+	test_memory();
+}