/// @returns The number of bytes read, or error.
Expected<size_t> readNativeFile(file_t FileHandle, MutableArrayRef<char> Buf);
+/// Default chunk size for \a readNativeFileToEOF().
+enum : size_t { DefaultReadChunkSize = 4 * 4096 };
+
+/// Reads from \p FileHandle until EOF, appending to \p Buffer in chunks of
+/// size \p ChunkSize.
+///
+/// This calls \a readNativeFile() in a loop. On Error, previous chunks that
+/// were read successfully are left in \p Buffer and returned.
+///
+/// Note: For reading the final chunk at EOF, \p Buffer's capacity needs extra
+/// storage of \p ChunkSize.
+///
+/// \param FileHandle File to read from.
+/// \param Buffer Where to put the file content.
+/// \param ChunkSize Size of chunks.
+/// \returns The error if EOF was not found.
+Error readNativeFileToEOF(file_t FileHandle, SmallVectorImpl<char> &Buffer,
+ ssize_t ChunkSize = DefaultReadChunkSize);
+
/// Reads \p Buf.size() bytes from \p FileHandle at offset \p Offset into \p
/// Buf. If 'pread' is available, this will use that, otherwise it will use
/// 'lseek'. Returns the number of bytes actually read. Returns 0 when reaching
static ErrorOr<std::unique_ptr<WritableMemoryBuffer>>
getMemoryBufferForStream(sys::fs::file_t FD, const Twine &BufferName) {
- const ssize_t ChunkSize = 4096*4;
- SmallString<ChunkSize> Buffer;
-
- // Read into Buffer until we hit EOF.
- size_t Size = Buffer.size();
- for (;;) {
- Buffer.resize_for_overwrite(Size + ChunkSize);
- Expected<size_t> ReadBytes = sys::fs::readNativeFile(
- FD, makeMutableArrayRef(Buffer.begin() + Size, ChunkSize));
- if (!ReadBytes)
- return errorToErrorCode(ReadBytes.takeError());
- if (*ReadBytes == 0)
- break;
- Size += *ReadBytes;
- }
- Buffer.truncate(Size);
-
+ SmallString<sys::fs::DefaultReadChunkSize> Buffer;
+ if (Error E = sys::fs::readNativeFileToEOF(FD, Buffer))
+ return errorToErrorCode(std::move(E));
return getMemBufferCopyImpl(Buffer, BufferName);
}
#include "llvm/Support/Path.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/ScopeExit.h"
#include "llvm/Config/config.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/Support/Endian.h"
return reinterpret_cast<const char *>(Mapping);
}
+Error readNativeFileToEOF(file_t FileHandle, SmallVectorImpl<char> &Buffer,
+ ssize_t ChunkSize) {
+ // Install a handler to truncate the buffer to the correct size on exit.
+ size_t Size = Buffer.size();
+ auto TruncateOnExit = make_scope_exit([&]() { Buffer.truncate(Size); });
+
+ // Read into Buffer until we hit EOF.
+ for (;;) {
+ Buffer.resize_for_overwrite(Size + ChunkSize);
+ Expected<size_t> ReadBytes = readNativeFile(
+ FileHandle, makeMutableArrayRef(Buffer.begin() + Size, ChunkSize));
+ if (!ReadBytes)
+ return ReadBytes.takeError();
+ if (*ReadBytes == 0)
+ return Error::success();
+ Size += *ReadBytes;
+ }
+}
+
} // end namespace fs
} // end namespace sys
} // end namespace llvm
EXPECT_THAT_EXPECTED(Read(6), HasValue("01234"));
}
+TEST_F(FileSystemTest, readNativeFileToEOF) {
+ constexpr StringLiteral Content = "0123456789";
+ createFileWithData(NonExistantFile, false, fs::CD_CreateNew, Content);
+ FileRemover Cleanup(NonExistantFile);
+ const auto &Read = [&](SmallVectorImpl<char> &V,
+ Optional<ssize_t> ChunkSize) {
+ Expected<fs::file_t> FD = fs::openNativeFileForRead(NonExistantFile);
+ if (!FD)
+ return FD.takeError();
+ auto Close = make_scope_exit([&] { fs::closeFile(*FD); });
+ if (ChunkSize)
+ return fs::readNativeFileToEOF(*FD, V, *ChunkSize);
+ return fs::readNativeFileToEOF(*FD, V);
+ };
+
+ // Check basic operation.
+ {
+ SmallString<0> NoSmall;
+ SmallString<fs::DefaultReadChunkSize + Content.size()> StaysSmall;
+ SmallVectorImpl<char> *Vectors[] = {
+ static_cast<SmallVectorImpl<char> *>(&NoSmall),
+ static_cast<SmallVectorImpl<char> *>(&StaysSmall),
+ };
+ for (SmallVectorImpl<char> *V : Vectors) {
+ ASSERT_THAT_ERROR(Read(*V, None), Succeeded());
+ ASSERT_EQ(Content, StringRef(V->begin(), V->size()));
+ }
+ ASSERT_EQ(fs::DefaultReadChunkSize + Content.size(), StaysSmall.capacity());
+
+ // Check appending.
+ {
+ constexpr StringLiteral Prefix = "prefix-";
+ for (SmallVectorImpl<char> *V : Vectors) {
+ V->assign(Prefix.begin(), Prefix.end());
+ ASSERT_THAT_ERROR(Read(*V, None), Succeeded());
+ ASSERT_EQ((Prefix + Content).str(), StringRef(V->begin(), V->size()));
+ }
+ }
+ }
+
+ // Check that the chunk size (if specified) is respected.
+ SmallString<Content.size() + 5> SmallChunks;
+ ASSERT_THAT_ERROR(Read(SmallChunks, 5), Succeeded());
+ ASSERT_EQ(SmallChunks, Content);
+ ASSERT_EQ(Content.size() + 5, SmallChunks.capacity());
+}
+
TEST_F(FileSystemTest, readNativeFileSlice) {
createFileWithData(NonExistantFile, false, fs::CD_CreateNew, "01234");
FileRemover Cleanup(NonExistantFile);