/* This file is part of Jellyfish.
Jellyfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jellyfish 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jellyfish. If not, see .
*/
#ifndef __JELLYFISH_MAPPED_FILE_HPP__
#define __JELLYFISH_MAPPED_FILE_HPP__
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace jellyfish {
class mapped_file {
protected:
std::string _path;
char *_base, *_end;
size_t _length;
void map_(int fd) {
struct stat stat;
if(fstat(fd, &stat) < 0)
throw ErrorMMap(err::msg() << "Can't stat file '" << _path << "'" << err::no);
_length = stat.st_size;
_base = (char*)mmap(NULL, _length, PROT_READ, MAP_SHARED, fd, 0);
if(_base == MAP_FAILED) {
_base = 0;
throw ErrorMMap(err::msg() << "Can't mmap file '" << _path << "'" << err::no);
}
_end = _base + _length;
}
void map_(const char *filename) {
int fd = open(filename, O_RDONLY);
if(fd < 0)
throw ErrorMMap(err::msg() << "Can't open file '" << filename << "'" << err::no);
map_(fd);
close(fd);
}
public:
define_error_class(ErrorMMap);
mapped_file() : _path(), _base(0), _end(0), _length(0) { }
explicit mapped_file(const char *filename)
: _path(filename), _base(0), _end(0), _length(0)
{
map_(filename);
}
explicit mapped_file(int fd)
: _path(), _base(0), _end(0), _length()
{
map_(fd);
}
mapped_file(mapped_file&& rhs)
: _path(std::move(rhs._path)), _base(rhs._base), _end(rhs._end),
_length(rhs._length)
{
rhs._base = 0;
}
~mapped_file() {
unmap();
}
void map(const char* filename) {
unmap();
map_(filename);
}
void map(int fd) {
unmap();
map_(fd);
}
void unmap() {
if(!_base)
return;
munmap(_base, _length);
_path.clear();
_base = 0;
_length = 0;
}
mapped_file& operator=(mapped_file&& rhs) {
_path = std::move(rhs._path);
_base = rhs._base;
rhs._base = 0;
_end = rhs._end;
_length = rhs._length;
return *this;
}
void swap(mapped_file& rhs) {
std::swap(_path, rhs._path);
std::swap(_base, rhs._base);
std::swap(_end, rhs._end);
std::swap(_length, rhs._length);
}
char *base() const { return _base; }
char *end() const { return _end; }
size_t length() const { return _length; }
std::string path() const { return _path; }
// No error checking here. Should I throw something?
const mapped_file & will_need() const {
madvise(_base, _length, MADV_WILLNEED);
return *this;
}
const mapped_file & sequential() const {
madvise(_base, _length, MADV_SEQUENTIAL);
return *this;
}
const mapped_file & random() const {
madvise(_base, _length, MADV_RANDOM);
return *this;
}
const mapped_file & lock() const {
if(mlock(_base, _length) < 0)
throw ErrorMMap(err::msg() << "Can't lock map in memory" << err::no);
return *this;
}
char load() const {
const long sz = sysconf(_SC_PAGESIZE);
// Do not optimize. Side effect is that every page is accessed and
// should now be in cache.
volatile char unused = 0;
for(const char *w = _base; w < _base + _length; w += sz)
unused ^= *w;
return unused;
}
};
inline void swap(mapped_file& a, mapped_file& b) { a.swap(b); }
// class mapped_files_t : public std::vector {
// public:
// mapped_files_t(int nb_files, char *argv[]) {
// for(int j = 0; j < nb_files; j++)
// push_back(mapped_file(argv[j]));
// }
// mapped_files_t(int nb_files, char *argv[], bool sequential) {
// for(int j = 0; j < nb_files; j++) {
// push_back(mapped_file(argv[j]));
// if(sequential)
// end()->sequential();
// }
// }
// };
// // File mapped on demand.
// class lazy_mapped_file_t : public mapped_file {
// std::string _path;
// volatile bool done;
// volatile long used_counter;
// public:
// explicit lazy_mapped_file_t(const char *path) :
// mapped_file((char *)0, (size_t)0),
// _path(path), done(false), used_counter(0) {}
// void map() {
// used_counter = 1;
// done = false;
// mapped_file::map(_path.c_str());
// }
// void unmap() {
// done = true;
// dec();
// }
// void inc() {
// atomic::gcc::fetch_add(&used_counter, (long)1);
// }
// void dec() {
// long val = atomic::gcc::add_fetch(&used_counter, (long)-1);
// if(done && val == 0)
// mapped_file::unmap();
// }
// };
// class lazy_mapped_files_t : public std::vector {
// public:
// lazy_mapped_files_t(int nb_files, char *argv[]) {
// for(int j = 0; j < nb_files; j++)
// push_back(lazy_mapped_file_t(argv[j]));
// }
// lazy_mapped_files_t(int nb_files, char *argv[], bool sequential) {
// for(int j = 0; j < nb_files; j++) {
// push_back(lazy_mapped_file_t(argv[j]));
// if(sequential)
// end()->sequential();
// }
// }
// };
}
#endif