Index: include/llvm/Support/FileUtilities.h =================================================================== --- include/llvm/Support/FileUtilities.h +++ include/llvm/Support/FileUtilities.h @@ -15,6 +15,7 @@ #ifndef LLVM_SUPPORT_FILEUTILITIES_H #define LLVM_SUPPORT_FILEUTILITIES_H +#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" @@ -75,6 +76,81 @@ /// will not be removed when the object is destroyed. void releaseFile() { DeleteIt = false; } }; + + /// \brief Small text file reader. + /// + /// This class defines a very simple interface to read text files one line + /// at a time. It keeps track of line number information and location of + /// the file pointer. Users of this class are responsible for actually + /// parsing the lines returned by the readLine() function. + /// + /// The reader optionally supports comment markers. A comment marker is + /// a single character. If a comment marker is specified + /// at construction time, lines starting with that marker will be + /// ignored. In those cases, readLine() will return an empty StringRef. + /// + /// Note that this uses MemoryBuffer for reading the file, so it + /// should not be used on large files. + class FileReader { + public: + FileReader(StringRef F, const char Marker = '\0') + : Filename(F), CommentMarker(Marker) { + error_code EC; + EC = MemoryBuffer::getFile(Filename, Buffer); + if (EC) + report_fatal_error("Could not open file " + Filename + ": " + + EC.message()); + FP = Buffer->getBufferStart(); + Lineno = 0; + } + + /// \brief Read a line from the mapped file. + StringRef readLine() { + size_t Length = 0; + const char *start = FP; + while (FP != Buffer->getBufferEnd() && *FP != '\n') { + Length++; + FP++; + } + if (FP != Buffer->getBufferEnd()) + FP++; + Lineno++; + // If we are using comment markers and the line started with one, + // return an empty string. + if (CommentMarker && start[0] == CommentMarker) + return StringRef(); + return StringRef(start, Length); + } + + /// \brief Return true, if we've reached EOF. + bool atEOF() const { return FP == Buffer->getBufferEnd(); } + + /// \brief Return the current line number. + int64_t getLineno() const { return Lineno; } + + /// \brief Report a parse error message and stop compilation. + void reportParseError(Twine Msg) const { + report_fatal_error(Filename + ":" + Twine(Lineno) + ": " + Msg + "\n"); + } + + private: + /// \brief Memory buffer holding the text file. + OwningPtr Buffer; + + /// \brief Current position into the memory buffer. + const char *FP; + + /// \brief Current line number. + int64_t Lineno; + + /// \brief Path name to the file being read. + StringRef Filename; + + /// \brief Comment marker. If this is non-zero, lines starting with + /// this character are ignored. + const char CommentMarker; + }; + } // End llvm namespace #endif Index: lib/Support/FileUtilities.cpp =================================================================== --- lib/Support/FileUtilities.cpp +++ lib/Support/FileUtilities.cpp @@ -257,3 +257,61 @@ return CompareFailed; } + +/// \brief Loader class for text-based profiles. +/// +/// This class defines a simple interface to read text files containing +/// profiles. It keeps track of line number information and location of +/// the file pointer. Users of this class are responsible for actually +/// parsing the lines returned by the readLine function. +/// +/// TODO - This does not really belong here. It is a generic text file +/// reader. It should be moved to the Support library and made more general. +class ExternalProfileTextLoader { +public: + ExternalProfileTextLoader(StringRef F) : Filename(F) { + error_code EC; + EC = MemoryBuffer::getFile(Filename, Buffer); + if (EC) + report_fatal_error("Could not open profile file " + Filename + ": " + + EC.message()); + FP = Buffer->getBufferStart(); + Lineno = 0; + } + + /// \brief Read a line from the mapped file. + StringRef readLine() { + size_t Length = 0; + const char *start = FP; + while (FP != Buffer->getBufferEnd() && *FP != '\n') { + Length++; + FP++; + } + if (FP != Buffer->getBufferEnd()) + FP++; + Lineno++; + return StringRef(start, Length); + } + + /// \brief Return true, if we've reached EOF. + bool atEOF() const { return FP == Buffer->getBufferEnd(); } + + /// \brief Report a parse error message and stop compilation. + void reportParseError(Twine Msg) const { + report_fatal_error(Filename + ":" + Twine(Lineno) + ": " + Msg + "\n"); + } + +private: + /// \brief Memory buffer holding the text file. + OwningPtr Buffer; + + /// \brief Current position into the memory buffer. + const char *FP; + + /// \brief Current line number. + int64_t Lineno; + + /// \brief Path name where to the profile file. + StringRef Filename; +}; + Index: lib/Transforms/Scalar/SampleProfile.cpp =================================================================== --- lib/Transforms/Scalar/SampleProfile.cpp +++ lib/Transforms/Scalar/SampleProfile.cpp @@ -44,8 +44,8 @@ #include "llvm/Pass.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/FileUtilities.h" #include "llvm/Support/InstIterator.h" -#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Regex.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Transforms/Scalar.h" @@ -221,63 +221,6 @@ StringRef Filename; }; -/// \brief Loader class for text-based profiles. -/// -/// This class defines a simple interface to read text files containing -/// profiles. It keeps track of line number information and location of -/// the file pointer. Users of this class are responsible for actually -/// parsing the lines returned by the readLine function. -/// -/// TODO - This does not really belong here. It is a generic text file -/// reader. It should be moved to the Support library and made more general. -class ExternalProfileTextLoader { -public: - ExternalProfileTextLoader(StringRef F) : Filename(F) { - error_code EC; - EC = MemoryBuffer::getFile(Filename, Buffer); - if (EC) - report_fatal_error("Could not open profile file " + Filename + ": " + - EC.message()); - FP = Buffer->getBufferStart(); - Lineno = 0; - } - - /// \brief Read a line from the mapped file. - StringRef readLine() { - size_t Length = 0; - const char *start = FP; - while (FP != Buffer->getBufferEnd() && *FP != '\n') { - Length++; - FP++; - } - if (FP != Buffer->getBufferEnd()) - FP++; - Lineno++; - return StringRef(start, Length); - } - - /// \brief Return true, if we've reached EOF. - bool atEOF() const { return FP == Buffer->getBufferEnd(); } - - /// \brief Report a parse error message and stop compilation. - void reportParseError(Twine Msg) const { - report_fatal_error(Filename + ":" + Twine(Lineno) + ": " + Msg + "\n"); - } - -private: - /// \brief Memory buffer holding the text file. - OwningPtr Buffer; - - /// \brief Current position into the memory buffer. - const char *FP; - - /// \brief Current line number. - int64_t Lineno; - - /// \brief Path name where to the profile file. - StringRef Filename; -}; - /// \brief Sample profile pass. /// /// This pass reads profile data from the file specified by @@ -415,7 +358,7 @@ /// profiles for large programs, as the representation is extremely /// inefficient. void SampleModuleProfile::loadText() { - ExternalProfileTextLoader Loader(Filename); + FileReader Loader(Filename, '#'); // Read the symbol table. StringRef Line = Loader.readLine(); Index: unittests/Support/CMakeLists.txt =================================================================== --- unittests/Support/CMakeLists.txt +++ unittests/Support/CMakeLists.txt @@ -17,6 +17,7 @@ EndianTest.cpp ErrorOrTest.cpp FileOutputBufferTest.cpp + FileReaderTest.cpp LeakDetectorTest.cpp LockFileManagerTest.cpp ManagedStatic.cpp Index: unittests/Support/FileReaderTest.cpp =================================================================== --- /dev/null +++ unittests/Support/FileReaderTest.cpp @@ -0,0 +1,85 @@ +//===- llvm/unittest/Support/FileReader.cpp - unit tests ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileUtilities.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::sys; + +#define ASSERT_NO_ERROR(x) \ + if (error_code ASSERT_NO_ERROR_ec = x) { \ + errs() << #x ": did not return errc::success.\n" \ + << "error number: " << ASSERT_NO_ERROR_ec.value() << "\n" \ + << "error message: " << ASSERT_NO_ERROR_ec.message() << "\n"; \ + } else { \ + } + +namespace { + +TEST(FileReader, Test) { + // Create unique temporary directory for these tests. + SmallString<128> TestDirectory; + { + ASSERT_NO_ERROR( + fs::createUniqueDirectory("FileReader-test", TestDirectory)); + } + + // Create a small text file. + SmallString<128> File(TestDirectory); + File.append("/file.txt"); + OwningPtr Buffer; + ASSERT_NO_ERROR(FileOutputBuffer::create(File, 8192, Buffer)); + const char contents[] = "line 1\nline 2\nline 3\n; commented line\n"; + memcpy(Buffer->getBufferStart(), contents, sizeof(contents)); + ASSERT_NO_ERROR(Buffer->commit()); + + // Test 1: Read it with no comment markers. + { + FileReader Reader(File); + EXPECT_FALSE(Reader.atEOF()); + StringRef Line; + Line = Reader.readLine(); + EXPECT_EQ(Line, "line 1"); + Line = Reader.readLine(); + EXPECT_EQ(Line, "line 2"); + EXPECT_EQ(Reader.getLineno(), 2); + Line = Reader.readLine(); + EXPECT_EQ(Line, "line 3"); + Line = Reader.readLine(); + EXPECT_EQ(Line, "; commented line"); + Reader.readLine(); + EXPECT_EQ(Reader.getLineno(), 5); + EXPECT_TRUE(Reader.atEOF()); + } + + // Test 2: Read it with ';' as a comment marker. + { + FileReader Reader(File, ';'); + for (int i = 0; i < 3; ++i) + Reader.readLine(); + EXPECT_EQ(Reader.getLineno(), 3); + StringRef Empty = Reader.readLine(); + EXPECT_EQ(Empty, ""); + Reader.readLine(); + EXPECT_TRUE(Reader.atEOF()); + } + + // Clean up. + uint32_t RemovedCount; + ASSERT_NO_ERROR(fs::remove_all(TestDirectory.str(), RemovedCount)); +} + +} // anonymous namespace