dbacchet avatar dbacchet committed 359a37c

started implementing foundation library

Comments (0)

Files changed (12)

sources/SConscript

 env_local = env.Clone()
 
 
+
+# build foundation
+SConscript( 'foundation/SConscript',       exports={'env':env_local} )
+# build utils
+SConscript( 'utils/SConscript',            exports={'env':env_local} )
+
+
 # build blendelf
 if not (env_local['TARGET_OS']=='win32' and env_local['TARGET_ARCH'] in ['x86_64','amd64']):
-	SConscript( 'blendelf/SConscript',
-	            exports={'env':env_local} )
-
-# build utils
-SConscript( 'utils/SConscript',
-            exports={'env':env_local} )
+	SConscript( 'blendelf/SConscript',     exports={'env':env_local} )
 
 # build bf2
-SConscript( 'bf2/SConscript',
-            exports={'env':env_local} )
+SConscript( 'bf2/SConscript',              exports={'env':env_local} )
 
 # build opengl samples
-SConscript( 'opengl_samples/SConscript',
-            exports={'env':env_local} )
-SConscript( 'opengl2_samples/SConscript',
-            exports={'env':env_local} )
+SConscript( 'opengl_samples/SConscript',   exports={'env':env_local} )
+SConscript( 'opengl2_samples/SConscript',  exports={'env':env_local} )
 
 # build tools
-SConscript( 'tools/SConscript',
-            exports={'env':env_local} )
+SConscript( 'tools/SConscript',            exports={'env':env_local} )
 

sources/foundation/SConscript

+# coding: utf-8
+# 2012, Davide Bacchet (davide.bacchet@gmail.com)
+# $LastChangedDate: 2011-05-12 12:03:21 +0200 (Mon, 12 May 2008) $
+# $LastChangedBy: dbacc $
+
+
+import os, sys, fnmatch
+import pyutils
+import thirdparty
+
+Import('env')
+env_local = env.Clone()
+
+
+# include dirs
+env_local.AppendUnique(CPPPATH = ['.'] )
+
+# add 3rdparty parameters
+thirdparty.osdeps.addOSToEnv(env_local)
+
+                                  
+# get list of files
+files = []
+files.append(Glob('./*.c*'))
+
+# build library
+foundation_lib  = env_local.StaticLibrary('foundation',files)
+
+
+Return('foundation_lib')
+

sources/foundation/events.h

+#pragma once
+
+#include "types.h"
+
+
+#define EVT_TYPE_MASK    0x00FF // 8 bit for type
+#define EVT_TYPE_SHIFT   0      // bits 0-7
+#define EVT_THREAD_MASK  0x00FF // 8 bit for thread num
+#define EVT_THREAD_SHIFT 8      // bits 8-15
+#define EVT_SIZE_MASK    0x00FF // 8 bit for data size
+#define EVT_SIZE_SHIFT   16     // bits 16-23
+
+
+/// event message header
+/// @details The header has the following bit structure (each block is 8 bits):\n
+/// [ empty  |  size  | thread |  type  ] 
+/// [........|........|........|........]
+typedef EventHeader int32_t;
+
+enum Events
+{
+	ENTER_PROFILER_SCOPE, 
+	LEAVE_PROFILER_SCOPE, 
+	ALLOCATE_MEMORY, 
+	FREE_MEMORY, 
+	RECORD_GLOBAL_FLOAT
+};
+
+// /// @struct Event
+// struct Event
+// {
+//     int32_t id;       ///< event identifier
+//     char    data[12]; ///< associated data 
+// };
+
+
+// // Print n as a binary number
+// void printbitssimple(int32_t n)
+// {
+//     unsigned int i;
+//     i = 1<<(sizeof(n) * 8 - 1);
+    
+//     for(int k=0;k<sizeof(n)*8;k++)
+//     {
+//         if (k%8==0)
+//             printf(" ");
+//         if (n & i)
+//             printf("1");
+//         else
+//             printf("0");
+//         i >>= 1;
+//     }
+// }
+
+// int main(int argc, const char * argv[])
+// {
+
+//     // insert code here...
+//     std::cout << "Hello, World!\n";
+    
+    
+//     int32_t value;
+    
+//     int type = 121;
+//     int thread = 15;
+//     value = (thread<<SHIFT_THREAD) + type;
+
+//     printf("\n value:   ");
+//     printbitssimple(value);
+//     printf("\ntyp mask: ");
+//     printbitssimple(MASK_TYPE);
+//     printf("\nthr mask: ");
+//     printbitssimple(MASK_THREAD);
+//     printf("\n");
+    
+//     printf("thread from value: %d\n",(value&MASK_THREAD)>>SHIFT_THREAD);
+//     printf("type from value:   %d\n",(value&MASK_TYPE));
+    
+//     printf("int   ptr size: %lu\n",sizeof(int32_t*));
+//     printf("char  ptr size: %lu\n",sizeof(char*));
+//     printf("float ptr size: %lu\n",sizeof(float*));
+//     return 0;
+// }
+

sources/foundation/memory.cpp

+#include "memory.h"
+
+#include <cstdlib>
+#include <cassert>
+#include <new>
+
+namespace 
+{
+    using namespace foundation;
+
+    // Header stored at the beginning of a memory allocation to indicate the
+    // size of the allocated data.
+    struct Header 
+    {
+        uint32_t size;
+    };
+
+    // If we need to align the memory allocation we pad the header with this value after storing the size. 
+    // In this way we can mark the memory used for padding
+    const uint32_t HEADER_PAD_VALUE = 0xffffffffu;
+
+    // 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 memory::align_forward(p, align);
+    }
+
+    // Given a pointer to the data, returns a pointer to the header before it.
+    inline Header *header(void *data)
+    {
+        uint32_t *p = (uint32_t *)data;
+        while (p[-1] == HEADER_PAD_VALUE)
+            --p;
+        return (Header *)p - 1;
+    }
+
+    // Stores the size in the header and pads with HEADER_PAD_VALUE up to the
+    // data pointer.
+    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;
+    }
+
+
+    /// An allocator that uses the default system malloc(). Allocations are
+    /// padded so that we can store the size of each allocation and align them
+    /// to the desired alignment.
+    class MallocAllocator : public Allocator
+    {
+    public:
+
+        MallocAllocator() : _total_allocated(0) {}
+
+        ~MallocAllocator() {
+            // Check that we don't have any memory leaks when allocator is
+            // destroyed.
+            assert(_total_allocated == 0);
+        }
+
+        virtual void *allocate(uint32_t size, uint32_t align) 
+        {
+            const uint32_t ts = size_with_padding(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) 
+        {
+            if (!p)
+                return;
+
+            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;
+        }
+
+    private:
+        
+        uint32_t _total_allocated;
+
+        // Returns the size to allocate from malloc() for a given size and align.       
+        static inline uint32_t size_with_padding(uint32_t size, uint32_t align) 
+        {
+            return size + align + sizeof(Header);
+        }
+
+    };
+
+
+    /// An allocator used to allocate temporary "scratch" memory. The allocator
+    /// uses a fixed size ring buffer to services the requests.
+    ///
+    /// Memory is always allocated linearly. An allocation pointer is advanced
+    /// through the buffer as memory is allocated and wraps around at the end of
+    /// the buffer. Similarly, a free pointer is advanced as memory is freed.
+    ///
+    /// It is important that the scratch allocator is only used for short-lived
+    /// memory allocations. A long lived allocator will lock the "free" pointer
+    /// and prevent the "allocate" pointer from proceeding past it, which means
+    /// the ring buffer can't be used.
+    /// 
+    /// If the ring buffer is exhausted, the scratch allocator will use its backing
+    /// allocator to allocate memory instead.
+    class ScratchAllocator : public Allocator
+    {
+        Allocator &_backing;
+        
+        // Start and end of the ring buffer.
+        char *_begin, *_end;
+
+        // Pointers to where to allocate memory and where to free memory.
+        char *_allocate, *_free;
+        
+    public:
+        /// Creates a ScratchAllocator. The allocator will use the backing
+        /// allocator to create the ring buffer and to service any requests
+        /// that don't fit in the ring buffer.
+        ///
+        /// size specifies the size of the ring buffer.
+        ScratchAllocator(Allocator &backing, uint32_t size) : _backing(backing) 
+        {
+            _begin = (char *)_backing.allocate(size);   // alloc the buffer using the backing allocator
+            _end = _begin + size;
+            _allocate = _begin;
+            _free = _begin;
+        }
+
+        ~ScratchAllocator() 
+        {
+            assert(_free == _allocate);
+            _backing.deallocate(_begin);
+        }
+
+        bool in_use(void *p)
+        {
+            if (_free == _allocate)
+                return false;
+            if (_allocate > _free)
+                return p >= _free && p < _allocate;
+            return p >= _free || p < _allocate;
+        }
+
+        virtual void *allocate(uint32_t size, uint32_t align) 
+        {
+            assert(align % 4 == 0);
+            size = ((size + 3)/4)*4;
+
+            char *p = _allocate;
+            Header *h = (Header *)p;
+            char *data = (char *)data_pointer(h, align);
+            p = data + size;
+
+            // Reached the end of the buffer, wrap around to the beginning.
+            if (p > _end) 
+            {
+                h->size = (_end - (char *)h) | 0x80000000u;
+                
+                p = _begin;
+                h = (Header *)p;
+                data = (char *)data_pointer(h, align);
+                p = data + size;
+            }
+            
+            // If the buffer is exhausted use the backing allocator instead.
+            if (in_use(p))
+                return _backing.allocate(size, align);
+
+            fill(h, data, p - (char *)h);
+            _allocate = p;
+            return data;
+        }
+
+        virtual void deallocate(void *p) 
+        {
+            if (!p)
+                return;
+            // if memory was allocated using the backing allocator
+            if (p < _begin || p >= _end) 
+            {
+                _backing.deallocate(p);
+                return;
+            }
+
+            // Mark this slot as free
+            Header *h = header(p);
+            assert((h->size & 0x80000000u) == 0);
+            h->size = h->size | 0x80000000u;
+
+            // Advance the free pointer past all free slots.
+            while (_free != _allocate) {
+                Header *h = (Header *)_free;
+                if ((h->size & 0x80000000u) == 0)
+                    break;
+
+                _free += h->size & 0x7fffffffu;
+                if (_free == _end)
+                    _free = _begin;
+            }
+        }
+
+        virtual uint32_t allocated_size(void *p) 
+        {
+            Header *h = header(p);
+            return h->size - ((char *)p - (char *)h);
+        }
+
+        virtual uint32_t total_allocated() 
+        {
+            return _end - _begin;
+        }
+    };
+
+
+    struct MemoryGlobals 
+    {
+        static const int ALLOCATOR_MEMORY = sizeof(MallocAllocator) + sizeof(ScratchAllocator);
+        char buffer[ALLOCATOR_MEMORY];
+
+        MallocAllocator *default_allocator;
+        ScratchAllocator *default_scratch_allocator;
+
+        MemoryGlobals() : default_allocator(0), default_scratch_allocator(0) {}
+    };
+
+    MemoryGlobals _memory_globals;
+}
+
+namespace foundation
+{
+    namespace memory_globals
+    {
+        void init(uint32_t temporary_memory) 
+        {
+            char *p = _memory_globals.buffer;
+            _memory_globals.default_allocator = new (p) MallocAllocator();
+            p += sizeof(MallocAllocator);
+            _memory_globals.default_scratch_allocator = new (p) ScratchAllocator(*_memory_globals.default_allocator, temporary_memory);
+        }
+
+        Allocator &default_allocator() {
+            return *_memory_globals.default_allocator;
+        }
+
+        Allocator &default_scratch_allocator() {
+            return *_memory_globals.default_scratch_allocator;
+        }
+
+        void shutdown() {
+            _memory_globals.default_scratch_allocator->~ScratchAllocator();
+            _memory_globals.default_allocator->~MallocAllocator();
+            _memory_globals = MemoryGlobals();
+        }
+    }
+}

sources/foundation/memory.h

+#pragma once
+
+// this file is based on the memory routines of the bitquid foundation libraries (https://bitbucket.org/bitsquid/foundation/)
+
+
+#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 
+	{
+		// 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;
+		}
+	}
+
+
+}

sources/foundation/memory_types.h

+#pragma once
+
+namespace foundation
+{
+	class Allocator;
+}

sources/foundation/temp_allocator.h

+#pragma once
+
+#include "memory.h"
+
+namespace foundation
+{
+	/// 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 ((int)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;
+	}
+}

sources/foundation/types.h

+#pragma once
+
+
+#include <stdint.h>		// fixed size int types
+
+
+
+// use same notation on win and unix
+#ifndef alignof
+	#define alignof(x) __alignof(x)
+#endif

testing/SConscript

 Import('env')
 env_local = env.Clone()
 
+# build foundation testing
+SConscript( 'foundation/SConscript', exports={'env':env_local} )
 # build utils testing
-SConscript( 'utils/SConscript',
-            exports={'env':env_local} )
+SConscript( 'utils/SConscript',      exports={'env':env_local} )
+# build bf2 testing
+SConscript( 'bf2/SConscript',        exports={'env':env_local} )
 
-# build bf2 testing
-SConscript( 'bf2/SConscript',
-            exports={'env':env_local} )
-

testing/foundation/SConscript

+# coding: utf-8
+# bf2 testing Sconscript
+# 2011, Davide Bacchet (davide.bacchet@gmail.com)
+# $LastChangedDate: 2011-05-12 12:03:21 +0200 (Mon, 12 May 2008) $
+# $LastChangedBy: dbacc $
+
+
+import os, sys, fnmatch
+import pyutils
+import thirdparty
+
+Import('env')
+env_local = env.Clone()
+
+# include dirs
+env_local.AppendUnique(CPPPATH = ['#/sources/foundation'] )
+# lib dirs
+env_local.AppendUnique(LIBPATH = ['../../sources/foundation'] )
+                                  
+# add 3rdparty parameters
+thirdparty.osdeps.addOSToEnv(env_local)
+thirdparty.unittestpp.addUnitTestppToEnv(env_local)
+
+
+# get list of files
+files = Glob('./*.c*')
+
+# additional libraries
+libs = []
+libs.append('foundation')
+env_local.AppendUnique(LIBS = libs )
+
+# build test app
+test_foundation = env_local.Program('test_foundation',files,PDB='test_foundation.pdb')
+
+
+Return('test_foundation')
+

testing/foundation/main.cpp

+// unit testing main file
+
+#include <iostream>
+#include "UnitTest++.h"
+
+
+int main(int, char const *[])
+{
+#ifdef __Debug__
+int dbidx = 0;
+std::cout << "enter a number:";
+std::cin >> dbidx;
+#endif //__Debug__
+
+    return UnitTest::RunAllTests();
+}

testing/foundation/test_memory.cpp

+#include <iostream>
+#include <cstdio>
+#include <string>
+
+#include "UnitTest++.h"
+using namespace UnitTest;
+
+#include "memory.h"
+
+namespace 
+{
+    using namespace foundation;
+
+	TEST (TestDefaultAllocator)
+	{
+        memory_globals::init();
+
+        Allocator &a = memory_globals::default_allocator();
+
+        uint32_t allsize = 0;//a.allocated_size(p);
+        uint32_t totsize = a.total_allocated();
+        CHECK(totsize>=4*1024*1024);
+
+        void *p = a.allocate(100);
+        allsize = a.allocated_size(p);
+        totsize = a.total_allocated();
+        CHECK(a.allocated_size(p) >= 100);
+        CHECK(a.total_allocated() >= 100 + 4*1024*1024);
+
+        void *q = a.allocate(100);
+        allsize = a.allocated_size(q);
+        totsize = a.total_allocated();
+        CHECK(a.allocated_size(q) >= 100);
+        CHECK(a.total_allocated() >= 200 + 4*1024*1024);
+        
+        a.deallocate(p);
+        a.deallocate(q);
+
+        CHECK(a.total_allocated() < 100 + 4*1024*1024);
+        CHECK(a.total_allocated() >= 4*1024*1024);
+        
+        memory_globals::shutdown();        
+	}
+	
+
+}
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.