diff options
Diffstat (limited to 'src/include/storage.H')
-rw-r--r-- | src/include/storage.H | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/include/storage.H b/src/include/storage.H new file mode 100644 index 00000000..b75ced9d --- /dev/null +++ b/src/include/storage.H @@ -0,0 +1,320 @@ +// Copyright (C) 2010-2013, Gabriel Dos Reis. +// All rights reserved. +// Written by Gabriel Dos Reis. +// +// 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 OpenAxiom. 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 +// --% from the host OS. Provide random access read to +// --% files through file mapping. + +#ifndef OPENAXIOM_STORAGE_included +#define OPENAXIOM_STORAGE_included + +#include <open-axiom/config> +#include <stddef.h> +#include <new> +#include <cmath> +#include <string> + +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(const 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(const std::string&); + + namespace Memory { + // Datatype for the unit of storage. + using Byte = unsigned char; + + // Datatype for pointers to data. + using Pointer = void*; + + // 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 previously 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 provides low-level abstractions intented for use + // to implement higher level storage abstractions. + struct Storage { + // Objects of this abstract datatype hold storage objects. + struct Handle; + + // 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(Handle*); + + // Return the start address of storage area. Clients would + // want to add padding bytes necessary to accomodate alignment + // requirements. + // Note: this should not be confused with the address of + // the handle object. + static Pointer begin(Handle*); + + // Courtesy conversion function from pointer to byte address. + static Byte* byte_address(Pointer h) { + return static_cast<Byte*>(h); + } + + // 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); + } + }; + + // ------------------------- + // -- SinglyLinkedStorage -- + // ------------------------- + // This class implements a simple single-linked list of storage + // objects. Each storage object in the link is created with + // a specific starting alignment requirements. + struct SinglyLinkedStorage : Storage { + // Return the previous handle in the link chain. + static Handle*& previous(Handle*); + }; + + // ------------------------- + // -- DoublyLinkedStorage -- + // ------------------------- + // Like SinglyLinkedStorage, except that the chain of storage + // object supports bidirectional travervsal. + struct DoublyLinkedStorage : Storage { + // Same as Storage::acquire, except that begin(h) returns an + // address that satisfies the alignment requirement `a'. + static Handle* acquire(size_t n, size_t a); + + // Return the previous handle in the link chain. + static Handle*& previous(Handle*); + + // Return the next handle in the link chain. + static Handle*& next(Handle*); + }; + + // ------------------ + // -- BlockStorage -- + // ------------------ + // This class implements a simple single-linked list of block storage. + // Each block maintains information about the next allocatable + // address within that block. + struct BlockStorage : SinglyLinkedStorage { + // Same as SinglyLinkedStorage::acquire(); initialize internal + // bookkeepking machinery. + static Handle* acquire(size_t n, size_t a); + + // Return the next allocatable address within the given block. + static Pointer next_address(Handle*); + + // Return the amount of allocatable byte in the given block. + static size_t room(Handle*); + + // Set `n' bytes aside with the given storage block. + static Pointer book(Handle*, size_t); + }; + + // ----------- + // -- 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<typename T> + struct Arena : protected BlockStorage { + // 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(Handle* h) { + return static_cast<T*>(BlockStorage::begin(h)); + } + + // Address of one-past-the-end object of type `T' in this storage. + static T* last_object(Handle* h) { + return static_cast<T*>(BlockStorage::next_address(h)); + } + + // Number of objects allocated in a storage. + static size_t object_count(Handle* h) { + return last_object(h) - first_object(h); + } + + BlockStorage::Handle* store; // active storage to allocate from + }; + + template<typename T> + size_t + Arena<T>::population() const { + size_t n = 0; + for (Handle* h = store; h != nullptr; h = previous(h)) + n += object_count(h); + return n; + } + + template<typename T> + T* + Arena<T>::allocate(size_t n) { + const size_t sz = n * sizeof(T); + if (BlockStorage::room(store) < sz) { + // Not enough room left. Make sure we allocate storage + // at least as big as the current. + Handle* h = acquire(std::max(n, object_count(store)), + openaxiom_alignment(T)); + previous(h) = store; + store = h; + } + return static_cast<T*>(BlockStorage::book(store, sz)); + } + + template<typename T> + Arena<T>::Arena(size_t n) + : store(BlockStorage::acquire(n * sizeof (T), + openaxiom_alignment (T))) + { } + + template<typename T> + Arena<T>::~Arena() { + // Release storage in the reverse order of their + // their allocation. + while (store != nullptr) { + Handle* current = store; + store = BlockStorage::previous(store); + BlockStorage::release(current); + } + } + + // ------------- + // -- Factory -- + // ------------- + template<typename T> + struct Factory : Arena<T> { + using Handler = typename Arena<T>::Handle; + + Factory() : Arena<T>(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<typename... Args> + T* make(const Args&... args) { + return new(this->allocate(1)) T(args...); + } + + private: + // Return 1 or the number of objects that can fit in a page unit. + static size_t nominal_population() { + const size_t psz = page_size(); + if (sizeof (T) > psz) + return 1; + return psz / sizeof(T); + } + }; + + // Destroy objects in the reverse order of their construction. + template<typename T> + Factory<T>::~Factory() { + for (auto s = this->store; s != nullptr; s = Arena<T>::previous(s)) { + T* last = Arena<T>::last_object(s); + for (--last; last >= Arena<T>::first_object(s); --last) + last->~T(); + } + } + + // ----------------- + // -- FileMapping -- + // ----------------- + struct FileMapping { + explicit FileMapping(std::string); + FileMapping(FileMapping&&); + ~FileMapping(); + const char* begin() const { return static_cast<const char*>(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&) = delete; + FileMapping& operator=(const FileMapping&) = delete; + }; + + } +} + +#endif // OPENAXIOM_STORAGE_included + |