mirror of
https://github.com/dlang/phobos.git
synced 2025-04-29 14:40:30 +03:00
410 lines
8.1 KiB
D
410 lines
8.1 KiB
D
// Copyright (c) 2004 by Digital Mars
|
|
// All Rights Reserved
|
|
// written by Walter Bright and Matthew Wilson (Sysesis Software Pty Ltd.)
|
|
// www.digitalmars.com
|
|
// www.synesis.com.au/software
|
|
|
|
/*
|
|
* Memory mapped files.
|
|
*/
|
|
|
|
module mmfile;
|
|
|
|
private import std.file;
|
|
private import std.c.stdio;
|
|
private import std.c.stdlib;
|
|
private import std.path;
|
|
private import std.string;
|
|
|
|
//debug = MMFILE;
|
|
|
|
version (Win32)
|
|
{
|
|
private import std.c.windows.windows;
|
|
private import std.utf;
|
|
|
|
private int useWfuncs = 1;
|
|
private uint dwVersion;
|
|
|
|
static this()
|
|
{ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/getversion.asp
|
|
dwVersion = GetVersion();
|
|
|
|
// Win 95, 98, ME do not implement the W functions
|
|
useWfuncs = (dwVersion < 0x80000000);
|
|
}
|
|
}
|
|
else version (linux)
|
|
{
|
|
private import std.c.linux.linux;
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
|
|
|
|
auto class MmFile
|
|
{
|
|
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 for reading
|
|
*/
|
|
this(char[] filename)
|
|
{
|
|
this(filename, Mode.Read, 0, null);
|
|
}
|
|
|
|
/* Open
|
|
*/
|
|
this(char[] filename, Mode mode, size_t size, void* address)
|
|
{
|
|
this.filename = filename;
|
|
|
|
version (Win32)
|
|
{
|
|
void* p;
|
|
uint dwDesiredAccess2;
|
|
uint dwShareMode;
|
|
uint dwCreationDisposition;
|
|
uint dwDesiredAccess;
|
|
uint flProtect;
|
|
|
|
if (dwVersion & 0x80000000 && (dwVersion & 0xFF) == 3)
|
|
{
|
|
throw new FileException(filename,
|
|
"Win32s does not implement mm files");
|
|
}
|
|
|
|
switch (mode)
|
|
{
|
|
case Mode.Read:
|
|
dwDesiredAccess2 = GENERIC_READ;
|
|
dwShareMode = FILE_SHARE_READ;
|
|
dwCreationDisposition = OPEN_EXISTING;
|
|
flProtect = PAGE_READONLY;
|
|
dwDesiredAccess = FILE_MAP_READ;
|
|
break;
|
|
|
|
case Mode.ReadWriteNew:
|
|
assert(size != 0);
|
|
dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE;
|
|
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
dwCreationDisposition = CREATE_ALWAYS;
|
|
flProtect = PAGE_READWRITE;
|
|
dwDesiredAccess = FILE_MAP_WRITE;
|
|
break;
|
|
|
|
case Mode.ReadWrite:
|
|
dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE;
|
|
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
dwCreationDisposition = OPEN_ALWAYS;
|
|
flProtect = PAGE_READWRITE;
|
|
dwDesiredAccess = FILE_MAP_WRITE;
|
|
break;
|
|
|
|
case Mode.ReadCopyOnWrite:
|
|
if (dwVersion & 0x80000000)
|
|
{
|
|
throw new FileException(filename,
|
|
"Win9x does not implement copy on write");
|
|
}
|
|
dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE;
|
|
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
dwCreationDisposition = OPEN_EXISTING;
|
|
flProtect = PAGE_WRITECOPY;
|
|
dwDesiredAccess = FILE_MAP_COPY;
|
|
break;
|
|
}
|
|
|
|
if (useWfuncs)
|
|
{
|
|
wchar* namez = std.utf.toUTF16z(filename);
|
|
hFile = CreateFileW(namez,
|
|
dwDesiredAccess2,
|
|
dwShareMode,
|
|
null,
|
|
dwCreationDisposition,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
cast(HANDLE)null);
|
|
}
|
|
else
|
|
{
|
|
char* namez = std.file.toMBSz(filename);
|
|
hFile = CreateFileA(namez,
|
|
dwDesiredAccess2,
|
|
dwShareMode,
|
|
null,
|
|
dwCreationDisposition,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
cast(HANDLE)null);
|
|
}
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
goto err1;
|
|
|
|
hFileMap = CreateFileMappingA(hFile, null, flProtect, 0, size, null);
|
|
if (hFileMap == null) // mapping failed
|
|
goto err1;
|
|
|
|
p = MapViewOfFileEx(hFileMap, dwDesiredAccess, 0, 0, size, address);
|
|
if (p == null) // mapping view failed
|
|
{
|
|
goto err1;
|
|
}
|
|
if (size == 0)
|
|
size = GetFileSize(hFile, null);
|
|
|
|
debug (MMFILE) printf("MmFile.this(): p = %p, size = %d\n", p, size);
|
|
data = p[0 .. size];
|
|
return;
|
|
|
|
err1:
|
|
if (hFileMap != null)
|
|
CloseHandle(hFileMap);
|
|
hFileMap = null;
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
errNo();
|
|
}
|
|
else version (linux)
|
|
{
|
|
char* namez = toStringz(filename);
|
|
void* p;
|
|
int fd;
|
|
int prot;
|
|
int flags;
|
|
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 = 0660;
|
|
break;
|
|
|
|
case Mode.ReadWrite:
|
|
flags = MAP_SHARED;
|
|
prot = PROT_READ | PROT_WRITE;
|
|
oflag = O_CREAT | O_RDWR;
|
|
fmode = 0660;
|
|
break;
|
|
|
|
case Mode.ReadCopyOnWrite:
|
|
flags = MAP_PRIVATE;
|
|
prot = PROT_READ | PROT_WRITE;
|
|
oflag = O_RDWR;
|
|
fmode = 0;
|
|
break;
|
|
}
|
|
|
|
if (filename.length)
|
|
{
|
|
struct_stat statbuf;
|
|
|
|
fd = std.c.linux.linux.open(namez, oflag, fmode);
|
|
if (fd == -1)
|
|
{
|
|
printf("\topen error, errno = %d\n",getErrno());
|
|
errNo();
|
|
}
|
|
|
|
if (std.c.linux.linux.fstat(fd, &statbuf))
|
|
{
|
|
//printf("\tfstat error, errno = %d\n",getErrno());
|
|
std.c.linux.linux.close(fd);
|
|
errNo();
|
|
}
|
|
|
|
if (prot & PROT_WRITE && size > statbuf.st_size)
|
|
{
|
|
// Need to make the file size bytes big
|
|
std.c.linux.linux.lseek(fd, size - 1, SEEK_SET);
|
|
char c = 0;
|
|
std.c.linux.linux.write(fd, &c, 1);
|
|
}
|
|
else if (prot & PROT_READ && size == 0)
|
|
size = statbuf.st_size;
|
|
}
|
|
else
|
|
{
|
|
fd = -1;
|
|
flags |= MAP_ANONYMOUS;
|
|
}
|
|
|
|
p = mmap(address, size, prot, flags, fd, 0);
|
|
//printf(" p = %x, size = %d\n", p, size);
|
|
|
|
/* Memory mapping stays active even if we close the handle.
|
|
* Closing it now avoids worrys about closing it during error
|
|
* recovery.
|
|
*/
|
|
if (fd != -1 && std.c.linux.linux.close(fd) == -1)
|
|
errNo();
|
|
|
|
if (p == MAP_FAILED) // in sys/mman.h
|
|
errNo();
|
|
data = p[0 .. size];
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
}
|
|
|
|
~this()
|
|
{
|
|
debug (MMFILE) printf("MmFile.~this()\n");
|
|
version (Win32)
|
|
{
|
|
/* Note that under Windows 95, UnmapViewOfFile() seems to return
|
|
* random values, not TRUE or FALSE.
|
|
*/
|
|
if (data && UnmapViewOfFile(data) == FALSE &&
|
|
(dwVersion & 0x80000000) == 0)
|
|
errNo();
|
|
data = null;
|
|
|
|
if (hFileMap != null && CloseHandle(hFileMap) != TRUE)
|
|
errNo();
|
|
hFileMap = null;
|
|
|
|
if (hFile != INVALID_HANDLE_VALUE && CloseHandle(hFile) != TRUE)
|
|
errNo();
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
else version (linux)
|
|
{
|
|
int i;
|
|
|
|
i = munmap(cast(void*)data, data.length);
|
|
if (i != 0)
|
|
errNo();
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
data = null;
|
|
}
|
|
|
|
/* Flush any pending output.
|
|
*/
|
|
void flush()
|
|
{
|
|
debug (MMFILE) printf("MmFile.flush()\n");
|
|
version (Win32)
|
|
{
|
|
FlushViewOfFile(data, data.length);
|
|
}
|
|
else version (linux)
|
|
{
|
|
int i;
|
|
|
|
i = msync(cast(void*)data, data.length, MS_SYNC); // sys/mman.h
|
|
if (i != 0)
|
|
errNo();
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
}
|
|
|
|
size_t length()
|
|
{
|
|
debug (MMFILE) printf("MmFile.length()\n");
|
|
return data.length;
|
|
}
|
|
|
|
void[] opSlice()
|
|
{
|
|
debug (MMFILE) printf("MmFile.opSlice()\n");
|
|
return data;
|
|
}
|
|
|
|
void[] opSlice(size_t i1, size_t i2)
|
|
{
|
|
debug (MMFILE) printf("MmFile.opSlice(%d, %d)\n", i1, i2);
|
|
return data[i1 .. i2];
|
|
}
|
|
|
|
ubyte opIndex(size_t i)
|
|
{
|
|
debug (MMFILE) printf("MmFile.opIndex(%d)\n", i);
|
|
return (cast(ubyte[])data)[i];
|
|
}
|
|
|
|
ubyte opIndex(size_t i, ubyte value)
|
|
{
|
|
debug (MMFILE) printf("MmFile.opIndex(%d, %d)\n", i, value);
|
|
return (cast(ubyte[])data)[i] = value;
|
|
}
|
|
|
|
|
|
private:
|
|
char[] filename;
|
|
void[] data;
|
|
|
|
version (Win32)
|
|
{
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
HANDLE hFileMap = null;
|
|
}
|
|
else version (linux)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
|
|
// Report error, where errno gives the error number
|
|
void errNo()
|
|
{
|
|
version (Win32)
|
|
{
|
|
throw new FileException(filename, GetLastError());
|
|
}
|
|
else version (linux)
|
|
{
|
|
throw new FileException(filename, getErrno());
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
version (Win32)
|
|
{
|
|
}
|
|
else version (linux)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
*/
|
|
|