// Copyright Notice (GNU Affero GPL) {{{
/* Cyndir - (Awesome) Memory Mapped Dictionary
* Copyright (C) 2010 Jay Freeman (saurik)
*/
/*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
// }}}
#ifndef CYTORE_HPP
#define CYTORE_HPP
#include
#include
#include
#include
#include
#include
#include
#include
#define _assert(test) do \
if (!(test)) { \
fprintf(stderr, "_assert(%d:%s)@%s:%u[%s]\n", errno, #test, __FILE__, __LINE__, __FUNCTION__); \
exit(-1); \
} \
while (false)
namespace Cytore {
static const uint32_t Magic = 'cynd';
struct Header {
uint32_t magic_;
uint32_t version_;
uint32_t size_;
uint32_t reserved_;
} _packed;
template
class Offset {
private:
uint32_t offset_;
public:
Offset() :
offset_(0)
{
}
Offset(uint32_t offset) :
offset_(offset)
{
}
Offset &operator =(uint32_t offset) {
offset_ = offset;
return *this;
}
uint32_t GetOffset() const {
return offset_;
}
bool IsNull() const {
return offset_ == 0;
}
} _packed;
struct Block {
Cytore::Offset reserved_;
} _packed;
template
static _finline Type_ Round(Type_ value, Type_ size) {
Type_ mask(size - 1);
return value + mask & ~mask;
}
template
class File {
private:
static const unsigned Shift_ = 17;
static const size_t Block_ = 1 << Shift_;
static const size_t Mask_ = Block_ - 1;
private:
int file_;
typedef std::vector BlockVector_;
BlockVector_ blocks_;
struct Mapping_ {
uint8_t *data_;
size_t size_;
Mapping_(uint8_t *data, size_t size) :
data_(data),
size_(size)
{
}
};
typedef std::vector MappingVector_;
MappingVector_ maps_;
Header &Header_() {
return *reinterpret_cast(blocks_[0]);
}
uint32_t &Size_() {
return Header_().size_;
}
void Map_(size_t size) {
size_t before(blocks_.size() * Block_);
size_t extend(size - before);
void *data(mmap(NULL, extend, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, file_, before));
_assert(data != MAP_FAILED);
uint8_t *bytes(reinterpret_cast(data));
maps_.push_back(Mapping_(bytes, extend));
for (size_t i(0); i != extend >> Shift_; ++i)
blocks_.push_back(bytes + Block_ * i);
}
bool Truncate_(size_t capacity) {
capacity = Round(capacity, Block_);
int error(ftruncate(file_, capacity));
if (error != 0)
return false;
Map_(capacity);
return true;
}
public:
File() :
file_(-1)
{
}
File(const char *path) :
file_(-1)
{
Open(path);
}
~File() {
for (typename MappingVector_::const_iterator map(maps_.begin()); map != maps_.end(); ++map)
munmap(map->data_, map->size_);
close(file_);
}
void Sync() {
for (typename MappingVector_::const_iterator map(maps_.begin()); map != maps_.end(); ++map)
msync(map->data_, map->size_, MS_SYNC);
}
size_t Capacity() const {
return blocks_.size() * Block_;
}
void Open(const char *path) {
_assert(file_ == -1);
file_ = open(path, O_RDWR | O_CREAT | O_EXLOCK, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
_assert(file_ != -1);
struct stat stat;
_assert(fstat(file_, &stat) == 0);
size_t core(sizeof(Header) + sizeof(Base_));
size_t size(stat.st_size);
if (size == 0) {
if (!Truncate_(core)) {
unlink(path);
_assert(false);
}
Header_().magic_ = Magic;
Size_() = core;
} else {
_assert(size >= core);
// XXX: this involves an unneccessary call to ftruncate()
_assert(Truncate_(size));
_assert(Header_().magic_ == Magic);
_assert(Header_().version_ == 0);
}
}
bool Reserve(size_t capacity) {
if (capacity <= Capacity())
return true;
uint8_t *block(blocks_.back());
blocks_.pop_back();
if (Truncate_(capacity))
return true;
else {
blocks_.push_back(block);
return false;
}
}
template
Target_ &Get(uint32_t offset) {
return *reinterpret_cast(offset == 0 ? NULL : blocks_[offset >> Shift_] + (offset & Mask_));
}
template
Target_ &Get(Offset &ref) {
return Get(ref.GetOffset());
}
Base_ *operator ->() {
return &Get(sizeof(Header));
}
template
Offset New(size_t extra = 0) {
size_t size(sizeof(Target_) + extra);
size = Round(size, sizeof(uintptr_t));
uint32_t offset;
if (!Reserve(Size_() + size))
offset = 0;
else {
offset = Size_();
Size_() += size;
}
return Offset(offset);
}
};
}
#endif//CYTORE_HPP