#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(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 {
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
return 4096;
#endif
}
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
}
struct Storage::Handle {
size_t extent;
void* start;
};
static inline Pointer
storage_end(Storage::Handle* h) {
return Storage::byte_address(h) + h->extent;
}
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;
}
struct OneWayLinkHeader : Storage::Handle {
Handle* previous;
};
SinglyLinkedStorage::Handle*&
SinglyLinkedStorage::previous(Handle* h) {
return static_cast<OneWayLinkHeader*>(h)->previous;
}
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) {
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;
}
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);
h->available = byte_address(h) + overhead;
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(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
}
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
}
}
}
}