aboutsummaryrefslogtreecommitdiff
path: root/src/utils/storage.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/storage.cxx')
-rw-r--r--src/utils/storage.cxx314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/utils/storage.cxx b/src/utils/storage.cxx
new file mode 100644
index 00000000..80cfea0d
--- /dev/null
+++ b/src/utils/storage.cxx
@@ -0,0 +1,314 @@
+// Copyright (C) 2010-2013, 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.
+
+// --%: Gabriel Dos Reis.
+
+#include <open-axiom/storage>
+#include <open-axiom/FileMapping>
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+#ifdef OPENAXIOM_MS_WINDOWS_HOST
+# include <windows.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace OpenAxiom {
+ // ----------------
+ // -- SystemError --
+ // ----------------
+ SystemError::SystemError(const std::string& s) : text(s) { }
+
+ SystemError::~SystemError() { }
+
+ const std::string&
+ SystemError::message() const {
+ return text;
+ }
+
+ void
+ filesystem_error(const std::string& s) {
+ throw SystemError(s);
+ }
+
+ namespace Memory {
+ // Return storage page allocation unit in byte count.
+ size_t page_size() {
+#if defined(OPENAXIOM_MS_WINDOWS_HOST)
+ SYSTEM_INFO si = { };
+ GetSystemInfo(&si);
+ return si.dwPageSize;
+#elif defined(HAVE_UNISTD_H)
+ return sysconf(_SC_PAGESIZE);
+#else
+ // Well, we have to return a number.
+ return 4096;
+#endif
+ }
+
+ // Subroutine of os_acquire_raw_memory. Attempt to acquire
+ // storage from the host OS. Return null on failure.
+ static inline Pointer
+ os_allocate_read_write_raw_memory(size_t nbytes) {
+#if defined(OPENAXIOM_MS_WINDOWS_HOST)
+ return VirtualAlloc(Pointer(), nbytes,
+ MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+#elif defined(HAVE_SYS_MMAN_H)
+ Pointer p = mmap(Pointer(), nbytes, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | OPENAXIOM_MM_ANONYMOUS_MAP_FLAG,
+ -1, 0);
+ return p == MAP_FAILED ? Pointer() : p;
+#else
+ return malloc(byte_count);
+#endif
+ }
+
+ Pointer
+ os_acquire_raw_memory(size_t nbytes) {
+ Pointer p = os_allocate_read_write_raw_memory(nbytes);
+ if (p == nullptr)
+ throw SystemError("cannot acquire more memory");
+ return memset(p, nbytes, 0);
+ }
+
+ void
+ os_release_raw_memory(Pointer p, size_t n) {
+#if defined(OPENAXIOM_MS_WINDOWS_HOST)
+ VirtualFree(p, 0, MEM_RELEASE);
+#elif defined(HAVE_SYS_MMAN_H)
+ munmap(p, n);
+#else
+ free(p);
+#endif
+ }
+
+ // -------------
+ // -- Storage --
+ // -------------
+ struct Storage::Handle {
+ size_t extent; // count of allocated bytes
+ void* start; // beginning of usable address.
+ };
+
+ static inline Pointer
+ storage_end(Storage::Handle* h) {
+ return Storage::byte_address(h) + h->extent;
+ }
+
+ // Acquire storage chunk of at least `n' bytes.
+ // The result is a pointer to a storage object. That object
+ // `result' is constructed such that `begin(result)' points
+ // to the next allocatable address.
+ template<typename T>
+ static T*
+ acquire_storage_with_header(size_t n) {
+ n = Storage::round_up(n, page_size());
+ T* h = static_cast<T*>(os_acquire_raw_memory(n));
+ h->extent = n;
+ h->start = h + 1;
+ return h;
+ }
+
+ void
+ Storage::release(Handle* h) {
+ os_release_raw_memory(h, h->extent);
+ }
+
+ Pointer
+ Storage::begin(Handle* h) {
+ return h->start;
+ }
+
+ // -------------------------
+ // -- SinglyLinkedStorage --
+ // -------------------------
+ struct OneWayLinkHeader : Storage::Handle {
+ Handle* previous;
+ };
+
+ SinglyLinkedStorage::Handle*&
+ SinglyLinkedStorage::previous(Handle* h) {
+ return static_cast<OneWayLinkHeader*>(h)->previous;
+ }
+
+ // -------------------------
+ // -- DoublyLinkedStorage --
+ // -------------------------
+ struct TwoWayLinkHeader : Storage::Handle {
+ Handle* previous;
+ Handle* next;
+ };
+
+ static inline TwoWayLinkHeader*
+ two_way_link(Storage::Handle* h) {
+ return static_cast<TwoWayLinkHeader*>(h);
+ }
+
+ DoublyLinkedStorage::Handle*&
+ DoublyLinkedStorage::previous(Handle* h) {
+ return two_way_link(h)->previous;
+ }
+
+ DoublyLinkedStorage::Handle*&
+ DoublyLinkedStorage::next(Handle* h) {
+ return two_way_link(h)->next;
+ }
+
+ DoublyLinkedStorage::Handle*
+ DoublyLinkedStorage::acquire(size_t n, size_t a) {
+ // Add enough padding space for specified alignment.
+ const size_t overhead = round_up(sizeof (TwoWayLinkHeader), a);
+ TwoWayLinkHeader* h =
+ acquire_storage_with_header<TwoWayLinkHeader>(overhead + n);
+ h->start = byte_address (h) + overhead;
+ h->previous = nullptr;
+ h->next = nullptr;
+ return h;
+ }
+
+ // ------------------
+ // -- BlockStorage --
+ // ------------------
+ struct BlockHeader : OneWayLinkHeader {
+ Byte* available;
+ };
+
+ static inline BlockHeader*
+ block_header(BlockStorage::Handle* h) {
+ return static_cast<BlockHeader*>(h);
+ }
+
+ BlockStorage::Handle*
+ BlockStorage::acquire(size_t n, size_t a) {
+ const size_t overhead = round_up(sizeof (BlockHeader), a);
+ BlockHeader* h =
+ acquire_storage_with_header<BlockHeader>(overhead + n);
+ // Remember the next available address to allocate from.
+ h->available = byte_address(h) + overhead;
+ // That is also where the actual object storage starts.
+ h->start = h->available;
+ h->previous = nullptr;
+ return h;
+ }
+
+ Pointer
+ BlockStorage::next_address(Handle* h) {
+ return block_header(h)->available;
+ }
+
+ size_t
+ BlockStorage::room(Handle* h) {
+ return byte_address(storage_end(h)) - block_header(h)->available;
+ }
+
+ Pointer
+ BlockStorage::book(Handle* h, size_t n) {
+ BlockHeader* block = block_header(h);
+ void* const p = block->available;
+ block->available += n;
+ return p;
+ }
+
+
+ // -----------------
+ // -- FileMapping --
+ // -----------------
+ FileMapping::FileMapping(std::string path)
+ : start(), extent() {
+#if defined(OPENAXIOM_MS_WINDOWS_HOST)
+ HANDLE file = CreateFile(path.c_str(), GENERIC_READ, 0, 0,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if (file == INVALID_HANDLE_VALUE)
+ filesystem_error("could not access file " + path);
+ HANDLE mapping = CreateFileMapping(file, 0, PAGE_READONLY, 0, 0, 0);
+ if (mapping == 0)
+ filesystem_error("could not map file " + path);
+ start = static_cast<Byte*>
+ (MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0));
+ extent = GetFileSize(file, 0);
+ CloseHandle(mapping);
+ CloseHandle(file);
+#elif defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_MMAN_H) && defined(HAVE_FCNTL_H)
+ struct stat s;
+ errno = 0;
+ if (stat(path.c_str(), &s) < 0)
+ filesystem_error("could not access file " + path);
+ else if (!S_ISREG(s.st_mode))
+ filesystem_error(path + " is not a regular file");
+ int fd = open(path.c_str(), O_RDONLY);
+ if (fd < 0)
+ filesystem_error("could not open " + path);
+ start = static_cast<Byte*>
+ (mmap(Pointer(), s.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
+ close(fd);
+ if (start == MAP_FAILED)
+ filesystem_error("could not map file " + path);
+ extent = s.st_size;
+#else
+# error "Don't know how to map a file on this platform"
+#endif // OPENAXIOM_MS_WINDOWS_HOST
+ }
+
+ FileMapping::FileMapping(FileMapping&& f)
+ : start(f.start), extent(f.extent) {
+ f.start = nullptr;
+ f.extent = 0;
+ }
+
+ FileMapping::~FileMapping() {
+ if (start != nullptr) {
+#if defined(OPENAXIOM_MS_WINDOWS_HOST)
+ UnmapViewOfFile(start);
+#elif defined(HAVE_SYS_MMAN_H)
+ munmap(start, extent);
+#else
+# error "Don't know how to unmap a file on this platform"
+#endif
+ }
+ }
+ }
+}