Index: include/llvm/Support/MemoryBuffer.h =================================================================== --- include/llvm/Support/MemoryBuffer.h +++ include/llvm/Support/MemoryBuffer.h @@ -130,6 +130,10 @@ static ErrorOr> getFileOrSTDIN(StringRef Filename, int64_t FileSize = -1); + /// Map a subrange of the the specified file as a MemoryBuffer. + static ErrorOr> + getFileSlice(StringRef Filename, uint64_t Offset, uint64_t Length); + //===--------------------------------------------------------------------===// // Provided for performance analysis. //===--------------------------------------------------------------------===// Index: lib/Support/MemoryBuffer.cpp =================================================================== --- lib/Support/MemoryBuffer.cpp +++ lib/Support/MemoryBuffer.cpp @@ -159,7 +159,29 @@ return getFile(Filename, FileSize); } +static ErrorOr> +getFileSliceImpl(int FD, const char *Filename, uint64_t Offset, uint64_t Size); +ErrorOr> +MemoryBuffer::getFileSlice(StringRef FilePath, uint64_t Offset, + uint64_t MapSize) { + assert(!FilePath.equals("-") && "getFileSlice does not work on stdin"); + // Ensure the path is null terminated. + SmallString<256> PathBuf; + StringRef NullTermName = Twine(FilePath).toNullTerminatedStringRef(PathBuf); + + int FD; + std::error_code EC = sys::fs::openFileForRead(NullTermName.data(), FD); + if (EC) + return EC; + + ErrorOr> Ret + = getFileSliceImpl(FD, NullTermName.data(), Offset, MapSize); + + close(FD); + return Ret; +} + //===----------------------------------------------------------------------===// // MemoryBuffer::getFile implementation. //===----------------------------------------------------------------------===// @@ -315,6 +337,48 @@ } static ErrorOr> +getBufferRead(int FD, const char *Filename, uint64_t MapSize, int64_t Offset) { + std::unique_ptr Buf = + MemoryBuffer::getNewUninitMemBuffer(MapSize, Filename); + if (!Buf) { + // Failed to create a buffer. The only way it can fail is if + // new(std::nothrow) returns 0. + return make_error_code(errc::not_enough_memory); + } + + char *BufPtr = const_cast(Buf->getBufferStart()); + + size_t BytesLeft = MapSize; +#ifndef HAVE_PREAD + if (lseek(FD, Offset, SEEK_SET) == -1) + return std::error_code(errno, std::generic_category()); +#endif + + while (BytesLeft) { +#ifdef HAVE_PREAD + ssize_t NumRead = ::pread(FD, BufPtr, BytesLeft, MapSize-BytesLeft+Offset); +#else + ssize_t NumRead = ::read(FD, BufPtr, BytesLeft); +#endif + if (NumRead == -1) { + if (errno == EINTR) + continue; + // Error while reading. + return std::error_code(errno, std::generic_category()); + } + if (NumRead == 0) { + memset(BufPtr, 0, BytesLeft); // zero-initialize rest of the buffer. + break; + } + BytesLeft -= NumRead; + BufPtr += NumRead; + } + + return std::move(Buf); +} + + +static ErrorOr> getOpenFileImpl(int FD, const char *Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, bool IsVolatileSize) { @@ -353,43 +417,24 @@ return std::move(Result); } - std::unique_ptr Buf = - MemoryBuffer::getNewUninitMemBuffer(MapSize, Filename); - if (!Buf) { - // Failed to create a buffer. The only way it can fail is if - // new(std::nothrow) returns 0. - return make_error_code(errc::not_enough_memory); - } + return getBufferRead(FD, Filename, MapSize, Offset); +} - char *BufPtr = const_cast(Buf->getBufferStart()); - - size_t BytesLeft = MapSize; -#ifndef HAVE_PREAD - if (lseek(FD, Offset, SEEK_SET) == -1) - return std::error_code(errno, std::generic_category()); -#endif - - while (BytesLeft) { -#ifdef HAVE_PREAD - ssize_t NumRead = ::pread(FD, BufPtr, BytesLeft, MapSize-BytesLeft+Offset); -#else - ssize_t NumRead = ::read(FD, BufPtr, BytesLeft); -#endif - if (NumRead == -1) { - if (errno == EINTR) - continue; - // Error while reading. - return std::error_code(errno, std::generic_category()); - } - if (NumRead == 0) { - memset(BufPtr, 0, BytesLeft); // zero-initialize rest of the buffer. - break; - } - BytesLeft -= NumRead; - BufPtr += NumRead; +static ErrorOr> +getFileSliceImpl(int FD, const char *Filename, uint64_t Offset, + uint64_t MapSize) { + static int PageSize = sys::process::get_self()->page_size(); + if (shouldUseMmap(FD, -1, MapSize, Offset, false, PageSize, false)) { + std::error_code EC; + std::unique_ptr Result(new (NamedBufferAlloc(Filename)) + MemoryBufferMMapFile(false, FD, MapSize, Offset, EC)); + if (EC) + return EC; + else + return std::move(Result); } - - return std::move(Buf); + + return getBufferRead(FD, Filename, MapSize, Offset); } ErrorOr> Index: unittests/Support/MemoryBufferTest.cpp =================================================================== --- unittests/Support/MemoryBufferTest.cpp +++ unittests/Support/MemoryBufferTest.cpp @@ -169,4 +169,54 @@ testGetOpenFileSlice(true); } + +TEST_F(MemoryBufferTest, slice) { + // Create a file that is six pages long with different data on each page. + int FD; + SmallString<64> TestPath; + sys::fs::createTemporaryFile("MemoryBufferTest_Slice", "temp", FD, TestPath); + raw_fd_ostream OF(FD, true, /*unbuffered=*/true); + for (unsigned i = 0; i < 0x2000 / 8; ++i) { + OF << "12345678"; + } + for (unsigned i = 0; i < 0x2000 / 8; ++i) { + OF << "abcdefgh"; + } + for (unsigned i = 0; i < 0x2000 / 8; ++i) { + OF << "ABCDEFGH"; + } + OF.close(); + + // Try offset of one page. + ErrorOr MB = MemoryBuffer::getFileSubrange(TestPath.str(), + 0x1000, 0x4000); + std::error_code EC = MB.getError(); + ASSERT_FALSE(EC); + EXPECT_EQ(0x4000UL, MB.get()->getBufferSize()); + + StringRef BufData = MB.get()->getBuffer(); + EXPECT_TRUE(BufData.substr(0x0000,8).equals("12345678")); + EXPECT_TRUE(BufData.substr(0x0FF8,8).equals("12345678")); + EXPECT_TRUE(BufData.substr(0x1000,8).equals("abcdefgh")); + EXPECT_TRUE(BufData.substr(0x2FF8,8).equals("abcdefgh")); + EXPECT_TRUE(BufData.substr(0x3000,8).equals("ABCDEFGH")); + EXPECT_TRUE(BufData.substr(0x3FF8,8).equals("ABCDEFGH")); + + // Try non-page aligned. + ErrorOr MB2 = MemoryBuffer::getFileSubrange(TestPath.str(), + 0x0800, 0x3000); + EC = MB2.getError(); + ASSERT_FALSE(EC); + EXPECT_EQ(0x3000UL, MB2.get()->getBufferSize()); + + StringRef BufData2 = MB2.get()->getBuffer(); + EXPECT_TRUE(BufData2.substr(0x0000,8).equals("12345678")); + EXPECT_TRUE(BufData2.substr(0x17F8,8).equals("12345678")); + EXPECT_TRUE(BufData2.substr(0x1800,8).equals("abcdefgh")); + EXPECT_TRUE(BufData2.substr(0x2FF8,8).equals("abcdefgh")); + } + + + +}