mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-13 04:30:23 +00:00
[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:
parent
b9d565ac99
commit
1ebd25e438
@ -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
|
||||
///
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user