From 333ffd4abfcc3be32a945dc73c81adeafde1ba6b Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Sun, 29 Apr 2007 06:58:52 +0000 Subject: [PATCH] Add a new memorybuffer class, to unify all the file reading code in the system git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@36553 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/llvm/Support/MemoryBuffer.h | 83 ++++++++++ lib/Support/MemoryBuffer.cpp | 239 ++++++++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 include/llvm/Support/MemoryBuffer.h create mode 100644 lib/Support/MemoryBuffer.cpp diff --git a/include/llvm/Support/MemoryBuffer.h b/include/llvm/Support/MemoryBuffer.h new file mode 100644 index 00000000000..e4635d0e7ab --- /dev/null +++ b/include/llvm/Support/MemoryBuffer.h @@ -0,0 +1,83 @@ +//===--- MemoryBuffer.h - Memory Buffer Interface ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the MemoryBuffer interface. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MEMORYBUFFER_H +#define LLVM_SUPPORT_MEMORYBUFFER_H + +#include "llvm/Support/DataTypes.h" + +namespace llvm { + +/// MemoryBuffer - This interface provides simple read-only access to a block +/// of memory, and provides simple methods for reading files and standard input +/// into a memory buffer. In addition to basic access to the characters in the +/// file, this interface guarantees you can read one character past the end of +/// the file, and that this character will read as '\0'. +class MemoryBuffer { + const char *BufferStart; // Start of the buffer. + const char *BufferEnd; // End of the buffer. + + /// MustDeleteBuffer - True if we allocated this buffer. If so, the + /// destructor must know the delete[] it. + bool MustDeleteBuffer; +protected: + MemoryBuffer() : MustDeleteBuffer(false) {} + void init(const char *BufStart, const char *BufEnd); + void initCopyOf(const char *BufStart, const char *BufEnd); +public: + virtual ~MemoryBuffer(); + + const char *getBufferStart() const { return BufferStart; } + const char *getBufferEnd() const { return BufferEnd; } + unsigned getBufferSize() const { return BufferEnd-BufferStart; } + + /// getBufferIdentifier - Return an identifier for this buffer, typically the + /// filename it was read from. + virtual const char *getBufferIdentifier() const { + return "Unknown buffer"; + } + + /// getFile - Open the specified file as a MemoryBuffer, returning a new + /// MemoryBuffer if successful, otherwise returning null. If FileSize is + /// specified, this means that the client knows that the file exists and that + /// it has the specified size. + static MemoryBuffer *getFile(const char *FilenameStart, unsigned FnSize, + int64_t FileSize = -1); + + /// getMemBuffer - Open the specified memory range as a MemoryBuffer. Note + /// that EndPtr[0] must be a null byte and be accessible! + static MemoryBuffer *getMemBuffer(const char *StartPtr, const char *EndPtr, + const char *BufferName = ""); + + /// getNewMemBuffer - Allocate a new MemoryBuffer of the specified size that + /// is completely initialized to zeros. Note that the caller should + /// initialize the memory allocated by this method. The memory is owned by + /// the MemoryBuffer object. + static MemoryBuffer *getNewMemBuffer(unsigned Size, + const char *BufferName = ""); + + /// getNewUninitMemBuffer - Allocate a new MemoryBuffer of the specified size + /// that is not initialized. Note that the caller should initialize the + /// memory allocated by this method. The memory is owned by the MemoryBuffer + /// object. + static MemoryBuffer *getNewUninitMemBuffer(unsigned Size, + const char *BufferName = ""); + + /// getSTDIN - Read all of stdin into a file buffer, and return it. This + /// fails if stdin is empty. + static MemoryBuffer *getSTDIN(); +}; + +} // end namespace llvm + +#endif diff --git a/lib/Support/MemoryBuffer.cpp b/lib/Support/MemoryBuffer.cpp new file mode 100644 index 00000000000..c590f02d74c --- /dev/null +++ b/lib/Support/MemoryBuffer.cpp @@ -0,0 +1,239 @@ +//===--- MemoryBuffer.cpp - Memory Buffer implementation ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the MemoryBuffer interface. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/System/MappedFile.h" +#include "llvm/System/Process.h" +#include +#include +#include +using namespace llvm; + +//===----------------------------------------------------------------------===// +// MemoryBuffer implementation itself. +//===----------------------------------------------------------------------===// + +MemoryBuffer::~MemoryBuffer() { + if (MustDeleteBuffer) + delete [] BufferStart; +} + +/// initCopyOf - Initialize this source buffer with a copy of the specified +/// memory range. We make the copy so that we can null terminate it +/// successfully. +void MemoryBuffer::initCopyOf(const char *BufStart, const char *BufEnd) { + size_t Size = BufEnd-BufStart; + BufferStart = new char[Size+1]; + BufferEnd = BufferStart+Size; + memcpy(const_cast(BufferStart), BufStart, Size); + *const_cast(BufferEnd) = 0; // Null terminate buffer. + MustDeleteBuffer = false; +} + +/// init - Initialize this MemoryBuffer as a reference to externally allocated +/// memory, memory that we know is already null terminated. +void MemoryBuffer::init(const char *BufStart, const char *BufEnd) { + assert(BufEnd[0] == 0 && "Buffer is not null terminated!"); + BufferStart = BufStart; + BufferEnd = BufEnd; + MustDeleteBuffer = false; +} + +//===----------------------------------------------------------------------===// +// MemoryBufferMem implementation. +//===----------------------------------------------------------------------===// + +namespace { +class MemoryBufferMem : public MemoryBuffer { + std::string FileID; +public: + MemoryBufferMem(const char *Start, const char *End, const char *FID) + : FileID(FID) { + init(Start, End); + } + + virtual const char *getBufferIdentifier() const { + return FileID.c_str(); + } +}; +} + +/// getMemBuffer - Open the specified memory range as a MemoryBuffer. Note +/// that EndPtr[0] must be a null byte and be accessible! +MemoryBuffer *MemoryBuffer::getMemBuffer(const char *StartPtr, + const char *EndPtr, + const char *BufferName) { + return new MemoryBufferMem(StartPtr, EndPtr, BufferName); +} + +/// getNewUninitMemBuffer - Allocate a new MemoryBuffer of the specified size +/// that is completely initialized to zeros. Note that the caller should +/// initialize the memory allocated by this method. The memory is owned by +/// the MemoryBuffer object. +MemoryBuffer *MemoryBuffer::getNewUninitMemBuffer(unsigned Size, + const char *BufferName) { + char *Buf = new char[Size+1]; + Buf[Size] = 0; + MemoryBufferMem *SB = new MemoryBufferMem(Buf, Buf+Size, BufferName); + // The memory for this buffer is owned by the MemoryBuffer. + SB->MustDeleteBuffer = true; + return SB; +} + +/// getNewMemBuffer - Allocate a new MemoryBuffer of the specified size that +/// is completely initialized to zeros. Note that the caller should +/// initialize the memory allocated by this method. The memory is owned by +/// the MemoryBuffer object. +MemoryBuffer *MemoryBuffer::getNewMemBuffer(unsigned Size, + const char *BufferName) { + MemoryBuffer *SB = getNewUninitMemBuffer(Size, BufferName); + memset(const_cast(SB->getBufferStart()), 0, Size+1); + return SB; +} + + +//===----------------------------------------------------------------------===// +// MemoryBufferMMapFile implementation. +//===----------------------------------------------------------------------===// + +namespace { +class MemoryBufferMMapFile : public MemoryBuffer { + sys::MappedFile File; +public: + MemoryBufferMMapFile(const sys::Path &Filename); + + virtual const char *getBufferIdentifier() const { + return File.path().c_str(); + } + + ~MemoryBufferMMapFile(); +}; +} + +MemoryBufferMMapFile::MemoryBufferMMapFile(const sys::Path &Filename) { + // FIXME: This does an extra stat syscall to figure out the size, but we + // already know the size! + bool Failure = File.open(Filename); + Failure = Failure; // Silence warning in no-asserts mode. + assert(!Failure && "Can't open file??"); + + File.map(); + + size_t Size = File.size(); + + static unsigned PageSize = sys::Process::GetPageSize(); + assert(((PageSize & (PageSize-1)) == 0) && PageSize && + "Page size is not a power of 2!"); + + // If this file is not an exact multiple of the system page size (common + // case), then the OS has zero terminated the buffer for us. + if ((Size & (PageSize-1))) { + init(File.charBase(), File.charBase()+Size); + } else { + // Otherwise, we allocate a new memory buffer and copy the data over + initCopyOf(File.charBase(), File.charBase()+Size); + + // No need to keep the file mapped any longer. + File.unmap(); + } +} + +MemoryBufferMMapFile::~MemoryBufferMMapFile() { + File.unmap(); +} + +//===----------------------------------------------------------------------===// +// MemoryBuffer::getFile implementation. +//===----------------------------------------------------------------------===// + +MemoryBuffer *MemoryBuffer::getFile(const char *FilenameStart, unsigned FnSize, + int64_t FileSize) { + sys::PathWithStatus P(FilenameStart, FnSize); +#if 1 + return new MemoryBufferMMapFile(P); +#else + // FIXME: We need an efficient and portable method to open a file and then use + // 'read' to copy the bits out. The unix implementation is below. This is + // an important optimization for clients that want to open large numbers of + // small files (using mmap on everything can easily exhaust address space!). + + // If the user didn't specify a filesize, do a stat to find it. + if (FileSize == -1) { + const sys::FileStatus *FS = P.getFileStatus(); + if (FS == 0) return 0; // Error stat'ing file. + + FileSize = FS->fileSize; + } + + // If the file is larger than some threshold, use mmap, otherwise use 'read'. + if (FileSize >= 4096*4) + return new MemoryBufferMMapFile(P); + + MemoryBuffer *SB = getNewUninitMemBuffer(FileSize, FilenameStart); + char *BufPtr = const_cast(SB->getBufferStart()); + + int FD = ::open(FilenameStart, O_RDONLY); + if (FD == -1) { + delete SB; + return 0; + } + + unsigned BytesLeft = FileSize; + while (BytesLeft) { + ssize_t NumRead = ::read(FD, BufPtr, BytesLeft); + if (NumRead != -1) { + BytesLeft -= NumRead; + BufPtr += NumRead; + } else if (errno == EINTR) { + // try again + } else { + // error reading. + close(FD); + delete SB; + return 0; + } + } + close(FD); + + return SB; +#endif +} + + +//===----------------------------------------------------------------------===// +// MemoryBuffer::getSTDIN implementation. +//===----------------------------------------------------------------------===// + +namespace { +class STDINBufferFile : public MemoryBuffer { +public: + virtual const char *getBufferIdentifier() const { + return ""; + } +}; +} + +MemoryBuffer *MemoryBuffer::getSTDIN() { + char Buffer[4096*4]; + + std::vector FileData; + + // Read in all of the data from stdin, we cannot mmap stdin. + while (size_t ReadBytes = fread(Buffer, 1, 4096*4, stdin)) + FileData.insert(FileData.end(), Buffer, Buffer+ReadBytes); + + size_t Size = FileData.size(); + MemoryBuffer *B = new STDINBufferFile(); + B->initCopyOf(&FileData[0], &FileData[Size]); + return B; +}