blob: 4d26d38731a8078b65a3e763207a1fb0389125e3 [file] [log] [blame]
Chris Lattner333ffd42007-04-29 06:58:52 +00001//===--- MemoryBuffer.cpp - Memory Buffer implementation ------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
Chris Lattner4ee451d2007-12-29 20:36:04 +00005// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
Chris Lattner333ffd42007-04-29 06:58:52 +00007//
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 Cohen0fea8eb2007-05-07 15:21:46 +000017#include "llvm/System/Program.h"
Jeff Cohen9bc40602007-04-29 14:21:44 +000018#include <cassert>
Chris Lattner333ffd42007-04-29 06:58:52 +000019#include <cstdio>
20#include <cstring>
21#include <cerrno>
22using namespace llvm;
23
24//===----------------------------------------------------------------------===//
25// MemoryBuffer implementation itself.
26//===----------------------------------------------------------------------===//
27
28MemoryBuffer::~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.
36void 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 Lattnera24b04e2007-05-11 00:43:26 +000042 MustDeleteBuffer = true;
Chris Lattner333ffd42007-04-29 06:58:52 +000043}
44
45/// init - Initialize this MemoryBuffer as a reference to externally allocated
46/// memory, memory that we know is already null terminated.
47void 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
58namespace {
59class MemoryBufferMem : public MemoryBuffer {
60 std::string FileID;
61public:
Chris Lattner3daae272007-10-09 21:46:38 +000062 MemoryBufferMem(const char *Start, const char *End, const char *FID,
63 bool Copy = false)
Chris Lattner333ffd42007-04-29 06:58:52 +000064 : FileID(FID) {
Chris Lattner3daae272007-10-09 21:46:38 +000065 if (!Copy)
66 init(Start, End);
67 else
68 initCopyOf(Start, End);
Chris Lattner333ffd42007-04-29 06:58:52 +000069 }
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!
79MemoryBuffer *MemoryBuffer::getMemBuffer(const char *StartPtr,
80 const char *EndPtr,
81 const char *BufferName) {
82 return new MemoryBufferMem(StartPtr, EndPtr, BufferName);
83}
84
Chris Lattner3daae272007-10-09 21:46:38 +000085/// 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].
88MemoryBuffer *MemoryBuffer::getMemBufferCopy(const char *StartPtr,
89 const char *EndPtr,
90 const char *BufferName) {
91 return new MemoryBufferMem(StartPtr, EndPtr, BufferName, true);
92}
93
Chris Lattner333ffd42007-04-29 06:58:52 +000094/// 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.
98MemoryBuffer *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.
112MemoryBuffer *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 Lattner2b1f1062007-11-18 18:52:28 +0000120/// 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.
124MemoryBuffer *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 Lattner333ffd42007-04-29 06:58:52 +0000138//===----------------------------------------------------------------------===//
139// MemoryBufferMMapFile implementation.
140//===----------------------------------------------------------------------===//
141
142namespace {
143class MemoryBufferMMapFile : public MemoryBuffer {
144 sys::MappedFile File;
145public:
Chris Lattner82e791d2007-05-06 07:24:46 +0000146 MemoryBufferMMapFile() {}
147
Chris Lattner5499da82007-05-06 23:32:36 +0000148 bool open(const sys::Path &Filename, std::string *ErrStr);
Chris Lattner333ffd42007-04-29 06:58:52 +0000149
150 virtual const char *getBufferIdentifier() const {
151 return File.path().c_str();
152 }
153
154 ~MemoryBufferMMapFile();
155};
156}
157
Chris Lattner5499da82007-05-06 23:32:36 +0000158bool MemoryBufferMMapFile::open(const sys::Path &Filename,
159 std::string *ErrStr) {
Chris Lattner333ffd42007-04-29 06:58:52 +0000160 // FIXME: This does an extra stat syscall to figure out the size, but we
161 // already know the size!
Chris Lattner5499da82007-05-06 23:32:36 +0000162 bool Failure = File.open(Filename, sys::MappedFile::READ_ACCESS, ErrStr);
Chris Lattner82e791d2007-05-06 07:24:46 +0000163 if (Failure) return true;
Chris Lattner333ffd42007-04-29 06:58:52 +0000164
Chris Lattner5499da82007-05-06 23:32:36 +0000165 if (!File.map(ErrStr))
166 return true;
Chris Lattner333ffd42007-04-29 06:58:52 +0000167
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 Lattner82e791d2007-05-06 07:24:46 +0000185 return false;
Chris Lattner333ffd42007-04-29 06:58:52 +0000186}
187
188MemoryBufferMMapFile::~MemoryBufferMMapFile() {
Chris Lattner82e791d2007-05-06 07:24:46 +0000189 if (File.isMapped())
190 File.unmap();
Chris Lattner333ffd42007-04-29 06:58:52 +0000191}
192
193//===----------------------------------------------------------------------===//
194// MemoryBuffer::getFile implementation.
195//===----------------------------------------------------------------------===//
196
197MemoryBuffer *MemoryBuffer::getFile(const char *FilenameStart, unsigned FnSize,
Chris Lattner5499da82007-05-06 23:32:36 +0000198 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 Cohen930c0fe2007-04-29 14:43:31 +0000201 sys::PathWithStatus P(FilenameStart, FnSize);
Chris Lattner333ffd42007-04-29 06:58:52 +0000202#if 1
Chris Lattner82e791d2007-05-06 07:24:46 +0000203 MemoryBufferMMapFile *M = new MemoryBufferMMapFile();
Chris Lattner5499da82007-05-06 23:32:36 +0000204 if (!M->open(P, ErrStr))
Chris Lattner82e791d2007-05-06 07:24:46 +0000205 return M;
206 delete M;
207 return 0;
Chris Lattner333ffd42007-04-29 06:58:52 +0000208#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 Lattner82e791d2007-05-06 07:24:46 +0000223 if (FileSize >= 4096*4) {
224 MemoryBufferMMapFile *M = new MemoryBufferMMapFile();
Chris Lattner5499da82007-05-06 23:32:36 +0000225 if (!M->open(P, ErrStr))
Chris Lattner82e791d2007-05-06 07:24:46 +0000226 return M;
227 delete M;
228 return 0;
229 }
Chris Lattner333ffd42007-04-29 06:58:52 +0000230
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
266namespace {
267class STDINBufferFile : public MemoryBuffer {
268public:
269 virtual const char *getBufferIdentifier() const {
270 return "<stdin>";
271 }
272};
273}
274
275MemoryBuffer *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 Cohen0fea8eb2007-05-07 15:21:46 +0000281 sys::Program::ChangeStdinToBinary();
Reid Spencer2372ccc2007-08-08 20:01:58 +0000282 while (size_t ReadBytes = fread(Buffer, sizeof(char), 4096*4, stdin))
Chris Lattner333ffd42007-04-29 06:58:52 +0000283 FileData.insert(FileData.end(), Buffer, Buffer+ReadBytes);
Reid Spencer2372ccc2007-08-08 20:01:58 +0000284
Nick Lewyckyea332942007-07-01 03:06:30 +0000285 FileData.push_back(0); // &FileData[Size] is invalid. So is &*FileData.end().
Chris Lattner333ffd42007-04-29 06:58:52 +0000286 size_t Size = FileData.size();
Reid Spencer2372ccc2007-08-08 20:01:58 +0000287 if (Size <= 1)
288 return 0;
Chris Lattner333ffd42007-04-29 06:58:52 +0000289 MemoryBuffer *B = new STDINBufferFile();
Nick Lewyckyea332942007-07-01 03:06:30 +0000290 B->initCopyOf(&FileData[0], &FileData[Size-1]);
Chris Lattner333ffd42007-04-29 06:58:52 +0000291 return B;
292}