| // Written in the D programming language. | |
| /** | |
| * Read and write memory mapped files. | |
| * | |
| * Memory mapped files are a mechanism in operating systems that allows | |
| * file access through virtual memory. After opening a file with `MmFile`, | |
| * the contents can be read from or written to with standard slice / pointer operations. | |
| * Changes to the memory are automatically reflected in the underlying file. | |
| * | |
| * Memory mapping can increase I/O performance of large files, compared to buffered | |
| * read / write operations from `std.file` and `std.stdio`. However, I/O errors are | |
| * not handled as safely: when for example the disk that the file is on gets removed, | |
| * reading from it may result in a segfault. | |
| * | |
| * 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 | |
| * References: $(LINK https://en.wikipedia.org/wiki/Memory-mapped_file) | |
| * 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) scope | |
| { | |
| 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) scope | |
| { | |
| // 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) scope | |
| { | |
| 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) scope | |
| { | |
| 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() scope | |
| { | |
| 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); | |
| FlushFileBuffers(hFile); | |
| } | |
| 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); | |
| } | |
| } | |
| /// Read an existing file | |
| @system unittest | |
| { | |
| import std.file; | |
| std.file.write(deleteme, "hello"); // deleteme is a temporary filename | |
| scope(exit) remove(deleteme); | |
| // Use a scope class so the file will be closed at the end of this function | |
| scope mmfile = new MmFile(deleteme); | |
| assert(mmfile.length == "hello".length); | |
| // Access file contents with the slice operator | |
| // This is typed as `void[]`, so cast to `char[]` or `ubyte[]` to use it | |
| const data = cast(const(char)[]) mmfile[]; | |
| // At this point, the file content may not have been read yet. | |
| // In that case, the following memory access will intentionally | |
| // trigger a page fault, causing the kernel to load the file contents | |
| assert(data[0 .. 5] == "hello"); | |
| } | |
| /// Write a new file | |
| @system unittest | |
| { | |
| import std.file; | |
| scope(exit) remove(deleteme); | |
| scope mmfile = new MmFile(deleteme, MmFile.Mode.readWriteNew, 5, null); | |
| assert(mmfile.length == 5); | |
| auto data = cast(ubyte[]) mmfile[]; | |
| // This write to memory will be reflected in the file contents | |
| data[] = '\n'; | |
| mmfile.flush(); | |
| assert(std.file.read(deleteme) == "\n\n\n\n\n"); | |
| } | |
| @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"); | |
| } | |
| } |