From 2c43eaa50ced53571de8a719946d2078fc8ea202 Mon Sep 17 00:00:00 2001 From: Sahan Fernando Date: Tue, 7 Dec 2021 01:16:01 +1100 Subject: [PATCH] LibCore: Add support for range-based for loops on LineIterators --- Tests/LibCore/TestLibCoreIODevice.cpp | 47 ++++++++++++++++--------- Userland/Libraries/LibCore/IODevice.cpp | 4 +++ Userland/Libraries/LibCore/IODevice.h | 20 +++++++++++ 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/Tests/LibCore/TestLibCoreIODevice.cpp b/Tests/LibCore/TestLibCoreIODevice.cpp index f8e4ba8080..0af3f820e9 100644 --- a/Tests/LibCore/TestLibCoreIODevice.cpp +++ b/Tests/LibCore/TestLibCoreIODevice.cpp @@ -8,6 +8,14 @@ #include #include +static bool files_have_same_contents(String filename1, String filename2) +{ + auto file1 = Core::File::open(filename1, Core::OpenMode::ReadOnly).value(); + auto file2 = Core::File::open(filename2, Core::OpenMode::ReadOnly).value(); + auto contents1 = file1->read_all(), contents2 = file2->read_all(); + return contents1 == contents2; +} + TEST_CASE(file_readline) { auto path = "long_lines.txt"; @@ -26,23 +34,7 @@ TEST_CASE(file_readline) } file->close(); outputfile->close(); - - // Open files again for comparison since otherwise read_all returns empty (even when not closing the file) - file = Core::File::construct(path); - if (!file->open(Core::OpenMode::ReadOnly)) - VERIFY_NOT_REACHED(); - outputfile = Core::File::construct(output_path); - if (!outputfile->open(Core::OpenMode::ReadOnly)) - VERIFY_NOT_REACHED(); - auto inputData = file->read_all(); - auto outputData = outputfile->read_all(); - EXPECT(inputData.size() > 0); - EXPECT_EQ(inputData.size(), outputData.size()); - - // Compare char by char - for (size_t i = 0; i < inputData.size(); i++) { - EXPECT_EQ(inputData[i], outputData[i]); - } + VERIFY(files_have_same_contents(path, output_path)); } TEST_CASE(file_get_read_position) @@ -76,3 +68,24 @@ TEST_CASE(file_get_read_position) EXPECT_EQ(offset, 0); } } + +TEST_CASE(file_lines_range) +{ + auto path = "long_lines.txt"; + auto file_or_error = Core::File::open(path, Core::OpenMode::ReadOnly); + if (file_or_error.is_error()) { + warnln("Failed to open {}: {}", path, file_or_error.error()); + VERIFY_NOT_REACHED(); + } + auto file = file_or_error.release_value(); + auto output_path = "/tmp/output.txt"; + auto outfile_or_error = Core::File::open(output_path, Core::OpenMode::WriteOnly); + auto outputfile = outfile_or_error.release_value(); + for (auto line : file->lines()) { + outputfile->write(line); + outputfile->write("\n"); + } + file->close(); + outputfile->close(); + VERIFY(files_have_same_contents(path, output_path)); +} diff --git a/Userland/Libraries/LibCore/IODevice.cpp b/Userland/Libraries/LibCore/IODevice.cpp index 0df99b0814..1911c8936e 100644 --- a/Userland/Libraries/LibCore/IODevice.cpp +++ b/Userland/Libraries/LibCore/IODevice.cpp @@ -319,4 +319,8 @@ LineIterator& LineIterator::operator++() m_buffer = m_device->read_line(); return *this; } + +LineIterator LineRange::begin() { return m_device.line_begin(); } +LineIterator LineRange::end() { return m_device.line_end(); } + } diff --git a/Userland/Libraries/LibCore/IODevice.h b/Userland/Libraries/LibCore/IODevice.h index 1caa82a5c7..752963483b 100644 --- a/Userland/Libraries/LibCore/IODevice.h +++ b/Userland/Libraries/LibCore/IODevice.h @@ -12,6 +12,8 @@ namespace Core { +class IODevice; + // This is not necessarily a valid iterator in all contexts, // if we had concepts, this would be InputIterator, not Copyable, Movable. class LineIterator { @@ -34,6 +36,20 @@ private: String m_buffer; }; +class LineRange { +public: + LineRange() = delete; + explicit LineRange(IODevice& device) + : m_device(device) + { + } + LineIterator begin(); + LineIterator end(); + +private: + IODevice& m_device; +}; + enum class OpenMode : unsigned { NotOpen = 0, ReadOnly = 1, @@ -91,6 +107,10 @@ public: LineIterator line_begin() & { return LineIterator(*this); } LineIterator line_end() { return LineIterator(*this, true); } + LineRange lines() + { + return LineRange(*this); + } protected: explicit IODevice(Object* parent = nullptr);