llvm.org GIT mirror llvm / c29d246
[Support] Add WriteThroughMemoryBuffer. This is like MemoryBuffer (read-only) and WritableMemoryBuffer (writable private), but where the underlying file can be modified after writing. This is useful when you want to open a file, make some targeted edits, and then write it back out. Differential Revision: https://reviews.llvm.org/D44230 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@327057 91177308-0d34-0410-b5e6-96231b3b80d8 Zachary Turner 2 years ago
6 changed file(s) with 152 addition(s) and 16 deletion(s). Raw diff Collapse all Expand all
677677 /// with F_Excl.
678678 F_Append = 2,
679679
680 /// F_NoTrunc - When opening a file, if it already exists don't truncate
681 /// the file contents. F_Append implies F_NoTrunc, but F_Append seeks to
682 /// the end of the file, which F_NoTrunc doesn't.
683 F_NoTrunc = 4,
684
680685 /// The file should be opened in text mode on platforms that make this
681686 /// distinction.
682 F_Text = 4,
687 F_Text = 8,
683688
684689 /// Open the file for read and write.
685 F_RW = 8,
690 F_RW = 16,
686691
687692 /// Delete the file on close. Only makes a difference on windows.
688 F_Delete = 16
693 F_Delete = 32
689694 };
690695
691696 /// @brief Create a uniquely named file.
1919 #include "llvm/ADT/Twine.h"
2020 #include "llvm/Support/CBindingWrapping.h"
2121 #include "llvm/Support/ErrorOr.h"
22 #include "llvm/Support/FileSystem.h"
2223 #include
2324 #include
2425 #include
4849 void init(const char *BufStart, const char *BufEnd,
4950 bool RequiresNullTerminator);
5051
51 static constexpr bool Writable = false;
52 static constexpr sys::fs::mapped_file_region::mapmode Mapmode =
53 sys::fs::mapped_file_region::readonly;
5254
5355 public:
5456 MemoryBuffer(const MemoryBuffer &) = delete;
147149 MemoryBufferRef getMemBufferRef() const;
148150 };
149151
150 /// This class is an extension of MemoryBuffer, which allows writing to the
151 /// underlying contents. It only supports creation methods that are guaranteed
152 /// to produce a writable buffer. For example, mapping a file read-only is not
153 /// supported.
152 /// This class is an extension of MemoryBuffer, which allows copy-on-write
153 /// access to the underlying contents. It only supports creation methods that
154 /// are guaranteed to produce a writable buffer. For example, mapping a file
155 /// read-only is not supported.
154156 class WritableMemoryBuffer : public MemoryBuffer {
155157 protected:
156158 WritableMemoryBuffer() = default;
157159
158 static constexpr bool Writable = true;
160 static constexpr sys::fs::mapped_file_region::mapmode Mapmode =
161 sys::fs::mapped_file_region::priv;
159162
160163 public:
161164 using MemoryBuffer::getBuffer;
208211 using MemoryBuffer::getSTDIN;
209212 };
210213
214 /// This class is an extension of MemoryBuffer, which allows write access to
215 /// the underlying contents and committing those changes to the original source.
216 /// It only supports creation methods that are guaranteed to produce a writable
217 /// buffer. For example, mapping a file read-only is not supported.
218 class WriteThroughMemoryBuffer : public MemoryBuffer {
219 protected:
220 WriteThroughMemoryBuffer() = default;
221
222 static constexpr sys::fs::mapped_file_region::mapmode Mapmode =
223 sys::fs::mapped_file_region::readwrite;
224
225 public:
226 using MemoryBuffer::getBuffer;
227 using MemoryBuffer::getBufferEnd;
228 using MemoryBuffer::getBufferStart;
229
230 // const_cast is well-defined here, because the underlying buffer is
231 // guaranteed to have been initialized with a mutable buffer.
232 char *getBufferStart() {
233 return const_cast(MemoryBuffer::getBufferStart());
234 }
235 char *getBufferEnd() {
236 return const_cast(MemoryBuffer::getBufferEnd());
237 }
238 MutableArrayRef getBuffer() {
239 return {getBufferStart(), getBufferEnd()};
240 }
241
242 static ErrorOr>
243 getFile(const Twine &Filename, int64_t FileSize = -1);
244
245 /// Map a subrange of the specified file as a ReadWriteMemoryBuffer.
246 static ErrorOr>
247 getFileSlice(const Twine &Filename, uint64_t MapSize, uint64_t Offset);
248
249 private:
250 // Hide these base class factory function so one can't write
251 // WritableMemoryBuffer::getXXX()
252 // and be surprised that he got a read-only Buffer.
253 using MemoryBuffer::getFileAsStream;
254 using MemoryBuffer::getFileOrSTDIN;
255 using MemoryBuffer::getMemBuffer;
256 using MemoryBuffer::getMemBufferCopy;
257 using MemoryBuffer::getOpenFile;
258 using MemoryBuffer::getOpenFileSlice;
259 using MemoryBuffer::getSTDIN;
260 };
261
211262 class MemoryBufferRef {
212263 StringRef Buffer;
213264 StringRef Identifier;
183183 public:
184184 MemoryBufferMMapFile(bool RequiresNullTerminator, int FD, uint64_t Len,
185185 uint64_t Offset, std::error_code &EC)
186 : MFR(FD,
187 MB::Writable ? sys::fs::mapped_file_region::priv
188 : sys::fs::mapped_file_region::readonly,
189 getLegalMapSize(Len, Offset), getLegalMapOffset(Offset), EC) {
186 : MFR(FD, MB::Mapmode, getLegalMapSize(Len, Offset),
187 getLegalMapOffset(Offset), EC) {
190188 if (!EC) {
191189 const char *Start = getStart(Len, Offset);
192190 MemoryBuffer::init(Start, Start + Len, RequiresNullTerminator);
358356 #endif
359357
360358 return true;
359 }
360
361 static ErrorOr>
362 getReadWriteFile(const Twine &Filename, int64_t FileSize, uint64_t MapSize,
363 uint64_t Offset) {
364 int FD;
365 std::error_code EC = sys::fs::openFileForWrite(
366 Filename, FD, sys::fs::F_RW | sys::fs::F_NoTrunc);
367
368 if (EC)
369 return EC;
370
371 // Default is to map the full file.
372 if (MapSize == uint64_t(-1)) {
373 // If we don't know the file size, use fstat to find out. fstat on an open
374 // file descriptor is cheaper than stat on a random path.
375 if (FileSize == uint64_t(-1)) {
376 sys::fs::file_status Status;
377 std::error_code EC = sys::fs::status(FD, Status);
378 if (EC)
379 return EC;
380
381 // If this not a file or a block device (e.g. it's a named pipe
382 // or character device), we can't mmap it, so error out.
383 sys::fs::file_type Type = Status.type();
384 if (Type != sys::fs::file_type::regular_file &&
385 Type != sys::fs::file_type::block_file)
386 return make_error_code(errc::invalid_argument);
387
388 FileSize = Status.getSize();
389 }
390 MapSize = FileSize;
391 }
392
393 std::unique_ptr Result(
394 new (NamedBufferAlloc(Filename))
395 MemoryBufferMMapFile(false, FD, MapSize,
396 Offset, EC));
397 if (EC)
398 return EC;
399 return std::move(Result);
400 }
401
402 ErrorOr>
403 WriteThroughMemoryBuffer::getFile(const Twine &Filename, int64_t FileSize) {
404 return getReadWriteFile(Filename, FileSize, FileSize, 0);
405 }
406
407 /// Map a subrange of the specified file as a WritableMemoryBuffer.
408 ErrorOr>
409 WriteThroughMemoryBuffer::getFileSlice(const Twine &Filename, uint64_t MapSize,
410 uint64_t Offset) {
411 return getReadWriteFile(Filename, -1, MapSize, Offset);
361412 }
362413
363414 template
791791
792792 if (Flags & F_Append)
793793 OpenFlags |= O_APPEND;
794 else
794 else if (!(Flags & F_NoTrunc))
795795 OpenFlags |= O_TRUNC;
796796
797797 if (Flags & F_Excl)
11001100 DWORD CreationDisposition;
11011101 if (Flags & F_Excl)
11021102 CreationDisposition = CREATE_NEW;
1103 else if (Flags & F_Append)
1103 else if ((Flags & F_Append) || (Flags & F_NoTrunc))
11041104 CreationDisposition = OPEN_ALWAYS;
11051105 else
11061106 CreationDisposition = CREATE_ALWAYS;
259259 for (size_t i = 0; i < MB.getBufferSize(); i += 0x10)
260260 EXPECT_EQ("0123456789abcdef", MB.getBuffer().substr(i, 0x10)) << "i: " << i;
261261 }
262 }
262
263 TEST_F(MemoryBufferTest, writeThroughFile) {
264 // Create a file initialized with some data
265 int FD;
266 SmallString<64> TestPath;
267 sys::fs::createTemporaryFile("MemoryBufferTest_WriteThrough", "temp", FD,
268 TestPath);
269 FileRemover Cleanup(TestPath);
270 raw_fd_ostream OF(FD, true);
271 OF << "0123456789abcdef";
272 OF.close();
273 {
274 auto MBOrError = WriteThroughMemoryBuffer::getFile(TestPath);
275 ASSERT_FALSE(MBOrError.getError());
276 // Write some data. It should be mapped readwrite, so that upon completion
277 // the original file contents are modified.
278 WriteThroughMemoryBuffer &MB = **MBOrError;
279 ASSERT_EQ(16, MB.getBufferSize());
280 char *Start = MB.getBufferStart();
281 ASSERT_EQ(MB.getBufferEnd(), MB.getBufferStart() + MB.getBufferSize());
282 ::memset(Start, 'x', MB.getBufferSize());
283 }
284
285 auto MBOrError = MemoryBuffer::getFile(TestPath);
286 ASSERT_FALSE(MBOrError.getError());
287 auto &MB = **MBOrError;
288 ASSERT_EQ(16, MB.getBufferSize());
289 EXPECT_EQ("xxxxxxxxxxxxxxxx", MB.getBuffer());
290 }
291 }