// Written in the D programming language.
/**
* Read and write memory mapped files.
* Macros:
* WIKI=Phobos/StdMmfile
*
* Copyright: Copyright Digital Mars 2004 - 2009.
* License: Boost License 1.0.
* Authors: $(WEB digitalmars.com, Walter Bright),
* Matthew Wilson
* Source: $(PHOBOSSRC std/_mmfile.d)
*/
/* Copyright Digital Mars 2004 - 2009.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module std.mmfile;
private import std.file;
private import core.stdc.stdio;
private import core.stdc.stdlib;
private import core.stdc.errno;
private import std.path;
private import std.string;
import std.conv, std.exception, std.stdio;
//debug = MMFILE;
version (Windows)
{
private import std.c.windows.windows;
private import std.utf;
}
else version (Posix)
{
private import core.sys.posix.fcntl;
private import core.sys.posix.unistd;
private import core.sys.posix.sys.mman;
private import core.sys.posix.sys.stat;
}
else
{
static assert(0);
}
/**
* MmFile objects control the memory mapped file resource.
*/
class MmFile
{
/**
* The mode the memory mapped file is opened with.
*/
enum Mode
{
read, /// Read existing file
readWriteNew, /// Delete existing file, write new file
readWrite, /// Read/Write existing file, create if not existing
readCopyOnWrite, /// Read/Write existing file, copy on write
}
/**
* Open memory mapped file filename for reading.
* File is closed when the object instance is deleted.
* Throws:
* std.file.FileException
*/
this(string filename)
{
this(filename, Mode.read, 0, null);
}
version(linux) this(File file, Mode mode = Mode.read, ulong size = 0,
void* address = null, size_t window = 0)
{
this(file.fileno(), mode, size, address, window);
}
version(linux) private this(int fildes, Mode mode, ulong size,
void* address, size_t window)
{
int oflag;
int fmode;
switch (mode)
{
case Mode.read:
flags = MAP_SHARED;
prot = PROT_READ;
oflag = O_RDONLY;
fmode = 0;
break;
case Mode.readWriteNew:
assert(size != 0);
flags = MAP_SHARED;
prot = PROT_READ | PROT_WRITE;
oflag = O_CREAT | O_RDWR | O_TRUNC;
fmode = octal!660;
break;
case Mode.readWrite:
flags = MAP_SHARED;
prot = PROT_READ | PROT_WRITE;
oflag = O_CREAT | O_RDWR;
fmode = octal!660;
break;
case Mode.readCopyOnWrite:
flags = MAP_PRIVATE;
prot = PROT_READ | PROT_WRITE;
oflag = O_RDWR;
fmode = 0;
break;
default:
assert(0);
}
fd = fildes;
// Adjust size
stat_t statbuf = void;
errnoEnforce(fstat(fd, &statbuf) == 0);
if (prot & PROT_WRITE && size > statbuf.st_size)
{
// Need to make the file size bytes big
lseek(fd, cast(int)(size - 1), SEEK_SET);
char c = 0;
core.sys.posix.unistd.write(fd, &c, 1);
}
else if (prot & PROT_READ && size == 0)
size = statbuf.st_size;
this.size = size;
// Map the file into memory!
size_t initial_map = (window && 2*window>32);
hFileMap = CreateFileMappingA(hFile, null, flProtect,
hi, cast(uint)size, null);
if (hFileMap == null) // mapping failed
goto err1;
if (size == 0)
{
uint sizehi;
uint sizelow = GetFileSize(hFile,&sizehi);
size = (cast(ulong)sizehi << 32) + sizelow;
}
this.size = size;
size_t initial_map = (window && 2*window statbuf.st_size)
{
// Need to make the file size bytes big
.lseek(fd, cast(int)(size - 1), SEEK_SET);
char c = 0;
core.sys.posix.unistd.write(fd, &c, 1);
}
else if (prot & PROT_READ && size == 0)
size = statbuf.st_size;
}
else
{
fd = -1;
flags |= MAP_ANON;
}
this.size = size;
size_t initial_map = (window && 2*window= start && i < start+data.length;
}
// unmap the current range
private void unmap()
{
debug (MMFILE) printf("MmFile.unmap()\n");
version(Windows) {
errnoEnforce(!data || UnmapViewOfFile(data.ptr) != FALSE);
} else {
errnoEnforce(!data || munmap(cast(void*)data, data.length) == 0,
"munmap failed");
}
data = null;
}
// map range
private void map(ulong start, size_t len)
{
debug (MMFILE) printf("MmFile.map(%lld, %d)\n", start, len);
void* p;
if (start+len > size)
len = cast(size_t)(size-start);
version(Windows) {
uint hi = cast(uint)(start>>32);
p = MapViewOfFileEx(hFileMap, dwDesiredAccess, hi, cast(uint)start, len, address);
errnoEnforce(p);
} else {
p = mmap(address, len, prot, flags, fd, cast(int)start);
errnoEnforce(p != MAP_FAILED);
}
data = p[0 .. len];
this.start = start;
}
// ensure a given position is mapped
private void ensureMapped(ulong i)
{
debug (MMFILE) printf("MmFile.ensureMapped(%lld)\n", i);
if (!mapped(i)) {
unmap();
if (window == 0) {
map(0,cast(size_t)size);
} else {
ulong block = i/window;
if (block == 0)
map(0,2*window);
else
map(window*(block-1),3*window);
}
}
}
// ensure a given range is mapped
private void ensureMapped(ulong i, ulong j)
{
debug (MMFILE) printf("MmFile.ensureMapped(%lld, %lld)\n", i, j);
if (!mapped(i) || !mapped(j-1)) {
unmap();
if (window == 0) {
map(0,cast(size_t)size);
} else {
ulong iblock = i/window;
ulong jblock = (j-1)/window;
if (iblock == 0) {
map(0,cast(size_t)(window*(jblock+2)));
} else {
map(window*(iblock-1),cast(size_t)(window*(jblock-iblock+3)));
}
}
}
}
private:
string filename;
void[] data;
ulong start;
size_t window;
ulong size;
Mode mMode;
void* address;
version (Windows)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hFileMap = null;
uint dwDesiredAccess;
}
else version (Posix)
{
int fd;
int prot;
int flags;
int fmode;
}
else
{
static assert(0);
}
// Report error, where errno gives the error number
// void errNo()
// {
// version (Windows)
// {
// throw new FileException(filename, GetLastError());
// }
// else version (linux)
// {
// throw new FileException(filename, errno);
// }
// else
// {
// static assert(0);
// }
// }
}
unittest
{
const size_t K = 1024;
size_t win = 64*K; // assume the page size is 64K
version(Windows) {
/+ these aren't defined in std.c.windows.windows so let's use default
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
win = sysinfo.dwAllocationGranularity;
+/
} else version (linux) {
// getpagesize() is not defined in the unix D headers so use the guess
}
MmFile mf = new MmFile("testing.txt",MmFile.Mode.readWriteNew,
100*K,null,win);
ubyte[] str = cast(ubyte[])"1234567890";
ubyte[] data = cast(ubyte[])mf[0 .. 10];
data[] = str[];
assert( mf[0 .. 10] == str );
data = cast(ubyte[])mf[50 .. 60];
data[] = str[];
assert( mf[50 .. 60] == str );
ubyte[] data2 = cast(ubyte[])mf[20*K .. 60*K];
assert( data2.length == 40*K );
assert( data2[$-1] == 0 );
mf[100*K-1] = cast(ubyte)'b';
data2 = cast(ubyte[])mf[21*K .. 100*K];
assert( data2.length == 79*K );
assert( data2[$-1] == 'b' );
delete mf;
std.file.remove("testing.txt");
// Create anonymous mapping
auto test = new MmFile(null, MmFile.Mode.readWriteNew, 1024*1024, null);
}