Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 1 | //===--- MemoryBuffer.cpp - Memory Buffer implementation ------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
Chris Lattner | 4ee451d | 2007-12-29 20:36:04 +0000 | [diff] [blame^] | 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file implements the MemoryBuffer interface. |
| 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "llvm/Support/MemoryBuffer.h" |
| 15 | #include "llvm/System/MappedFile.h" |
| 16 | #include "llvm/System/Process.h" |
Jeff Cohen | 0fea8eb | 2007-05-07 15:21:46 +0000 | [diff] [blame] | 17 | #include "llvm/System/Program.h" |
Jeff Cohen | 9bc4060 | 2007-04-29 14:21:44 +0000 | [diff] [blame] | 18 | #include <cassert> |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 19 | #include <cstdio> |
| 20 | #include <cstring> |
| 21 | #include <cerrno> |
| 22 | using namespace llvm; |
| 23 | |
| 24 | //===----------------------------------------------------------------------===// |
| 25 | // MemoryBuffer implementation itself. |
| 26 | //===----------------------------------------------------------------------===// |
| 27 | |
| 28 | MemoryBuffer::~MemoryBuffer() { |
| 29 | if (MustDeleteBuffer) |
| 30 | delete [] BufferStart; |
| 31 | } |
| 32 | |
| 33 | /// initCopyOf - Initialize this source buffer with a copy of the specified |
| 34 | /// memory range. We make the copy so that we can null terminate it |
| 35 | /// successfully. |
| 36 | void MemoryBuffer::initCopyOf(const char *BufStart, const char *BufEnd) { |
| 37 | size_t Size = BufEnd-BufStart; |
| 38 | BufferStart = new char[Size+1]; |
| 39 | BufferEnd = BufferStart+Size; |
| 40 | memcpy(const_cast<char*>(BufferStart), BufStart, Size); |
| 41 | *const_cast<char*>(BufferEnd) = 0; // Null terminate buffer. |
Chris Lattner | a24b04e | 2007-05-11 00:43:26 +0000 | [diff] [blame] | 42 | MustDeleteBuffer = true; |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 43 | } |
| 44 | |
| 45 | /// init - Initialize this MemoryBuffer as a reference to externally allocated |
| 46 | /// memory, memory that we know is already null terminated. |
| 47 | void MemoryBuffer::init(const char *BufStart, const char *BufEnd) { |
| 48 | assert(BufEnd[0] == 0 && "Buffer is not null terminated!"); |
| 49 | BufferStart = BufStart; |
| 50 | BufferEnd = BufEnd; |
| 51 | MustDeleteBuffer = false; |
| 52 | } |
| 53 | |
| 54 | //===----------------------------------------------------------------------===// |
| 55 | // MemoryBufferMem implementation. |
| 56 | //===----------------------------------------------------------------------===// |
| 57 | |
| 58 | namespace { |
| 59 | class MemoryBufferMem : public MemoryBuffer { |
| 60 | std::string FileID; |
| 61 | public: |
Chris Lattner | 3daae27 | 2007-10-09 21:46:38 +0000 | [diff] [blame] | 62 | MemoryBufferMem(const char *Start, const char *End, const char *FID, |
| 63 | bool Copy = false) |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 64 | : FileID(FID) { |
Chris Lattner | 3daae27 | 2007-10-09 21:46:38 +0000 | [diff] [blame] | 65 | if (!Copy) |
| 66 | init(Start, End); |
| 67 | else |
| 68 | initCopyOf(Start, End); |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 69 | } |
| 70 | |
| 71 | virtual const char *getBufferIdentifier() const { |
| 72 | return FileID.c_str(); |
| 73 | } |
| 74 | }; |
| 75 | } |
| 76 | |
| 77 | /// getMemBuffer - Open the specified memory range as a MemoryBuffer. Note |
| 78 | /// that EndPtr[0] must be a null byte and be accessible! |
| 79 | MemoryBuffer *MemoryBuffer::getMemBuffer(const char *StartPtr, |
| 80 | const char *EndPtr, |
| 81 | const char *BufferName) { |
| 82 | return new MemoryBufferMem(StartPtr, EndPtr, BufferName); |
| 83 | } |
| 84 | |
Chris Lattner | 3daae27 | 2007-10-09 21:46:38 +0000 | [diff] [blame] | 85 | /// getMemBufferCopy - Open the specified memory range as a MemoryBuffer, |
| 86 | /// copying the contents and taking ownership of it. This has no requirements |
| 87 | /// on EndPtr[0]. |
| 88 | MemoryBuffer *MemoryBuffer::getMemBufferCopy(const char *StartPtr, |
| 89 | const char *EndPtr, |
| 90 | const char *BufferName) { |
| 91 | return new MemoryBufferMem(StartPtr, EndPtr, BufferName, true); |
| 92 | } |
| 93 | |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 94 | /// getNewUninitMemBuffer - Allocate a new MemoryBuffer of the specified size |
| 95 | /// that is completely initialized to zeros. Note that the caller should |
| 96 | /// initialize the memory allocated by this method. The memory is owned by |
| 97 | /// the MemoryBuffer object. |
| 98 | MemoryBuffer *MemoryBuffer::getNewUninitMemBuffer(unsigned Size, |
| 99 | const char *BufferName) { |
| 100 | char *Buf = new char[Size+1]; |
| 101 | Buf[Size] = 0; |
| 102 | MemoryBufferMem *SB = new MemoryBufferMem(Buf, Buf+Size, BufferName); |
| 103 | // The memory for this buffer is owned by the MemoryBuffer. |
| 104 | SB->MustDeleteBuffer = true; |
| 105 | return SB; |
| 106 | } |
| 107 | |
| 108 | /// getNewMemBuffer - Allocate a new MemoryBuffer of the specified size that |
| 109 | /// is completely initialized to zeros. Note that the caller should |
| 110 | /// initialize the memory allocated by this method. The memory is owned by |
| 111 | /// the MemoryBuffer object. |
| 112 | MemoryBuffer *MemoryBuffer::getNewMemBuffer(unsigned Size, |
| 113 | const char *BufferName) { |
| 114 | MemoryBuffer *SB = getNewUninitMemBuffer(Size, BufferName); |
| 115 | memset(const_cast<char*>(SB->getBufferStart()), 0, Size+1); |
| 116 | return SB; |
| 117 | } |
| 118 | |
| 119 | |
Chris Lattner | 2b1f106 | 2007-11-18 18:52:28 +0000 | [diff] [blame] | 120 | /// getFileOrSTDIN - Open the specified file as a MemoryBuffer, or open stdin |
| 121 | /// if the Filename is "-". If an error occurs, this returns null and fills |
| 122 | /// in *ErrStr with a reason. If stdin is empty, this API (unlike getSTDIN) |
| 123 | /// returns an empty buffer. |
| 124 | MemoryBuffer *MemoryBuffer::getFileOrSTDIN(const char *FilenameStart, |
| 125 | unsigned FnSize, |
| 126 | std::string *ErrStr, |
| 127 | int64_t FileSize) { |
| 128 | if (FnSize != 1 || FilenameStart[0] != '-') |
| 129 | return getFile(FilenameStart, FnSize, ErrStr, FileSize); |
| 130 | MemoryBuffer *M = getSTDIN(); |
| 131 | if (M) return M; |
| 132 | |
| 133 | // If stdin was empty, M is null. Cons up an empty memory buffer now. |
| 134 | const char *EmptyStr = ""; |
| 135 | return MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<stdin>"); |
| 136 | } |
| 137 | |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 138 | //===----------------------------------------------------------------------===// |
| 139 | // MemoryBufferMMapFile implementation. |
| 140 | //===----------------------------------------------------------------------===// |
| 141 | |
| 142 | namespace { |
| 143 | class MemoryBufferMMapFile : public MemoryBuffer { |
| 144 | sys::MappedFile File; |
| 145 | public: |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 146 | MemoryBufferMMapFile() {} |
| 147 | |
Chris Lattner | 5499da8 | 2007-05-06 23:32:36 +0000 | [diff] [blame] | 148 | bool open(const sys::Path &Filename, std::string *ErrStr); |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 149 | |
| 150 | virtual const char *getBufferIdentifier() const { |
| 151 | return File.path().c_str(); |
| 152 | } |
| 153 | |
| 154 | ~MemoryBufferMMapFile(); |
| 155 | }; |
| 156 | } |
| 157 | |
Chris Lattner | 5499da8 | 2007-05-06 23:32:36 +0000 | [diff] [blame] | 158 | bool MemoryBufferMMapFile::open(const sys::Path &Filename, |
| 159 | std::string *ErrStr) { |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 160 | // FIXME: This does an extra stat syscall to figure out the size, but we |
| 161 | // already know the size! |
Chris Lattner | 5499da8 | 2007-05-06 23:32:36 +0000 | [diff] [blame] | 162 | bool Failure = File.open(Filename, sys::MappedFile::READ_ACCESS, ErrStr); |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 163 | if (Failure) return true; |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 164 | |
Chris Lattner | 5499da8 | 2007-05-06 23:32:36 +0000 | [diff] [blame] | 165 | if (!File.map(ErrStr)) |
| 166 | return true; |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 167 | |
| 168 | size_t Size = File.size(); |
| 169 | |
| 170 | static unsigned PageSize = sys::Process::GetPageSize(); |
| 171 | assert(((PageSize & (PageSize-1)) == 0) && PageSize && |
| 172 | "Page size is not a power of 2!"); |
| 173 | |
| 174 | // If this file is not an exact multiple of the system page size (common |
| 175 | // case), then the OS has zero terminated the buffer for us. |
| 176 | if ((Size & (PageSize-1))) { |
| 177 | init(File.charBase(), File.charBase()+Size); |
| 178 | } else { |
| 179 | // Otherwise, we allocate a new memory buffer and copy the data over |
| 180 | initCopyOf(File.charBase(), File.charBase()+Size); |
| 181 | |
| 182 | // No need to keep the file mapped any longer. |
| 183 | File.unmap(); |
| 184 | } |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 185 | return false; |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | MemoryBufferMMapFile::~MemoryBufferMMapFile() { |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 189 | if (File.isMapped()) |
| 190 | File.unmap(); |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 191 | } |
| 192 | |
| 193 | //===----------------------------------------------------------------------===// |
| 194 | // MemoryBuffer::getFile implementation. |
| 195 | //===----------------------------------------------------------------------===// |
| 196 | |
| 197 | MemoryBuffer *MemoryBuffer::getFile(const char *FilenameStart, unsigned FnSize, |
Chris Lattner | 5499da8 | 2007-05-06 23:32:36 +0000 | [diff] [blame] | 198 | std::string *ErrStr, int64_t FileSize){ |
| 199 | // FIXME: it would be nice if PathWithStatus didn't copy the filename into a |
| 200 | // temporary string. :( |
Jeff Cohen | 930c0fe | 2007-04-29 14:43:31 +0000 | [diff] [blame] | 201 | sys::PathWithStatus P(FilenameStart, FnSize); |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 202 | #if 1 |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 203 | MemoryBufferMMapFile *M = new MemoryBufferMMapFile(); |
Chris Lattner | 5499da8 | 2007-05-06 23:32:36 +0000 | [diff] [blame] | 204 | if (!M->open(P, ErrStr)) |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 205 | return M; |
| 206 | delete M; |
| 207 | return 0; |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 208 | #else |
| 209 | // FIXME: We need an efficient and portable method to open a file and then use |
| 210 | // 'read' to copy the bits out. The unix implementation is below. This is |
| 211 | // an important optimization for clients that want to open large numbers of |
| 212 | // small files (using mmap on everything can easily exhaust address space!). |
| 213 | |
| 214 | // If the user didn't specify a filesize, do a stat to find it. |
| 215 | if (FileSize == -1) { |
| 216 | const sys::FileStatus *FS = P.getFileStatus(); |
| 217 | if (FS == 0) return 0; // Error stat'ing file. |
| 218 | |
| 219 | FileSize = FS->fileSize; |
| 220 | } |
| 221 | |
| 222 | // If the file is larger than some threshold, use mmap, otherwise use 'read'. |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 223 | if (FileSize >= 4096*4) { |
| 224 | MemoryBufferMMapFile *M = new MemoryBufferMMapFile(); |
Chris Lattner | 5499da8 | 2007-05-06 23:32:36 +0000 | [diff] [blame] | 225 | if (!M->open(P, ErrStr)) |
Chris Lattner | 82e791d | 2007-05-06 07:24:46 +0000 | [diff] [blame] | 226 | return M; |
| 227 | delete M; |
| 228 | return 0; |
| 229 | } |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 230 | |
| 231 | MemoryBuffer *SB = getNewUninitMemBuffer(FileSize, FilenameStart); |
| 232 | char *BufPtr = const_cast<char*>(SB->getBufferStart()); |
| 233 | |
| 234 | int FD = ::open(FilenameStart, O_RDONLY); |
| 235 | if (FD == -1) { |
| 236 | delete SB; |
| 237 | return 0; |
| 238 | } |
| 239 | |
| 240 | unsigned BytesLeft = FileSize; |
| 241 | while (BytesLeft) { |
| 242 | ssize_t NumRead = ::read(FD, BufPtr, BytesLeft); |
| 243 | if (NumRead != -1) { |
| 244 | BytesLeft -= NumRead; |
| 245 | BufPtr += NumRead; |
| 246 | } else if (errno == EINTR) { |
| 247 | // try again |
| 248 | } else { |
| 249 | // error reading. |
| 250 | close(FD); |
| 251 | delete SB; |
| 252 | return 0; |
| 253 | } |
| 254 | } |
| 255 | close(FD); |
| 256 | |
| 257 | return SB; |
| 258 | #endif |
| 259 | } |
| 260 | |
| 261 | |
| 262 | //===----------------------------------------------------------------------===// |
| 263 | // MemoryBuffer::getSTDIN implementation. |
| 264 | //===----------------------------------------------------------------------===// |
| 265 | |
| 266 | namespace { |
| 267 | class STDINBufferFile : public MemoryBuffer { |
| 268 | public: |
| 269 | virtual const char *getBufferIdentifier() const { |
| 270 | return "<stdin>"; |
| 271 | } |
| 272 | }; |
| 273 | } |
| 274 | |
| 275 | MemoryBuffer *MemoryBuffer::getSTDIN() { |
| 276 | char Buffer[4096*4]; |
| 277 | |
| 278 | std::vector<char> FileData; |
| 279 | |
| 280 | // Read in all of the data from stdin, we cannot mmap stdin. |
Jeff Cohen | 0fea8eb | 2007-05-07 15:21:46 +0000 | [diff] [blame] | 281 | sys::Program::ChangeStdinToBinary(); |
Reid Spencer | 2372ccc | 2007-08-08 20:01:58 +0000 | [diff] [blame] | 282 | while (size_t ReadBytes = fread(Buffer, sizeof(char), 4096*4, stdin)) |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 283 | FileData.insert(FileData.end(), Buffer, Buffer+ReadBytes); |
Reid Spencer | 2372ccc | 2007-08-08 20:01:58 +0000 | [diff] [blame] | 284 | |
Nick Lewycky | ea33294 | 2007-07-01 03:06:30 +0000 | [diff] [blame] | 285 | FileData.push_back(0); // &FileData[Size] is invalid. So is &*FileData.end(). |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 286 | size_t Size = FileData.size(); |
Reid Spencer | 2372ccc | 2007-08-08 20:01:58 +0000 | [diff] [blame] | 287 | if (Size <= 1) |
| 288 | return 0; |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 289 | MemoryBuffer *B = new STDINBufferFile(); |
Nick Lewycky | ea33294 | 2007-07-01 03:06:30 +0000 | [diff] [blame] | 290 | B->initCopyOf(&FileData[0], &FileData[Size-1]); |
Chris Lattner | 333ffd4 | 2007-04-29 06:58:52 +0000 | [diff] [blame] | 291 | return B; |
| 292 | } |