Add a new memorybuffer class, to unify all the file reading code in the system
[oota-llvm.git] / lib / Support / MemoryBuffer.cpp
1 //===--- MemoryBuffer.cpp - Memory Buffer implementation ------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file was developed by Chris Lattner and is distributed under
6 // the University of Illinois Open Source 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/System/MappedFile.h"
16 #include "llvm/System/Process.h"
17 #include <cstdio>
18 #include <cstring>
19 #include <cerrno>
20 using namespace llvm;
21
22 //===----------------------------------------------------------------------===//
23 // MemoryBuffer implementation itself.
24 //===----------------------------------------------------------------------===//
25
26 MemoryBuffer::~MemoryBuffer() {
27   if (MustDeleteBuffer)
28     delete [] BufferStart;
29 }
30
31 /// initCopyOf - Initialize this source buffer with a copy of the specified
32 /// memory range.  We make the copy so that we can null terminate it
33 /// successfully.
34 void MemoryBuffer::initCopyOf(const char *BufStart, const char *BufEnd) {
35   size_t Size = BufEnd-BufStart;
36   BufferStart = new char[Size+1];
37   BufferEnd = BufferStart+Size;
38   memcpy(const_cast<char*>(BufferStart), BufStart, Size);
39   *const_cast<char*>(BufferEnd) = 0;   // Null terminate buffer.
40   MustDeleteBuffer = false;
41 }
42
43 /// init - Initialize this MemoryBuffer as a reference to externally allocated
44 /// memory, memory that we know is already null terminated.
45 void MemoryBuffer::init(const char *BufStart, const char *BufEnd) {
46   assert(BufEnd[0] == 0 && "Buffer is not null terminated!");
47   BufferStart = BufStart;
48   BufferEnd = BufEnd;
49   MustDeleteBuffer = false;
50 }
51
52 //===----------------------------------------------------------------------===//
53 // MemoryBufferMem implementation.
54 //===----------------------------------------------------------------------===//
55
56 namespace {
57 class MemoryBufferMem : public MemoryBuffer {
58   std::string FileID;
59 public:
60   MemoryBufferMem(const char *Start, const char *End, const char *FID)
61   : FileID(FID) {
62     init(Start, End);
63   }
64   
65   virtual const char *getBufferIdentifier() const {
66     return FileID.c_str();
67   }
68 };
69 }
70
71 /// getMemBuffer - Open the specified memory range as a MemoryBuffer.  Note
72 /// that EndPtr[0] must be a null byte and be accessible!
73 MemoryBuffer *MemoryBuffer::getMemBuffer(const char *StartPtr, 
74                                          const char *EndPtr,
75                                          const char *BufferName) {
76   return new MemoryBufferMem(StartPtr, EndPtr, BufferName);
77 }
78
79 /// getNewUninitMemBuffer - Allocate a new MemoryBuffer of the specified size
80 /// that is completely initialized to zeros.  Note that the caller should
81 /// initialize the memory allocated by this method.  The memory is owned by
82 /// the MemoryBuffer object.
83 MemoryBuffer *MemoryBuffer::getNewUninitMemBuffer(unsigned Size,
84                                                   const char *BufferName) {
85   char *Buf = new char[Size+1];
86   Buf[Size] = 0;
87   MemoryBufferMem *SB = new MemoryBufferMem(Buf, Buf+Size, BufferName);
88   // The memory for this buffer is owned by the MemoryBuffer.
89   SB->MustDeleteBuffer = true;
90   return SB;
91 }
92
93 /// getNewMemBuffer - Allocate a new MemoryBuffer of the specified size that
94 /// is completely initialized to zeros.  Note that the caller should
95 /// initialize the memory allocated by this method.  The memory is owned by
96 /// the MemoryBuffer object.
97 MemoryBuffer *MemoryBuffer::getNewMemBuffer(unsigned Size,
98                                             const char *BufferName) {
99   MemoryBuffer *SB = getNewUninitMemBuffer(Size, BufferName);
100   memset(const_cast<char*>(SB->getBufferStart()), 0, Size+1);
101   return SB;
102 }
103
104
105 //===----------------------------------------------------------------------===//
106 // MemoryBufferMMapFile implementation.
107 //===----------------------------------------------------------------------===//
108
109 namespace {
110 class MemoryBufferMMapFile : public MemoryBuffer {
111   sys::MappedFile File;
112 public:
113   MemoryBufferMMapFile(const sys::Path &Filename);
114   
115   virtual const char *getBufferIdentifier() const {
116     return File.path().c_str();
117   }
118     
119   ~MemoryBufferMMapFile();
120 };
121 }
122
123 MemoryBufferMMapFile::MemoryBufferMMapFile(const sys::Path &Filename) {
124   // FIXME: This does an extra stat syscall to figure out the size, but we
125   // already know the size!
126   bool Failure = File.open(Filename);
127   Failure = Failure;  // Silence warning in no-asserts mode.
128   assert(!Failure && "Can't open file??");
129   
130   File.map();
131   
132   size_t Size = File.size();
133   
134   static unsigned PageSize = sys::Process::GetPageSize();
135   assert(((PageSize & (PageSize-1)) == 0) && PageSize &&
136          "Page size is not a power of 2!");
137   
138   // If this file is not an exact multiple of the system page size (common
139   // case), then the OS has zero terminated the buffer for us.
140   if ((Size & (PageSize-1))) {
141     init(File.charBase(), File.charBase()+Size);
142   } else {
143     // Otherwise, we allocate a new memory buffer and copy the data over
144     initCopyOf(File.charBase(), File.charBase()+Size);
145     
146     // No need to keep the file mapped any longer.
147     File.unmap();
148   }
149 }
150
151 MemoryBufferMMapFile::~MemoryBufferMMapFile() {
152   File.unmap();
153 }
154
155 //===----------------------------------------------------------------------===//
156 // MemoryBuffer::getFile implementation.
157 //===----------------------------------------------------------------------===//
158
159 MemoryBuffer *MemoryBuffer::getFile(const char *FilenameStart, unsigned FnSize,
160                                     int64_t FileSize) {
161   sys::PathWithStatus P(FilenameStart, FnSize);
162 #if 1
163   return new MemoryBufferMMapFile(P);
164 #else
165   // FIXME: We need an efficient and portable method to open a file and then use
166   // 'read' to copy the bits out.  The unix implementation is below.  This is
167   // an important optimization for clients that want to open large numbers of
168   // small files (using mmap on everything can easily exhaust address space!).
169   
170   // If the user didn't specify a filesize, do a stat to find it.
171   if (FileSize == -1) {
172     const sys::FileStatus *FS = P.getFileStatus();
173     if (FS == 0) return 0;  // Error stat'ing file.
174    
175     FileSize = FS->fileSize;
176   }
177   
178   // If the file is larger than some threshold, use mmap, otherwise use 'read'.
179   if (FileSize >= 4096*4)
180     return new MemoryBufferMMapFile(P);
181   
182   MemoryBuffer *SB = getNewUninitMemBuffer(FileSize, FilenameStart);
183   char *BufPtr = const_cast<char*>(SB->getBufferStart());
184   
185   int FD = ::open(FilenameStart, O_RDONLY);
186   if (FD == -1) {
187     delete SB;
188     return 0;
189   }
190   
191   unsigned BytesLeft = FileSize;
192   while (BytesLeft) {
193     ssize_t NumRead = ::read(FD, BufPtr, BytesLeft);
194     if (NumRead != -1) {
195       BytesLeft -= NumRead;
196       BufPtr += NumRead;
197     } else if (errno == EINTR) {
198       // try again
199     } else {
200       // error reading.
201       close(FD);
202       delete SB;
203       return 0;
204     }
205   }
206   close(FD);
207   
208   return SB;
209 #endif
210 }
211
212
213 //===----------------------------------------------------------------------===//
214 // MemoryBuffer::getSTDIN implementation.
215 //===----------------------------------------------------------------------===//
216
217 namespace {
218 class STDINBufferFile : public MemoryBuffer {
219 public:
220   virtual const char *getBufferIdentifier() const {
221     return "<stdin>";
222   }
223 };
224 }
225
226 MemoryBuffer *MemoryBuffer::getSTDIN() {
227   char Buffer[4096*4];
228   
229   std::vector<char> FileData;
230   
231   // Read in all of the data from stdin, we cannot mmap stdin.
232   while (size_t ReadBytes = fread(Buffer, 1, 4096*4, stdin))
233     FileData.insert(FileData.end(), Buffer, Buffer+ReadBytes);
234   
235   size_t Size = FileData.size();
236   MemoryBuffer *B = new STDINBufferFile();
237   B->initCopyOf(&FileData[0], &FileData[Size]);
238   return B;
239 }