File:  [Isaki's NoNo m68k/m88k emulator] / nono / lib / mappedfile.cpp
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:48 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, v026, v025, HEAD
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();
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.