MemoryBuffer.cpp revision 7127b13342f25a1fa06ed2c6f81891d3613df3fd
1//===--- MemoryBuffer.cpp - Memory Buffer implementation ------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  This file implements the MemoryBuffer interface.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/Support/MemoryBuffer.h"
15#include "llvm/ADT/OwningPtr.h"
16#include "llvm/ADT/SmallString.h"
17#include "llvm/System/Path.h"
18#include "llvm/System/Process.h"
19#include "llvm/System/Program.h"
20#include <cassert>
21#include <cstdio>
22#include <cstring>
23#include <cerrno>
24#include <sys/types.h>
25#include <sys/stat.h>
26#if !defined(_MSC_VER) && !defined(__MINGW32__)
27#include <unistd.h>
28#include <sys/uio.h>
29#else
30#include <io.h>
31#endif
32#include <fcntl.h>
33using namespace llvm;
34
35//===----------------------------------------------------------------------===//
36// MemoryBuffer implementation itself.
37//===----------------------------------------------------------------------===//
38
39MemoryBuffer::~MemoryBuffer() {
40  if (MustDeleteBuffer)
41    free((void*)BufferStart);
42}
43
44/// initCopyOf - Initialize this source buffer with a copy of the specified
45/// memory range.  We make the copy so that we can null terminate it
46/// successfully.
47void MemoryBuffer::initCopyOf(const char *BufStart, const char *BufEnd) {
48  size_t Size = BufEnd-BufStart;
49  BufferStart = (char *)malloc((Size+1) * sizeof(char));
50  BufferEnd = BufferStart+Size;
51  memcpy(const_cast<char*>(BufferStart), BufStart, Size);
52  *const_cast<char*>(BufferEnd) = 0;   // Null terminate buffer.
53  MustDeleteBuffer = true;
54}
55
56/// init - Initialize this MemoryBuffer as a reference to externally allocated
57/// memory, memory that we know is already null terminated.
58void MemoryBuffer::init(const char *BufStart, const char *BufEnd) {
59  assert(BufEnd[0] == 0 && "Buffer is not null terminated!");
60  BufferStart = BufStart;
61  BufferEnd = BufEnd;
62  MustDeleteBuffer = false;
63}
64
65//===----------------------------------------------------------------------===//
66// MemoryBufferMem implementation.
67//===----------------------------------------------------------------------===//
68
69namespace {
70class MemoryBufferMem : public MemoryBuffer {
71  std::string FileID;
72public:
73  MemoryBufferMem(const char *Start, const char *End, const char *FID,
74                  bool Copy = false)
75  : FileID(FID) {
76    if (!Copy)
77      init(Start, End);
78    else
79      initCopyOf(Start, End);
80  }
81
82  virtual const char *getBufferIdentifier() const {
83    return FileID.c_str();
84  }
85};
86}
87
88/// getMemBuffer - Open the specified memory range as a MemoryBuffer.  Note
89/// that EndPtr[0] must be a null byte and be accessible!
90MemoryBuffer *MemoryBuffer::getMemBuffer(const char *StartPtr,
91                                         const char *EndPtr,
92                                         const char *BufferName) {
93  return new MemoryBufferMem(StartPtr, EndPtr, BufferName);
94}
95
96/// getMemBufferCopy - Open the specified memory range as a MemoryBuffer,
97/// copying the contents and taking ownership of it.  This has no requirements
98/// on EndPtr[0].
99MemoryBuffer *MemoryBuffer::getMemBufferCopy(const char *StartPtr,
100                                             const char *EndPtr,
101                                             const char *BufferName) {
102  return new MemoryBufferMem(StartPtr, EndPtr, BufferName, true);
103}
104
105/// getNewUninitMemBuffer - Allocate a new MemoryBuffer of the specified size
106/// that is completely initialized to zeros.  Note that the caller should
107/// initialize the memory allocated by this method.  The memory is owned by
108/// the MemoryBuffer object.
109MemoryBuffer *MemoryBuffer::getNewUninitMemBuffer(size_t Size,
110                                                  const char *BufferName) {
111  char *Buf = (char *)malloc((Size+1) * sizeof(char));
112  if (!Buf) return 0;
113  Buf[Size] = 0;
114  MemoryBufferMem *SB = new MemoryBufferMem(Buf, Buf+Size, BufferName);
115  // The memory for this buffer is owned by the MemoryBuffer.
116  SB->MustDeleteBuffer = true;
117  return SB;
118}
119
120/// getNewMemBuffer - Allocate a new MemoryBuffer of the specified size that
121/// is completely initialized to zeros.  Note that the caller should
122/// initialize the memory allocated by this method.  The memory is owned by
123/// the MemoryBuffer object.
124MemoryBuffer *MemoryBuffer::getNewMemBuffer(size_t Size,
125                                            const char *BufferName) {
126  MemoryBuffer *SB = getNewUninitMemBuffer(Size, BufferName);
127  if (!SB) return 0;
128  memset(const_cast<char*>(SB->getBufferStart()), 0, Size+1);
129  return SB;
130}
131
132
133/// getFileOrSTDIN - Open the specified file as a MemoryBuffer, or open stdin
134/// if the Filename is "-".  If an error occurs, this returns null and fills
135/// in *ErrStr with a reason.  If stdin is empty, this API (unlike getSTDIN)
136/// returns an empty buffer.
137MemoryBuffer *MemoryBuffer::getFileOrSTDIN(const char *Filename,
138                                           std::string *ErrStr,
139                                           int64_t FileSize) {
140  if (Filename[0] != '-' || Filename[1] != 0)
141    return getFile(Filename, ErrStr, FileSize);
142  MemoryBuffer *M = getSTDIN();
143  if (M) return M;
144
145  // If stdin was empty, M is null.  Cons up an empty memory buffer now.
146  const char *EmptyStr = "";
147  return MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<stdin>");
148}
149
150//===----------------------------------------------------------------------===//
151// MemoryBuffer::getFile implementation.
152//===----------------------------------------------------------------------===//
153
154namespace {
155/// MemoryBufferMMapFile - This represents a file that was mapped in with the
156/// sys::Path::MapInFilePages method.  When destroyed, it calls the
157/// sys::Path::UnMapFilePages method.
158class MemoryBufferMMapFile : public MemoryBuffer {
159  std::string Filename;
160public:
161  MemoryBufferMMapFile(const char *filename, const char *Pages, uint64_t Size)
162    : Filename(filename) {
163    init(Pages, Pages+Size);
164  }
165
166  virtual const char *getBufferIdentifier() const {
167    return Filename.c_str();
168  }
169
170  ~MemoryBufferMMapFile() {
171    sys::Path::UnMapFilePages(getBufferStart(), getBufferSize());
172  }
173};
174}
175
176MemoryBuffer *MemoryBuffer::getFile(const char *Filename, std::string *ErrStr,
177                                    int64_t FileSize) {
178  int OpenFlags = 0;
179#ifdef O_BINARY
180  OpenFlags |= O_BINARY;  // Open input file in binary mode on win32.
181#endif
182  int FD = ::open(Filename, O_RDONLY|OpenFlags);
183  if (FD == -1) {
184    if (ErrStr) *ErrStr = "could not open file";
185    return 0;
186  }
187
188  // If we don't know the file size, use fstat to find out.  fstat on an open
189  // file descriptor is cheaper than stat on a random path.
190  if (FileSize == -1) {
191    struct stat FileInfo;
192    // TODO: This should use fstat64 when available.
193    if (fstat(FD, &FileInfo) == -1) {
194      if (ErrStr) *ErrStr = "could not get file length";
195      ::close(FD);
196      return 0;
197    }
198    FileSize = FileInfo.st_size;
199  }
200
201
202  // If the file is large, try to use mmap to read it in.  We don't use mmap
203  // for small files, because this can severely fragment our address space. Also
204  // don't try to map files that are exactly a multiple of the system page size,
205  // as the file would not have the required null terminator.
206  if (FileSize >= 4096*4 &&
207      (FileSize & (sys::Process::GetPageSize()-1)) != 0) {
208    if (const char *Pages = sys::Path::MapInFilePages(FD, FileSize)) {
209      // Close the file descriptor, now that the whole file is in memory.
210      ::close(FD);
211      return new MemoryBufferMMapFile(Filename, Pages, FileSize);
212    }
213  }
214
215  MemoryBuffer *Buf = MemoryBuffer::getNewUninitMemBuffer(FileSize, Filename);
216  if (!Buf) {
217    // Failed to create a buffer.
218    if (ErrStr) *ErrStr = "could not allocate buffer";
219    ::close(FD);
220    return 0;
221  }
222
223  OwningPtr<MemoryBuffer> SB(Buf);
224  char *BufPtr = const_cast<char*>(SB->getBufferStart());
225
226  size_t BytesLeft = FileSize;
227  while (BytesLeft) {
228    ssize_t NumRead = ::read(FD, BufPtr, BytesLeft);
229    if (NumRead > 0) {
230      BytesLeft -= NumRead;
231      BufPtr += NumRead;
232    } else if (NumRead == -1 && errno == EINTR) {
233      // try again
234    } else {
235      // error reading.
236      close(FD);
237      if (ErrStr) *ErrStr = "error reading file data";
238      return 0;
239    }
240  }
241  close(FD);
242
243  return SB.take();
244}
245
246//===----------------------------------------------------------------------===//
247// MemoryBuffer::getSTDIN implementation.
248//===----------------------------------------------------------------------===//
249
250namespace {
251class STDINBufferFile : public MemoryBuffer {
252public:
253  virtual const char *getBufferIdentifier() const {
254    return "<stdin>";
255  }
256};
257}
258
259MemoryBuffer *MemoryBuffer::getSTDIN() {
260  char Buffer[4096*4];
261
262  std::vector<char> FileData;
263
264  // Read in all of the data from stdin, we cannot mmap stdin.
265  sys::Program::ChangeStdinToBinary();
266  size_t ReadBytes;
267  do {
268    ReadBytes = fread(Buffer, sizeof(char), sizeof(Buffer), stdin);
269    FileData.insert(FileData.end(), Buffer, Buffer+ReadBytes);
270  } while (ReadBytes == sizeof(Buffer));
271
272  FileData.push_back(0); // &FileData[Size] is invalid. So is &*FileData.end().
273  size_t Size = FileData.size();
274  if (Size <= 1)
275    return 0;
276  MemoryBuffer *B = new STDINBufferFile();
277  B->initCopyOf(&FileData[0], &FileData[Size-1]);
278  return B;
279}
280