// Copyright (C) 2010, Gabriel Dos Reis. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // - Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // - Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in // the documentation and/or other materials provided with the // distribution. // // - Neither the name of The Numerical Algorithms Group Ltd. nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED // TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER // OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // --% Author: Gabriel Dos Reis // --% Description: // --% Memory management facility. Acquire raw memory directly // --% directly for the host OS. Provide random access read to // --% files through file mapping. #ifndef OPENAXIOM_STORAGE_INCLUDED #define OPENAXIOM_STORAGE_INCLUDED #include #include #include #include #include #include namespace OpenAxiom { // ----------------- // -- SystemError -- // ----------------- // Objects of (type derived from) this type are used to report // error orignating from the OpenAxiom core system. struct SystemError { explicit SystemError(std::string); virtual ~SystemError(); // Return the text of the diagnostic message. virtual const std::string& message() const; protected: const std::string text; }; // Report a file system error void filesystem_error(std::string); namespace Memory { // Datatype for the unit of storage. typedef unsigned char Byte; // Datatype for pointers to data. typedef void* Pointer; // Precision of the host OS storage page unit in byte count size_t page_size(); // Acquire raw memory from the host OS. Pointer os_acquire_raw_memory(size_t); // Release raw storage to the hosting OS. The first operand must // be a pointer value previous returned by `os_acquire_raw_memory'. // Otherwise, the result is undefined. void os_release_raw_memory(Pointer, size_t); // Acquire `n' pages of memory storage from the host OS. inline Pointer acquire_raw_pages(size_t n) { return os_acquire_raw_memory(n * page_size()); } // Release `n' pages of storage starting the location `p'. inline void release_raw_pages(Pointer p, size_t n) { os_release_raw_memory(p, n * page_size()); } // ------------- // -- Storage -- // ------------- // This class is a low-level abstraction intented for use // to implement higher level storage abstraction. struct Storage { // Acquire storage chunk of `n' bytes, and align // the first allocatable address to `a' booundary. // The result is a pointer to a storage object. That object // `result' is constructed such that `result->free' points // to the next allocatable address, with alignment `a'. static Storage* acquire(size_t a, size_t n); // Return the storage pointed to by the operand. It // must be a pointer value previously returned by `acquire'. // Otherwise, the result is undefined. static void release(Storage*); // Count of bytes that can fit in this storage. size_t capacity() const { return limit_bot - limit_top; } // Count of avaliable allocatable bytes in this storage. size_t room() const { return limit_bot - free; } // Count of allocated storage in this storage. size_t occupancy() const { return free - limit_top; } // Align next allocatable address to a boundary (operand). // Return true on success. bool align_to(size_t); // Allocate `n' bytes of storage. It is assumed that prior // to calling this function, `n' is less than `room()'. // The allocated storage is guaranteed to contain only zeros. void* allocate(size_t n) { void* result = free; free += n; return memset(result, 0, n); } // Next unused address void* next_available() { return free; } // address at offset `o' from the first allocatable address. void* at_offset(size_t o) { return limit_top + o; } // Round up `n' to a multiple of `a', a power of 2. static size_t round_up(size_t n, size_t a) { return (n + a - 1) & ~(a - 1); } // Next address after `p' in this storage that has alignment `a'. void* round_up(void* p, size_t a) { return base() + round_up(base() - static_cast(p), a); } protected: Byte* limit_top; // first allocatable address Byte* limit_bot; // one-past-the-end of valid allocatable // address in this storage. Byte* free; // first allocatable address suitably // aligned at boundary specified at // construction time. Storage() { } // Address of the host OS page holding this storage. Byte* base() { return reinterpret_cast(this); } size_t extent() { return size_t(limit_bot - base()); } private: Storage(const Storage&); // not implemented Storage& operator=(const Storage&); // idem. }; // ----------- // -- Arena -- // ----------- // Extensible storage holding objects of a given type. // The totality of all objects held in such a storage does not // necessarily constitute a contiguous block. However, // it is guaranteed that objects allocated in a single call // to `allocate()' occupy a contiguous block of storage. template struct Arena { // Acquire storage capable of holding `n' objects of type `T'. explicit Arena(size_t); // Release all storage acquired by this object, upon end of life. ~Arena(); // allocate storage for `n' more objects of type `T'. T* allocate(size_t); // Number of objects of type `T' allocated in this storage. size_t population() const; protected: // Address of the first object of type `T' in a storage. static T* first_object(Storage* s) { return static_cast (s->round_up(&previous(s) + 1, openaxiom_alignment(T))); } // Address of one-past-the-end object of type `T' in this storage. static T* last_object(Storage* s) { return static_cast(s->next_available()); } // Number of objects allocated in a storage. static size_t object_count(Storage* s) { return last_object(s) - first_object(s); } // The `previous' link in the chain of storage. static Storage*& previous(Storage* s) { return *static_cast(s->at_offset(0)); } Storage* store; // active storage to allocate from private: // Acquire storage large enough to hold `n' objects of type `T'. static Storage* acquire(size_t); }; template size_t Arena::population() const { size_t n = 0; for (Storage* s = store; s != 0; s = previous(s)) n += object_count(s); return n; } template T* Arena::allocate(size_t n) { const size_t sz = n * sizeof(T); if (store->room() < sz) { Storage* s = acquire(std::max(n, object_count(store))); previous(s) = store; store = s; } return static_cast(store->allocate(sz)); } template Arena::Arena(size_t n) : store(acquire(n)) { } template Arena::~Arena() { // Destroy objects in the reverse order of their // their allocation. while (store != 0) { Storage* current = store; store = previous(store); Storage::release(current); } } template Storage* Arena::acquire(size_t n) { // We build single-linked list of Storage objects, so // don't forget to account for the additional pointer, // and necessary padding. const size_t sz = n * sizeof(T) + Storage::round_up(sizeof(Storage*), openaxiom_alignment(T)); Storage* s = Storage::acquire(openaxiom_alignment(Storage*), sz); s->allocate(sizeof(Storage*)); previous(s) = 0; s->align_to(openaxiom_alignment(T)); return s; } // ------------- // -- Factory -- // ------------- template struct Factory : Arena { Factory() : Arena(nominal_population()) { } ~Factory(); // Allocate storage and value-construct an object of type `T'. T* make() { return new(this->allocate(1)) T(); } // Allocate storage and construct an object of type `T'. template T* make(const U& u) { return new(this->allocate(1)) T(u); } // Allocate storage and construct an object of type `T'. template T* make(const U& u, const V& v) { return new(this->allocate(1)) T(u, v); } // Allocate storage and construct an object of type `T'. template T* make(const U& u, const V& v, const W& w) { return new(this->allocate(1)) T(u, v, w); } private: // Return 1 or the number of objects that can fit in a page unit. static size_t nominal_population() { const size_t overhead = Storage::round_up(sizeof(Storage), openaxiom_alignment(Storage*)) + Storage::round_up(sizeof(Storage*), openaxiom_alignment(T)); const size_t psz = page_size(); if (overhead + sizeof (T) > psz) return 1; return (psz - overhead) / sizeof(T); } }; // Destroy objects in the reverse order of their construction. template Factory::~Factory() { for (Storage* s = this->store; s != 0; s = Arena::previous(s)) { T* last = Arena::last_object(s); for (--last; last >= Arena::first_object(s); --last) last->~T(); } } // ----------------- // -- FileMapping -- // ----------------- struct FileMapping { explicit FileMapping(std::string); ~FileMapping(); const char* begin() const { return static_cast(start); } const char* end() const { return begin() + extent; } std::size_t size() const { return extent; } protected: Pointer start; // address at the mapped storage size_t extent; // length (in bytes) of the storage private: FileMapping(const FileMapping&); // not implemented FileMapping& operator=(const FileMapping&); // idem }; } } #endif // OPENAXIOM_STORAGE_INCLUDED