[PathV2] Add mapped_file_region. Implementation for Windows and POSIX.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@161976 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Michael J. Spencer 2012-08-15 19:05:47 +00:00
parent b9d565ac99
commit 1ebd25e438
4 changed files with 421 additions and 26 deletions

View File

@ -28,6 +28,7 @@
#define LLVM_SUPPORT_FILE_SYSTEM_H
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/Twine.h"
#include "llvm/Support/DataTypes.h"
@ -576,6 +577,82 @@ error_code FindLibrary(const Twine &short_name, SmallVectorImpl<char> &result);
error_code GetMainExecutable(const char *argv0, void *MainAddr,
SmallVectorImpl<char> &result);
/// This class represents a memory mapped file. It is based on
/// boost::iostreams::mapped_file.
class mapped_file_region {
mapped_file_region() LLVM_DELETED_FUNCTION;
mapped_file_region(mapped_file_region&) LLVM_DELETED_FUNCTION;
mapped_file_region &operator =(mapped_file_region&) LLVM_DELETED_FUNCTION;
public:
enum mapmode {
readonly, //< May only access map via const_data as read only.
readwrite, //< May access map via data and modify it. Written to path.
priv //< May modify via data, but changes are lost on destruction.
};
private:
/// Platform specific mapping state.
mapmode Mode;
uint64_t Size;
void *Mapping;
#if LLVM_ON_WIN32
int FileDescriptor;
void *FileHandle;
void *FileMappingHandle;
#endif
error_code init(int FD, uint64_t Offset);
public:
typedef char char_type;
#ifdef LLVM_USE_RVALUE_REFERENCES
mapped_file_region(mapped_file_region&&);
mapped_file_region &operator =(mapped_file_region&&);
#endif
/// Construct a mapped_file_region at \a path starting at \a offset of length
/// \a length and with access \a mode.
///
/// \param path Path to the file to map. If it does not exist it will be
/// created.
/// \param mode How to map the memory.
/// \param length Number of bytes to map in starting at \a offset. If the file
/// is shorter than this, it will be extended. If \a length is
/// 0, the entire file will be mapped.
/// \param offset Byte offset from the beginning of the file where the map
/// should begin. Must be a multiple of
/// mapped_file_region::alignment().
/// \param ec This is set to errc::success if the map was constructed
/// sucessfully. Otherwise it is set to a platform dependent error.
mapped_file_region(const Twine &path,
mapmode mode,
uint64_t length,
uint64_t offset,
error_code &ec);
/// \param fd An open file descriptor to map. mapped_file_region takes
/// ownership. It must have been opended in the correct mode.
mapped_file_region(int fd,
mapmode mode,
uint64_t length,
uint64_t offset,
error_code &ec);
~mapped_file_region();
mapmode flags() const;
uint64_t size() const;
char *data() const;
/// Get a const view of the data. Modifying this memory has undefined
/// behaivor.
const char *const_data() const;
/// \returns The minimum alignment offset must be.
static int alignment();
};
/// @brief Memory maps the contents of a file
///

View File

@ -465,6 +465,118 @@ rety_open_create:
return error_code::success();
}
error_code mapped_file_region::init(int fd, uint64_t offset) {
AutoFD FD(fd);
// Figure out how large the file is.
struct stat FileInfo;
if (fstat(fd, &FileInfo) == -1)
return error_code(errno, system_category());
uint64_t FileSize = FileInfo.st_size;
if (Size == 0)
Size = FileSize;
else if (FileSize < Size) {
// We need to grow the file.
if (ftruncate(fd, Size) == -1)
return error_code(errno, system_category());
}
int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE;
int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE);
#ifdef MAP_FILE
flags |= MAP_FILE;
#endif
Mapping = ::mmap(0, Size, prot, flags, fd, offset);
if (Mapping == MAP_FAILED)
return error_code(errno, system_category());
return error_code::success();
}
mapped_file_region::mapped_file_region(const Twine &path,
mapmode mode,
uint64_t length,
uint64_t offset,
error_code &ec)
: Mode(mode)
, Size(length)
, Mapping() {
// Make sure that the requested size fits within SIZE_T.
if (length > std::numeric_limits<size_t>::max()) {
ec = make_error_code(errc::invalid_argument);
return;
}
SmallString<128> path_storage;
StringRef name = path.toNullTerminatedStringRef(path_storage);
int oflags = (mode == readonly) ? O_RDONLY : O_RDWR;
int ofd = ::open(name.begin(), oflags);
if (ofd == -1) {
ec = error_code(errno, system_category());
return;
}
ec = init(ofd, offset);
if (ec)
Mapping = 0;
}
mapped_file_region::mapped_file_region(int fd,
mapmode mode,
uint64_t length,
uint64_t offset,
error_code &ec)
: Mode(mode)
, Size(length)
, Mapping() {
// Make sure that the requested size fits within SIZE_T.
if (length > std::numeric_limits<size_t>::max()) {
ec = make_error_code(errc::invalid_argument);
return;
}
ec = init(fd, offset);
if (ec)
Mapping = 0;
}
mapped_file_region::~mapped_file_region() {
if (Mapping)
::munmap(Mapping, Size);
}
#ifdef LLVM_USE_RVALUE_REFERENCES
mapped_file_region::mapped_file_region(mapped_file_region &&other)
: Mode(other.Mode), Size(other.Size), Mapping(other.Mapping) {
other.Mapping = 0;
}
#endif
mapped_file_region::mapmode mapped_file_region::flags() const {
assert(Mapping && "Mapping failed but used anyway!");
return Mode;
}
uint64_t mapped_file_region::size() const {
assert(Mapping && "Mapping failed but used anyway!");
return Size;
}
char *mapped_file_region::data() const {
assert(Mapping && "Mapping failed but used anyway!");
assert(Mode != readonly && "Cannot get non const data for readonly mapping!");
return reinterpret_cast<char*>(Mapping);
}
const char *mapped_file_region::const_data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<const char*>(Mapping);
}
int mapped_file_region::alignment() {
return Process::GetPageSize();
}
error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path){
SmallString<128> path_null(path);

View File

@ -22,6 +22,8 @@
#include <sys/stat.h>
#include <sys/types.h>
#undef max
// MinGW doesn't define this.
#ifndef _ERRNO_T_DEFINED
#define _ERRNO_T_DEFINED
@ -703,6 +705,203 @@ error_code get_magic(const Twine &path, uint32_t len,
return error_code::success();
}
error_code mapped_file_region::init(int FD, uint64_t Offset) {
FileDescriptor = FD;
// Make sure that the requested size fits within SIZE_T.
if (Size > std::numeric_limits<SIZE_T>::max()) {
if (FileDescriptor)
_close(FileDescriptor);
else
::CloseHandle(FileHandle);
return make_error_code(errc::invalid_argument);
}
DWORD flprotect;
switch (Mode) {
case readonly: flprotect = PAGE_READONLY; break;
case readwrite: flprotect = PAGE_READWRITE; break;
case priv: flprotect = PAGE_WRITECOPY; break;
default: llvm_unreachable("invalid mapping mode");
}
FileMappingHandle = ::CreateFileMapping(FileHandle,
0,
flprotect,
Size >> 32,
Size & 0xffffffff,
0);
if (FileMappingHandle == NULL) {
error_code ec = windows_error(GetLastError());
if (FileDescriptor)
_close(FileDescriptor);
else
::CloseHandle(FileHandle);
return ec;
}
DWORD dwDesiredAccess;
switch (Mode) {
case readonly: dwDesiredAccess = FILE_MAP_READ; break;
case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break;
case priv: dwDesiredAccess = FILE_MAP_COPY; break;
default: llvm_unreachable("invalid mapping mode");
}
Mapping = ::MapViewOfFile(FileMappingHandle,
dwDesiredAccess,
Offset >> 32,
Offset & 0xffffffff,
Size);
if (Mapping == NULL) {
error_code ec = windows_error(GetLastError());
::CloseHandle(FileMappingHandle);
if (FileDescriptor)
_close(FileDescriptor);
else
::CloseHandle(FileHandle);
return ec;
}
if (Size == 0) {
MEMORY_BASIC_INFORMATION mbi;
SIZE_T Result = VirtualQuery(Mapping, &mbi, sizeof(mbi));
if (Result == 0) {
error_code ec = windows_error(GetLastError());
::UnmapViewOfFile(Mapping);
::CloseHandle(FileMappingHandle);
if (FileDescriptor)
_close(FileDescriptor);
else
::CloseHandle(FileHandle);
return ec;
}
Size = mbi.RegionSize;
}
return error_code::success();
}
mapped_file_region::mapped_file_region(const Twine &path,
mapmode mode,
uint64_t length,
uint64_t offset,
error_code &ec)
: Mode(mode)
, Size(length)
, Mapping()
, FileDescriptor()
, FileHandle(INVALID_HANDLE_VALUE)
, FileMappingHandle() {
SmallString<128> path_storage;
SmallVector<wchar_t, 128> path_utf16;
// Convert path to UTF-16.
if (ec = UTF8ToUTF16(path.toStringRef(path_storage), path_utf16))
return;
// Get file handle for creating a file mapping.
FileHandle = ::CreateFileW(c_str(path_utf16),
Mode == readonly ? GENERIC_READ
: GENERIC_READ | GENERIC_WRITE,
Mode == readonly ? FILE_SHARE_READ
: 0,
0,
Mode == readonly ? OPEN_EXISTING
: OPEN_ALWAYS,
Mode == readonly ? FILE_ATTRIBUTE_READONLY
: FILE_ATTRIBUTE_NORMAL,
0);
if (FileHandle == INVALID_HANDLE_VALUE) {
ec = windows_error(::GetLastError());
return;
}
FileDescriptor = 0;
ec = init(FileDescriptor, offset);
if (ec) {
Mapping = FileMappingHandle = 0;
FileHandle = INVALID_HANDLE_VALUE;
FileDescriptor = 0;
}
}
mapped_file_region::mapped_file_region(int fd,
mapmode mode,
uint64_t length,
uint64_t offset,
error_code &ec)
: Mode(mode)
, Size(length)
, Mapping()
, FileDescriptor(fd)
, FileHandle(INVALID_HANDLE_VALUE)
, FileMappingHandle() {
FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
if (FileHandle == INVALID_HANDLE_VALUE) {
_close(FileDescriptor);
FileDescriptor = 0;
ec = make_error_code(errc::bad_file_descriptor);
return;
}
ec = init(FileDescriptor, offset);
if (ec) {
Mapping = FileMappingHandle = 0;
FileHandle = INVALID_HANDLE_VALUE;
FileDescriptor = 0;
}
}
mapped_file_region::~mapped_file_region() {
if (Mapping)
::UnmapViewOfFile(Mapping);
if (FileMappingHandle)
::CloseHandle(FileMappingHandle);
if (FileDescriptor)
_close(FileDescriptor);
else if (FileHandle != INVALID_HANDLE_VALUE)
::CloseHandle(FileHandle);
}
#ifdef LLVM_USE_RVALUE_REFERENCES
mapped_file_region::mapped_file_region(mapped_file_region &&other)
: Mode(other.Mode)
, Size(other.Size)
, Mapping(other.Mapping)
, FileDescriptor(other.FileDescriptor)
, FileHandle(other.FileHandle)
, FileMappingHandle(other.FileMappingHandle) {
other.Mapping = other.FileMappingHandle = 0;
other.FileHandle = INVALID_HANDLE_VALUE;
other.FileDescriptor = 0;
}
#endif
mapped_file_region::mapmode mapped_file_region::flags() const {
assert(Mapping && "Mapping failed but used anyway!");
return Mode;
}
uint64_t mapped_file_region::size() const {
assert(Mapping && "Mapping failed but used anyway!");
return Size;
}
char *mapped_file_region::data() const {
assert(Mode != readonly && "Cannot get non const data for readonly mapping!");
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<char*>(Mapping);
}
const char *mapped_file_region::const_data() const {
assert(Mapping && "Mapping failed but used anyway!");
return reinterpret_cast<const char*>(Mapping);
}
int mapped_file_region::alignment() {
SYSTEM_INFO SysInfo;
::GetSystemInfo(&SysInfo);
return SysInfo.dwAllocationGranularity;
}
error_code detail::directory_iterator_construct(detail::DirIterState &it,
StringRef path){
SmallVector<wchar_t, 128> path_utf16;

View File

@ -340,44 +340,51 @@ TEST_F(FileSystemTest, Permissions) {
}
#endif
#if !defined(_WIN32) // FIXME: temporary suppressed.
TEST_F(FileSystemTest, FileMapping) {
// Create a temp file.
int FileDescriptor;
SmallString<64> TempPath;
ASSERT_NO_ERROR(
fs::unique_file("%%-%%-%%-%%.temp", FileDescriptor, TempPath));
// Grow temp file to be 4096 bytes
ASSERT_NO_ERROR(sys::fs::resize_file(Twine(TempPath), 4096));
// Map in temp file and add some content
void* MappedMemory;
ASSERT_NO_ERROR(fs::map_file_pages(Twine(TempPath), 0, 4096,
true /*writable*/, MappedMemory));
char* Memory = reinterpret_cast<char*>(MappedMemory);
strcpy(Memory, "hello there");
// Unmap temp file
ASSERT_NO_ERROR(fs::unmap_file_pages(MappedMemory, 4096));
MappedMemory = NULL;
Memory = NULL;
error_code EC;
StringRef Val("hello there");
{
fs::mapped_file_region mfr(FileDescriptor,
fs::mapped_file_region::readwrite,
4096,
0,
EC);
ASSERT_NO_ERROR(EC);
std::copy(Val.begin(), Val.end(), mfr.data());
// Explicitly add a 0.
mfr.data()[Val.size()] = 0;
// Unmap temp file
}
// Map it back in read-only
ASSERT_NO_ERROR(fs::map_file_pages(Twine(TempPath), 0, 4096,
false /*read-only*/, MappedMemory));
fs::mapped_file_region mfr(Twine(TempPath),
fs::mapped_file_region::readonly,
0,
0,
EC);
ASSERT_NO_ERROR(EC);
// Verify content
Memory = reinterpret_cast<char*>(MappedMemory);
bool SAME = (strcmp(Memory, "hello there") == 0);
EXPECT_TRUE(SAME);
EXPECT_EQ(StringRef(mfr.const_data()), Val);
// Unmap temp file
ASSERT_NO_ERROR(fs::unmap_file_pages(MappedMemory, 4096));
MappedMemory = NULL;
Memory = NULL;
}
#ifdef LLVM_USE_RVALUE_REFERENCES
fs::mapped_file_region m(Twine(TempPath),
fs::mapped_file_region::readonly,
0,
0,
EC);
ASSERT_NO_ERROR(EC);
const char *Data = m.const_data();
fs::mapped_file_region mfrrv(llvm_move(m));
EXPECT_EQ(mfrrv.const_data(), Data);
#endif
}
} // anonymous namespace