|
|
nono 1.5.0
//
// nono
// Copyright (C) 2020 nono project
// Licensed under nono-license.txt
//
//
// ファイルを mmap して提供するクラス
//
#include "mappedfile.h"
#include "mystring.h"
#include <vector>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
// コンストラクタ
MappedFile::MappedFile()
{
}
// デストラクタ
MappedFile::~MappedFile()
{
Close();
}
// ファイル名を指定する。
// filename_ が空でないことは呼び出し側で保証すること。
void
MappedFile::SetFilename(const std::string& filename_)
{
filename = filename_;
assert(!filename.empty());
// 表示名を更新
SetDispname(string_format("\"%s\"", filename.c_str()));
}
// 表示名を設定する。
// デフォルトのから変えたい時にどうぞ。
void
MappedFile::SetDispname(const std::string& name)
{
dispname = name;
}
// サイズが len で確定しているファイルを読み書き両用でオープンして mmap する。
// ファイルがなければ作成して、あれば data で初期化する。これは SRAM とか用。
// 成功すれば mmap した領域のポインタを返す。
// 失敗すればエラーメッセージを表示して NULL を返す。
uint8 *
MappedFile::OpenCreate(off_t memlen_, const uint8 *data, uint datalen)
{
void *m;
memlen = memlen_;
fd = open(filename.c_str(), O_RDWR);
if (fd < 0) {
if (errno != ENOENT) {
warn("%s open failed", dispname.c_str());
return NULL;
} else {
// オープンできない理由が ENOENT ならファイルを作ってみる
fd = open(filename.c_str(), O_RDWR | O_CREAT, 0644);
if (fd < 0) {
warn("%s create failed", dispname.c_str());
return NULL;
}
uint written = 0;
// data, datalen が指定されていれば先頭から書き込む。
if (data != NULL && datalen > 0) {
if (write(fd, data, datalen) < 0) {
warn("%s write failed", dispname.c_str());
goto abort;
}
written = datalen;
}
// (残りを)ゼロで埋める。
std::vector<char> zerobuf(memlen - written);
if (write(fd, zerobuf.data(), zerobuf.size()) < 0) {
warn("%s clear failed", dispname.c_str());
goto abort;
}
warnx("%s created", dispname.c_str());
// FALLTHROUGH
}
}
if (CheckFilesize() == false) {
goto abort;
}
// mmap する。
m = mmap(NULL, memlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (m == MAP_FAILED) {
warn("%s mmap failed", dispname.c_str());
goto abort;
}
mem = (uint8 *)m;
return mem;
abort:
Close();
return NULL;
}
// ファイルサイズが memlen と同じかどうか調べる。
// ファイルサイズが memlen より短ければエラーメッセージを表示し false を返す。
// ファイルサイズが memlen より長ければワーニングを表示し true を返す。
// ファイルサイズが memlen と同じなら true を返す。
bool
MappedFile::CheckFilesize() const
{
struct stat st;
// ファイルサイズをチェック。
// ファイルが短くても mmap は黙って成功してしまうようなので。
if (fstat(fd, &st) < 0) {
warn("%s fstat failed", dispname.c_str());
return false;
}
if (st.st_size < memlen) {
warnx("%s is too short (filesize is expected %jd but %jd)",
dispname.c_str(), (intmax_t)memlen, (intmax_t)st.st_size);
return false;
}
if (st.st_size > memlen) {
// 大きすぎるほうはワーニングだけでいいか
warnx("%s is too large (ignore %jd bytes)",
dispname.c_str(), (intmax_t)(st.st_size - memlen));
}
return true;
}
// ファイルをクローズする。
// 呼び出しに副作用はない。
void
MappedFile::Close()
{
if (mem != NULL) {
munmap(mem, memlen);
mem = NULL;
memlen = 0;
}
fd.Close();
filename.clear();
dispname.clear();
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.