mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 21:51:40 +03:00
819 lines
23 KiB
D
819 lines
23 KiB
D
// Written in the D programming language.
|
|
|
|
/**
|
|
* Read and write memory mapped files.
|
|
* Copyright: Copyright The D Language Foundation 2004 - 2009.
|
|
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
|
|
* Authors: $(HTTP digitalmars.com, Walter Bright),
|
|
* Matthew Wilson
|
|
* Source: $(PHOBOSSRC std/mmfile.d)
|
|
*
|
|
* $(SCRIPT inhibitQuickIndex = 1;)
|
|
*/
|
|
/* Copyright The D Language Foundation 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;
|
|
|
|
import core.stdc.errno;
|
|
import core.stdc.stdio;
|
|
import core.stdc.stdlib;
|
|
import std.conv, std.exception, std.stdio;
|
|
import std.file;
|
|
import std.path;
|
|
import std.string;
|
|
|
|
import std.internal.cstring;
|
|
|
|
//debug = MMFILE;
|
|
|
|
version (Windows)
|
|
{
|
|
import core.sys.windows.winbase;
|
|
import core.sys.windows.winnt;
|
|
import std.utf;
|
|
import std.windows.syserror;
|
|
}
|
|
else version (Posix)
|
|
{
|
|
import core.sys.posix.fcntl;
|
|
import core.sys.posix.sys.mman;
|
|
import core.sys.posix.sys.stat;
|
|
import core.sys.posix.unistd;
|
|
}
|
|
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:
|
|
* - On POSIX, $(REF ErrnoException, std, exception).
|
|
* - On Windows, $(REF WindowsException, std, windows, syserror).
|
|
*/
|
|
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)
|
|
{
|
|
// Save a copy of the File to make sure the fd stays open.
|
|
this.file = file;
|
|
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;
|
|
|
|
final 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;
|
|
}
|
|
|
|
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(off_t)(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<size)
|
|
? 2*window : cast(size_t) size;
|
|
auto p = mmap(address, initial_map, prot, flags, fd, 0);
|
|
if (p == MAP_FAILED)
|
|
{
|
|
errnoEnforce(false, "Could not map file into memory");
|
|
}
|
|
data = p[0 .. initial_map];
|
|
}
|
|
|
|
/**
|
|
* Open memory mapped file filename in mode.
|
|
* File is closed when the object instance is deleted.
|
|
* Params:
|
|
* filename = name of the file.
|
|
* If null, an anonymous file mapping is created.
|
|
* mode = access mode defined above.
|
|
* size = the size of the file. If 0, it is taken to be the
|
|
* size of the existing file.
|
|
* address = the preferred address to map the file to,
|
|
* although the system is not required to honor it.
|
|
* If null, the system selects the most convenient address.
|
|
* window = preferred block size of the amount of data to map at one time
|
|
* with 0 meaning map the entire file. The window size must be a
|
|
* multiple of the memory allocation page size.
|
|
* Throws:
|
|
* - On POSIX, $(REF ErrnoException, std, exception).
|
|
* - On Windows, $(REF WindowsException, std, windows, syserror).
|
|
*/
|
|
this(string filename, Mode mode, ulong size, void* address,
|
|
size_t window = 0)
|
|
{
|
|
this.filename = filename;
|
|
this.mMode = mode;
|
|
this.window = window;
|
|
this.address = address;
|
|
|
|
version (Windows)
|
|
{
|
|
void* p;
|
|
uint dwDesiredAccess2;
|
|
uint dwShareMode;
|
|
uint dwCreationDisposition;
|
|
uint flProtect;
|
|
|
|
final 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:
|
|
dwDesiredAccess2 = GENERIC_READ | GENERIC_WRITE;
|
|
dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
dwCreationDisposition = OPEN_EXISTING;
|
|
flProtect = PAGE_WRITECOPY;
|
|
dwDesiredAccess = FILE_MAP_COPY;
|
|
break;
|
|
}
|
|
|
|
if (filename != null)
|
|
{
|
|
hFile = CreateFileW(filename.tempCStringW(),
|
|
dwDesiredAccess2,
|
|
dwShareMode,
|
|
null,
|
|
dwCreationDisposition,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
cast(HANDLE) null);
|
|
wenforce(hFile != INVALID_HANDLE_VALUE, "CreateFileW");
|
|
}
|
|
else
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
scope(failure)
|
|
{
|
|
if (hFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
int hi = cast(int)(size >> 32);
|
|
hFileMap = CreateFileMappingW(hFile, null, flProtect,
|
|
hi, cast(uint) size, null);
|
|
wenforce(hFileMap, "CreateFileMapping");
|
|
scope(failure)
|
|
{
|
|
CloseHandle(hFileMap);
|
|
hFileMap = null;
|
|
}
|
|
|
|
if (size == 0 && filename != null)
|
|
{
|
|
uint sizehi;
|
|
uint sizelow = GetFileSize(hFile, &sizehi);
|
|
wenforce(sizelow != INVALID_FILE_SIZE || GetLastError() != ERROR_SUCCESS,
|
|
"GetFileSize");
|
|
size = (cast(ulong) sizehi << 32) + sizelow;
|
|
}
|
|
this.size = size;
|
|
|
|
size_t initial_map = (window && 2*window<size)
|
|
? 2*window : cast(size_t) size;
|
|
p = MapViewOfFileEx(hFileMap, dwDesiredAccess, 0, 0,
|
|
initial_map, address);
|
|
wenforce(p, "MapViewOfFileEx");
|
|
data = p[0 .. initial_map];
|
|
|
|
debug (MMFILE) printf("MmFile.this(): p = %p, size = %d\n", p, size);
|
|
}
|
|
else version (Posix)
|
|
{
|
|
void* p;
|
|
int oflag;
|
|
int fmode;
|
|
|
|
final 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;
|
|
}
|
|
|
|
if (filename.length)
|
|
{
|
|
fd = .open(filename.tempCString(), oflag, fmode);
|
|
errnoEnforce(fd != -1, "Could not open file "~filename);
|
|
|
|
stat_t statbuf;
|
|
if (fstat(fd, &statbuf))
|
|
{
|
|
//printf("\tfstat error, errno = %d\n", errno);
|
|
.close(fd);
|
|
fd = -1;
|
|
errnoEnforce(false, "Could not stat file "~filename);
|
|
}
|
|
|
|
if (prot & PROT_WRITE && size > statbuf.st_size)
|
|
{
|
|
// Need to make the file size bytes big
|
|
.lseek(fd, cast(off_t)(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<size)
|
|
? 2*window : cast(size_t) size;
|
|
p = mmap(address, initial_map, prot, flags, fd, 0);
|
|
if (p == MAP_FAILED)
|
|
{
|
|
if (fd != -1)
|
|
{
|
|
.close(fd);
|
|
fd = -1;
|
|
}
|
|
errnoEnforce(false, "Could not map file "~filename);
|
|
}
|
|
|
|
data = p[0 .. initial_map];
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Flushes pending output and closes the memory mapped file.
|
|
*/
|
|
~this()
|
|
{
|
|
debug (MMFILE) printf("MmFile.~this()\n");
|
|
unmap();
|
|
data = null;
|
|
version (Windows)
|
|
{
|
|
wenforce(hFileMap == null || CloseHandle(hFileMap) == TRUE,
|
|
"Could not close file handle");
|
|
hFileMap = null;
|
|
|
|
wenforce(!hFile || hFile == INVALID_HANDLE_VALUE
|
|
|| CloseHandle(hFile) == TRUE,
|
|
"Could not close handle");
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
else version (Posix)
|
|
{
|
|
version (linux)
|
|
{
|
|
if (file !is File.init)
|
|
{
|
|
// The File destructor will close the file,
|
|
// if it is the only remaining reference.
|
|
return;
|
|
}
|
|
}
|
|
errnoEnforce(fd == -1 || fd <= 2
|
|
|| .close(fd) != -1,
|
|
"Could not close handle");
|
|
fd = -1;
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
}
|
|
|
|
/* Flush any pending output.
|
|
*/
|
|
void flush()
|
|
{
|
|
debug (MMFILE) printf("MmFile.flush()\n");
|
|
version (Windows)
|
|
{
|
|
FlushViewOfFile(data.ptr, data.length);
|
|
}
|
|
else version (Posix)
|
|
{
|
|
int i;
|
|
i = msync(cast(void*) data, data.length, MS_SYNC); // sys/mman.h
|
|
errnoEnforce(i == 0, "msync failed");
|
|
}
|
|
else
|
|
{
|
|
static assert(0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gives size in bytes of the memory mapped file.
|
|
*/
|
|
@property ulong length() const
|
|
{
|
|
debug (MMFILE) printf("MmFile.length()\n");
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Forwards `length`.
|
|
*/
|
|
alias opDollar = length;
|
|
|
|
/**
|
|
* Read-only property returning the file mode.
|
|
*/
|
|
Mode mode()
|
|
{
|
|
debug (MMFILE) printf("MmFile.mode()\n");
|
|
return mMode;
|
|
}
|
|
|
|
/**
|
|
* Returns entire file contents as an array.
|
|
*/
|
|
void[] opSlice()
|
|
{
|
|
debug (MMFILE) printf("MmFile.opSlice()\n");
|
|
return opSlice(0,size);
|
|
}
|
|
|
|
/**
|
|
* Returns slice of file contents as an array.
|
|
*/
|
|
void[] opSlice(ulong i1, ulong i2)
|
|
{
|
|
debug (MMFILE) printf("MmFile.opSlice(%lld, %lld)\n", i1, i2);
|
|
ensureMapped(i1,i2);
|
|
size_t off1 = cast(size_t)(i1-start);
|
|
size_t off2 = cast(size_t)(i2-start);
|
|
return data[off1 .. off2];
|
|
}
|
|
|
|
/**
|
|
* Returns byte at index i in file.
|
|
*/
|
|
ubyte opIndex(ulong i)
|
|
{
|
|
debug (MMFILE) printf("MmFile.opIndex(%lld)\n", i);
|
|
ensureMapped(i);
|
|
size_t off = cast(size_t)(i-start);
|
|
return (cast(ubyte[]) data)[off];
|
|
}
|
|
|
|
/**
|
|
* Sets and returns byte at index i in file to value.
|
|
*/
|
|
ubyte opIndexAssign(ubyte value, ulong i)
|
|
{
|
|
debug (MMFILE) printf("MmFile.opIndex(%lld, %d)\n", i, value);
|
|
ensureMapped(i);
|
|
size_t off = cast(size_t)(i-start);
|
|
return (cast(ubyte[]) data)[off] = value;
|
|
}
|
|
|
|
|
|
// return true if the given position is currently mapped
|
|
private int mapped(ulong i)
|
|
{
|
|
debug (MMFILE) printf("MmFile.mapped(%lld, %lld, %d)\n", i,start,
|
|
data.length);
|
|
return i >= start && i < start+data.length;
|
|
}
|
|
|
|
// unmap the current range
|
|
private void unmap()
|
|
{
|
|
debug (MMFILE) printf("MmFile.unmap()\n");
|
|
version (Windows)
|
|
{
|
|
wenforce(!data.ptr || UnmapViewOfFile(data.ptr) != FALSE, "UnmapViewOfFile");
|
|
}
|
|
else
|
|
{
|
|
errnoEnforce(!data.ptr || 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);
|
|
wenforce(p, "MapViewOfFileEx");
|
|
}
|
|
else
|
|
{
|
|
p = mmap(address, len, prot, flags, fd, cast(off_t) 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 (linux) File file;
|
|
|
|
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);
|
|
// }
|
|
// }
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
import core.memory : GC;
|
|
import std.file : deleteme;
|
|
|
|
const size_t K = 1024;
|
|
size_t win = 64*K; // assume the page size is 64K
|
|
version (Windows)
|
|
{
|
|
/+ these aren't defined in core.sys.windows.windows so let's use default
|
|
SYSTEM_INFO sysinfo;
|
|
GetSystemInfo(&sysinfo);
|
|
win = sysinfo.dwAllocationGranularity;
|
|
+/
|
|
}
|
|
else version (Posix)
|
|
{
|
|
import core.sys.posix.unistd;
|
|
win = cast(size_t) sysconf(_SC_PAGESIZE);
|
|
}
|
|
string test_file = std.file.deleteme ~ "-testing.txt";
|
|
MmFile mf = new MmFile(test_file,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' );
|
|
|
|
destroy(mf);
|
|
|
|
std.file.remove(test_file);
|
|
// Create anonymous mapping
|
|
auto test = new MmFile(null, MmFile.Mode.readWriteNew, 1024*1024, null);
|
|
}
|
|
|
|
version (linux)
|
|
@system unittest // https://issues.dlang.org/show_bug.cgi?id=14868
|
|
{
|
|
import std.file : deleteme;
|
|
import std.typecons : scoped;
|
|
|
|
// Test retaining ownership of File/fd
|
|
|
|
auto fn = std.file.deleteme ~ "-testing.txt";
|
|
scope(exit) std.file.remove(fn);
|
|
File(fn, "wb").writeln("Testing!");
|
|
scoped!MmFile(File(fn));
|
|
|
|
// Test that unique ownership of File actually leads to the fd being closed
|
|
|
|
auto f = File(fn);
|
|
auto fd = f.fileno;
|
|
{
|
|
auto mf = scoped!MmFile(f);
|
|
f = File.init;
|
|
}
|
|
assert(.close(fd) == -1);
|
|
}
|
|
|
|
// https://issues.dlang.org/show_bug.cgi?id=14994
|
|
// https://issues.dlang.org/show_bug.cgi?id=14995
|
|
@system unittest
|
|
{
|
|
import std.file : deleteme;
|
|
import std.typecons : scoped;
|
|
|
|
// Zero-length map may or may not be valid on OSX and NetBSD
|
|
version (OSX)
|
|
import std.exception : verifyThrown = collectException;
|
|
version (NetBSD)
|
|
import std.exception : verifyThrown = collectException;
|
|
else
|
|
import std.exception : verifyThrown = assertThrown;
|
|
|
|
auto fn = std.file.deleteme ~ "-testing.txt";
|
|
scope(exit) std.file.remove(fn);
|
|
verifyThrown(scoped!MmFile(fn, MmFile.Mode.readWrite, 0, null));
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
MmFile shar = new MmFile(null, MmFile.Mode.readWrite, 10, null, 0);
|
|
void[] output = shar[0 .. $];
|
|
}
|
|
|
|
@system unittest
|
|
{
|
|
import std.file : deleteme;
|
|
auto name = std.file.deleteme ~ "-test.tmp";
|
|
scope(exit) std.file.remove(name);
|
|
|
|
std.file.write(name, "abcd");
|
|
{
|
|
scope MmFile mmf = new MmFile(name);
|
|
string p;
|
|
|
|
assert(mmf[0] == 'a');
|
|
p = cast(string) mmf[];
|
|
assert(p[1] == 'b');
|
|
p = cast(string) mmf[0 .. 4];
|
|
assert(p[2] == 'c');
|
|
}
|
|
{
|
|
scope MmFile mmf = new MmFile(name, MmFile.Mode.read, 0, null);
|
|
string p;
|
|
|
|
assert(mmf[0] == 'a');
|
|
p = cast(string) mmf[];
|
|
assert(mmf.length == 4);
|
|
assert(p[1] == 'b');
|
|
p = cast(string) mmf[0 .. 4];
|
|
assert(p[2] == 'c');
|
|
}
|
|
std.file.remove(name);
|
|
{
|
|
scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null);
|
|
char[] p = cast(char[]) mmf[];
|
|
p[] = "1234";
|
|
mmf[3] = '5';
|
|
assert(mmf[2] == '3');
|
|
assert(mmf[3] == '5');
|
|
}
|
|
{
|
|
string p = cast(string) std.file.read(name);
|
|
assert(p[] == "1235");
|
|
}
|
|
{
|
|
scope MmFile mmf = new MmFile(name, MmFile.Mode.readWriteNew, 4, null);
|
|
char[] p = cast(char[]) mmf[];
|
|
p[] = "5678";
|
|
mmf[3] = '5';
|
|
assert(mmf[2] == '7');
|
|
assert(mmf[3] == '5');
|
|
assert(cast(string) mmf[] == "5675");
|
|
}
|
|
{
|
|
string p = cast(string) std.file.read(name);
|
|
assert(p[] == "5675");
|
|
}
|
|
{
|
|
scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null);
|
|
char[] p = cast(char[]) mmf[];
|
|
assert(cast(char[]) mmf[] == "5675");
|
|
p[] = "9102";
|
|
mmf[2] = '5';
|
|
assert(cast(string) mmf[] == "9152");
|
|
}
|
|
{
|
|
string p = cast(string) std.file.read(name);
|
|
assert(p[] == "9152");
|
|
}
|
|
std.file.remove(name);
|
|
{
|
|
scope MmFile mmf = new MmFile(name, MmFile.Mode.readWrite, 4, null);
|
|
char[] p = cast(char[]) mmf[];
|
|
p[] = "abcd";
|
|
mmf[2] = '5';
|
|
assert(cast(string) mmf[] == "ab5d");
|
|
}
|
|
{
|
|
string p = cast(string) std.file.read(name);
|
|
assert(p[] == "ab5d");
|
|
}
|
|
{
|
|
scope MmFile mmf = new MmFile(name, MmFile.Mode.readCopyOnWrite, 4, null);
|
|
char[] p = cast(char[]) mmf[];
|
|
assert(cast(string) mmf[] == "ab5d");
|
|
p[] = "9102";
|
|
mmf[2] = '5';
|
|
assert(cast(string) mmf[] == "9152");
|
|
}
|
|
{
|
|
string p = cast(string) std.file.read(name);
|
|
assert(p[] == "ab5d");
|
|
}
|
|
}
|