#include "llvm/Support/MemoryBuffer.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Config/config.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/SmallVectorMemoryBuffer.h"
#include <cassert>
#include <cstring>
#include <new>
#include <sys/types.h>
#include <system_error>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
#ifdef __MVS__
#include "llvm/Support/AutoConvert.h"
#endif
using namespace llvm;
MemoryBuffer::~MemoryBuffer() = default;
void MemoryBuffer::init(const char *BufStart, const char *BufEnd,
bool RequiresNullTerminator) {
assert((!RequiresNullTerminator || BufEnd[0] == 0) &&
"Buffer is not null terminated!");
BufferStart = BufStart;
BufferEnd = BufEnd;
}
static void CopyStringRef(char *Memory, StringRef Data) {
if (!Data.empty())
memcpy(Memory, Data.data(), Data.size());
Memory[Data.size()] = 0; }
namespace {
struct NamedBufferAlloc {
const Twine &Name;
NamedBufferAlloc(const Twine &Name) : Name(Name) {}
};
}
void *operator new(size_t N, const NamedBufferAlloc &Alloc) {
SmallString<256> NameBuf;
StringRef NameRef = Alloc.Name.toStringRef(NameBuf);
char *Mem = static_cast<char *>(operator new(N + NameRef.size() + 1));
CopyStringRef(Mem + N, NameRef);
return Mem;
}
namespace {
template<typename MB>
class MemoryBufferMem : public MB {
public:
MemoryBufferMem(StringRef InputData, bool RequiresNullTerminator) {
MemoryBuffer::init(InputData.begin(), InputData.end(),
RequiresNullTerminator);
}
void operator delete(void *p) { ::operator delete(p); }
StringRef getBufferIdentifier() const override {
return StringRef(reinterpret_cast<const char *>(this + 1));
}
MemoryBuffer::BufferKind getBufferKind() const override {
return MemoryBuffer::MemoryBuffer_Malloc;
}
};
}
template <typename MB>
static ErrorOr<std::unique_ptr<MB>>
getFileAux(const Twine &Filename, uint64_t MapSize, uint64_t Offset,
bool IsText, bool RequiresNullTerminator, bool IsVolatile);
std::unique_ptr<MemoryBuffer>
MemoryBuffer::getMemBuffer(StringRef InputData, StringRef BufferName,
bool RequiresNullTerminator) {
auto *Ret = new (NamedBufferAlloc(BufferName))
MemoryBufferMem<MemoryBuffer>(InputData, RequiresNullTerminator);
return std::unique_ptr<MemoryBuffer>(Ret);
}
std::unique_ptr<MemoryBuffer>
MemoryBuffer::getMemBuffer(MemoryBufferRef Ref, bool RequiresNullTerminator) {
return std::unique_ptr<MemoryBuffer>(getMemBuffer(
Ref.getBuffer(), Ref.getBufferIdentifier(), RequiresNullTerminator));
}
static ErrorOr<std::unique_ptr<WritableMemoryBuffer>>
getMemBufferCopyImpl(StringRef InputData, const Twine &BufferName) {
auto Buf = WritableMemoryBuffer::getNewUninitMemBuffer(InputData.size(), BufferName);
if (!Buf)
return make_error_code(errc::not_enough_memory);
memcpy(Buf->getBufferStart(), InputData.data(), InputData.size());
return std::move(Buf);
}
std::unique_ptr<MemoryBuffer>
MemoryBuffer::getMemBufferCopy(StringRef InputData, const Twine &BufferName) {
auto Buf = getMemBufferCopyImpl(InputData, BufferName);
if (Buf)
return std::move(*Buf);
return nullptr;
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getFileOrSTDIN(const Twine &Filename, bool IsText,
bool RequiresNullTerminator) {
SmallString<256> NameBuf;
StringRef NameRef = Filename.toStringRef(NameBuf);
if (NameRef == "-")
return getSTDIN();
return getFile(Filename, IsText, RequiresNullTerminator,
false);
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize,
uint64_t Offset, bool IsVolatile) {
return getFileAux<MemoryBuffer>(FilePath, MapSize, Offset, false,
false, IsVolatile);
}
namespace {
template <typename MB>
constexpr sys::fs::mapped_file_region::mapmode Mapmode =
sys::fs::mapped_file_region::readonly;
template <>
constexpr sys::fs::mapped_file_region::mapmode Mapmode<MemoryBuffer> =
sys::fs::mapped_file_region::readonly;
template <>
constexpr sys::fs::mapped_file_region::mapmode Mapmode<WritableMemoryBuffer> =
sys::fs::mapped_file_region::priv;
template <>
constexpr sys::fs::mapped_file_region::mapmode
Mapmode<WriteThroughMemoryBuffer> = sys::fs::mapped_file_region::readwrite;
template<typename MB>
class MemoryBufferMMapFile : public MB {
sys::fs::mapped_file_region MFR;
static uint64_t getLegalMapOffset(uint64_t Offset) {
return Offset & ~(sys::fs::mapped_file_region::alignment() - 1);
}
static uint64_t getLegalMapSize(uint64_t Len, uint64_t Offset) {
return Len + (Offset - getLegalMapOffset(Offset));
}
const char *getStart(uint64_t Len, uint64_t Offset) {
return MFR.const_data() + (Offset - getLegalMapOffset(Offset));
}
public:
MemoryBufferMMapFile(bool RequiresNullTerminator, sys::fs::file_t FD, uint64_t Len,
uint64_t Offset, std::error_code &EC)
: MFR(FD, Mapmode<MB>, getLegalMapSize(Len, Offset),
getLegalMapOffset(Offset), EC) {
if (!EC) {
const char *Start = getStart(Len, Offset);
MemoryBuffer::init(Start, Start + Len, RequiresNullTerminator);
}
}
void operator delete(void *p) { ::operator delete(p); }
StringRef getBufferIdentifier() const override {
return StringRef(reinterpret_cast<const char *>(this + 1));
}
MemoryBuffer::BufferKind getBufferKind() const override {
return MemoryBuffer::MemoryBuffer_MMap;
}
void dontNeedIfMmap() override { MFR.dontNeed(); }
};
}
static ErrorOr<std::unique_ptr<WritableMemoryBuffer>>
getMemoryBufferForStream(sys::fs::file_t FD, const Twine &BufferName) {
SmallString<sys::fs::DefaultReadChunkSize> Buffer;
if (Error E = sys::fs::readNativeFileToEOF(FD, Buffer))
return errorToErrorCode(std::move(E));
return getMemBufferCopyImpl(Buffer, BufferName);
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getFile(const Twine &Filename, bool IsText,
bool RequiresNullTerminator, bool IsVolatile) {
return getFileAux<MemoryBuffer>(Filename, -1, 0,
IsText, RequiresNullTerminator, IsVolatile);
}
template <typename MB>
static ErrorOr<std::unique_ptr<MB>>
getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize,
uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
bool IsVolatile);
template <typename MB>
static ErrorOr<std::unique_ptr<MB>>
getFileAux(const Twine &Filename, uint64_t MapSize, uint64_t Offset,
bool IsText, bool RequiresNullTerminator, bool IsVolatile) {
Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForRead(
Filename, IsText ? sys::fs::OF_TextWithCRLF : sys::fs::OF_None);
if (!FDOrErr)
return errorToErrorCode(FDOrErr.takeError());
sys::fs::file_t FD = *FDOrErr;
auto Ret = getOpenFileImpl<MB>(FD, Filename, -1, MapSize, Offset,
RequiresNullTerminator, IsVolatile);
sys::fs::closeFile(FD);
return Ret;
}
ErrorOr<std::unique_ptr<WritableMemoryBuffer>>
WritableMemoryBuffer::getFile(const Twine &Filename, bool IsVolatile) {
return getFileAux<WritableMemoryBuffer>(
Filename, -1, 0, false,
false, IsVolatile);
}
ErrorOr<std::unique_ptr<WritableMemoryBuffer>>
WritableMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize,
uint64_t Offset, bool IsVolatile) {
return getFileAux<WritableMemoryBuffer>(
Filename, MapSize, Offset, false,
false, IsVolatile);
}
std::unique_ptr<WritableMemoryBuffer>
WritableMemoryBuffer::getNewUninitMemBuffer(size_t Size, const Twine &BufferName) {
using MemBuffer = MemoryBufferMem<WritableMemoryBuffer>;
SmallString<256> NameBuf;
StringRef NameRef = BufferName.toStringRef(NameBuf);
size_t AlignedStringLen = alignTo(sizeof(MemBuffer) + NameRef.size() + 1, 16);
size_t RealLen = AlignedStringLen + Size + 1;
if (RealLen <= Size) return nullptr;
char *Mem = static_cast<char*>(operator new(RealLen, std::nothrow));
if (!Mem)
return nullptr;
CopyStringRef(Mem + sizeof(MemBuffer), NameRef);
char *Buf = Mem + AlignedStringLen;
Buf[Size] = 0;
auto *Ret = new (Mem) MemBuffer(StringRef(Buf, Size), true);
return std::unique_ptr<WritableMemoryBuffer>(Ret);
}
std::unique_ptr<WritableMemoryBuffer>
WritableMemoryBuffer::getNewMemBuffer(size_t Size, const Twine &BufferName) {
auto SB = WritableMemoryBuffer::getNewUninitMemBuffer(Size, BufferName);
if (!SB)
return nullptr;
memset(SB->getBufferStart(), 0, Size);
return SB;
}
static bool shouldUseMmap(sys::fs::file_t FD,
size_t FileSize,
size_t MapSize,
off_t Offset,
bool RequiresNullTerminator,
int PageSize,
bool IsVolatile) {
if (IsVolatile && RequiresNullTerminator)
return false;
if (MapSize < 4 * 4096 || MapSize < (unsigned)PageSize)
return false;
if (!RequiresNullTerminator)
return true;
if (FileSize == size_t(-1)) {
sys::fs::file_status Status;
if (sys::fs::status(FD, Status))
return false;
FileSize = Status.getSize();
}
size_t End = Offset + MapSize;
assert(End <= FileSize);
if (End != FileSize)
return false;
if ((FileSize & (PageSize -1)) == 0)
return false;
#if defined(__CYGWIN__)
if ((FileSize & (4096 - 1)) == 0)
return false;
#endif
return true;
}
static ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>>
getReadWriteFile(const Twine &Filename, uint64_t FileSize, uint64_t MapSize,
uint64_t Offset) {
Expected<sys::fs::file_t> FDOrErr = sys::fs::openNativeFileForReadWrite(
Filename, sys::fs::CD_OpenExisting, sys::fs::OF_None);
if (!FDOrErr)
return errorToErrorCode(FDOrErr.takeError());
sys::fs::file_t FD = *FDOrErr;
if (MapSize == uint64_t(-1)) {
if (FileSize == uint64_t(-1)) {
sys::fs::file_status Status;
std::error_code EC = sys::fs::status(FD, Status);
if (EC)
return EC;
sys::fs::file_type Type = Status.type();
if (Type != sys::fs::file_type::regular_file &&
Type != sys::fs::file_type::block_file)
return make_error_code(errc::invalid_argument);
FileSize = Status.getSize();
}
MapSize = FileSize;
}
std::error_code EC;
std::unique_ptr<WriteThroughMemoryBuffer> Result(
new (NamedBufferAlloc(Filename))
MemoryBufferMMapFile<WriteThroughMemoryBuffer>(false, FD, MapSize,
Offset, EC));
if (EC)
return EC;
return std::move(Result);
}
ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>>
WriteThroughMemoryBuffer::getFile(const Twine &Filename, int64_t FileSize) {
return getReadWriteFile(Filename, FileSize, FileSize, 0);
}
ErrorOr<std::unique_ptr<WriteThroughMemoryBuffer>>
WriteThroughMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize,
uint64_t Offset) {
return getReadWriteFile(Filename, -1, MapSize, Offset);
}
template <typename MB>
static ErrorOr<std::unique_ptr<MB>>
getOpenFileImpl(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize,
uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator,
bool IsVolatile) {
static int PageSize = sys::Process::getPageSizeEstimate();
if (MapSize == uint64_t(-1)) {
if (FileSize == uint64_t(-1)) {
sys::fs::file_status Status;
std::error_code EC = sys::fs::status(FD, Status);
if (EC)
return EC;
sys::fs::file_type Type = Status.type();
if (Type != sys::fs::file_type::regular_file &&
Type != sys::fs::file_type::block_file)
return getMemoryBufferForStream(FD, Filename);
FileSize = Status.getSize();
}
MapSize = FileSize;
}
if (shouldUseMmap(FD, FileSize, MapSize, Offset, RequiresNullTerminator,
PageSize, IsVolatile)) {
std::error_code EC;
std::unique_ptr<MB> Result(
new (NamedBufferAlloc(Filename)) MemoryBufferMMapFile<MB>(
RequiresNullTerminator, FD, MapSize, Offset, EC));
if (!EC)
return std::move(Result);
}
#ifdef __MVS__
if (auto EC = llvm::enableAutoConversion(FD))
return EC;
#endif
auto Buf = WritableMemoryBuffer::getNewUninitMemBuffer(MapSize, Filename);
if (!Buf) {
return make_error_code(errc::not_enough_memory);
}
MutableArrayRef<char> ToRead = Buf->getBuffer();
while (!ToRead.empty()) {
Expected<size_t> ReadBytes =
sys::fs::readNativeFileSlice(FD, ToRead, Offset);
if (!ReadBytes)
return errorToErrorCode(ReadBytes.takeError());
if (*ReadBytes == 0) {
std::memset(ToRead.data(), 0, ToRead.size());
break;
}
ToRead = ToRead.drop_front(*ReadBytes);
Offset += *ReadBytes;
}
return std::move(Buf);
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getOpenFile(sys::fs::file_t FD, const Twine &Filename, uint64_t FileSize,
bool RequiresNullTerminator, bool IsVolatile) {
return getOpenFileImpl<MemoryBuffer>(FD, Filename, FileSize, FileSize, 0,
RequiresNullTerminator, IsVolatile);
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getOpenFileSlice(sys::fs::file_t FD, const Twine &Filename, uint64_t MapSize,
int64_t Offset, bool IsVolatile) {
assert(MapSize != uint64_t(-1));
return getOpenFileImpl<MemoryBuffer>(FD, Filename, -1, MapSize, Offset, false,
IsVolatile);
}
ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getSTDIN() {
sys::ChangeStdinMode(sys::fs::OF_Text);
return getMemoryBufferForStream(sys::fs::getStdinHandle(), "<stdin>");
}
ErrorOr<std::unique_ptr<MemoryBuffer>>
MemoryBuffer::getFileAsStream(const Twine &Filename) {
Expected<sys::fs::file_t> FDOrErr =
sys::fs::openNativeFileForRead(Filename, sys::fs::OF_None);
if (!FDOrErr)
return errorToErrorCode(FDOrErr.takeError());
sys::fs::file_t FD = *FDOrErr;
ErrorOr<std::unique_ptr<MemoryBuffer>> Ret =
getMemoryBufferForStream(FD, Filename);
sys::fs::closeFile(FD);
return Ret;
}
MemoryBufferRef MemoryBuffer::getMemBufferRef() const {
StringRef Data = getBuffer();
StringRef Identifier = getBufferIdentifier();
return MemoryBufferRef(Data, Identifier);
}
SmallVectorMemoryBuffer::~SmallVectorMemoryBuffer() = default;