foundation / memory.h

#pragma once

#include "types.h"
#include "memory_types.h"

namespace foundation
{
	/// 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;

		Allocator() {}
		virtual ~Allocator() {}
		
		/// Allocates the specified amount of memory aligned to the specified alignment.
		virtual void *allocate(uint32_t size, uint32_t align = DEFAULT_ALIGN) = 0;

		/// Frees an allocation previously made with allocate().
		virtual void deallocate(void *p) = 0;

		static const uint32_t SIZE_NOT_TRACKED = 0xffffffffu;

		/// Returns the amount of usable memory allocated at p. p must be a pointer
		/// returned by allocate() that has not yet been deallocated. The value returned
		/// will be at least the size specified to allocate(), but it can be bigger.
		/// (The allocator may round up the allocation to fit into a set of predefined
		/// slot sizes.)
		///
		/// Not all allocators support tracking the size of individual allocations.
		/// An allocator that doesn't suppor it will return SIZE_NOT_TRACKED.
		virtual uint32_t allocated_size(void *p) = 0;

		/// 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:
		/// Allocators cannot be copied.
	    Allocator(const Allocator& other);
	    Allocator& operator=(const Allocator& other);
	};

	/// Creates a new object of type T using the allocator a to allocate the memory.
	#define MAKE_NEW(a, T, ...)		(new ((a).allocate(sizeof(T), alignof(T))) T(__VA_ARGS__))

	/// Frees an object allocated with MAKE_NEW.
	#define MAKE_DELETE(a, T, p)	do {if (p) {(p)->~T(); a.deallocate(p);}} while (0)

	/// Functions for accessing global memory data.
	namespace memory_globals {
		/// Initializes the global memory allocators. scratch_buffer_size is the size of the
		/// memory buffer used by the scratch allocators.
		void init(uint32_t scratch_buffer_size = 4*1024*1024);

		/// Returns a default memory allocator that can be used for most allocations.
		///
		/// You need to call init() for this allocator to be available.
		Allocator &default_allocator();

		/// Returns a "scratch" allocator that can be used for temporary short-lived memory
		/// allocations. The scratch allocator uses a ring buffer of size scratch_buffer_size
		/// to service the allocations.
		///
		/// If there is not enough memory in the buffer to match requests for scratch
		/// memory, memory from the default_allocator will be returned instaed.
		Allocator &default_scratch_allocator();

		/// Shuts down the global memory allocators created by init().
		void shutdown();
	}

	namespace memory {
		inline void *align_forward(void *p, uint32_t align);
		inline void *pointer_add(void *p, uint32_t bytes);
		inline const void *pointer_add(const void *p, uint32_t bytes);
		inline void *pointer_sub(void *p, uint32_t bytes);
		inline const void *pointer_sub(const void *p, uint32_t bytes);
	}

	// ---------------------------------------------------------------
	// 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;
	}

	/// Returns the result of advancing p by the specified number of bytes
	inline void *memory::pointer_add(void *p, uint32_t bytes)	{
		return (void*)((char *)p + bytes);
	}

	inline const void *memory::pointer_add(const void *p, uint32_t bytes)	{
		return (const void*)((const char *)p + bytes);
	}

	/// Returns the result of moving p back by the specified number of bytes
	inline void *memory::pointer_sub(void *p, uint32_t bytes)	{
		return (void*)((char *)p - bytes);
	}

	inline const void *memory::pointer_sub(const void *p, uint32_t bytes)	{
		return (const void*)((const char *)p - bytes);
	}
}
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.