diff --git a/coding/CMakeLists.txt b/coding/CMakeLists.txt index 23f881c047..ded520fc18 100644 --- a/coding/CMakeLists.txt +++ b/coding/CMakeLists.txt @@ -16,6 +16,8 @@ set( base64.hpp bit_streams.hpp buffer_reader.hpp + buffered_file_writer.cpp + buffered_file_writer.hpp bwt_coder.hpp byte_stream.hpp compressed_bit_vector.cpp diff --git a/coding/buffered_file_writer.cpp b/coding/buffered_file_writer.cpp new file mode 100644 index 0000000000..edf98e4f62 --- /dev/null +++ b/coding/buffered_file_writer.cpp @@ -0,0 +1,75 @@ +#include "coding/buffered_file_writer.hpp" + +#include "coding/internal/file_data.hpp" + +BufferedFileWriter::BufferedFileWriter(std::string const & fileName, + Op operation /* = OP_WRITE_TRUNCATE */, + size_t bufferSize /* = 4096 */) + : FileWriter(fileName, operation) +{ + m_buf.reserve(bufferSize); +} + +BufferedFileWriter::~BufferedFileWriter() +{ + DropBuffer(); + GetFileData().Flush(); +} + +void BufferedFileWriter::Seek(uint64_t pos) +{ + DropBuffer(); + FileWriter::Seek(pos); +} + +uint64_t BufferedFileWriter::Pos() const +{ + return FileWriter::Pos() + m_buf.size(); +} + +void BufferedFileWriter::Write(void const * p, size_t size) +{ + // Need to use pointer arithmetic. + auto src = static_cast(p); + + while (size >= m_buf.capacity() - m_buf.size()) + { + if (m_buf.empty()) + { + FileWriter::Write(src, m_buf.capacity()); + src += m_buf.capacity(); + size -= m_buf.capacity(); + } + else + { + auto const copyCount = m_buf.capacity() - m_buf.size(); + std::copy(src, src + copyCount, std::back_inserter(m_buf)); + FileWriter::Write(m_buf.data(), m_buf.size()); + m_buf.clear(); + src += copyCount; + size -= copyCount; + } + } + + std::copy(src, src + size, std::back_inserter(m_buf)); +} + +uint64_t BufferedFileWriter::Size() const +{ + return FileWriter::Size() + m_buf.size(); +} + +void BufferedFileWriter::Flush() +{ + DropBuffer(); + FileWriter::Flush(); +} + +void BufferedFileWriter::DropBuffer() +{ + if (m_buf.empty()) + return; + + GetFileData().Write(m_buf.data(), m_buf.size()); + m_buf.clear(); +} diff --git a/coding/buffered_file_writer.hpp b/coding/buffered_file_writer.hpp new file mode 100644 index 0000000000..fc9d5698b6 --- /dev/null +++ b/coding/buffered_file_writer.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "coding/file_writer.hpp" + +#include +#include +#include + +class BufferedFileWriter : public FileWriter +{ +public: + explicit BufferedFileWriter(std::string const & fileName, Op operation = OP_WRITE_TRUNCATE, + size_t bufferSize = 4096); + + // Writer overrides: + ~BufferedFileWriter() override; + void Seek(uint64_t pos) override; + uint64_t Pos() const override; + void Write(void const * p, size_t size) override; + + // FileWriter overrides: + uint64_t Size() const override; + void Flush() override; + +private: + void DropBuffer(); + + std::vector m_buf; +}; diff --git a/coding/coding_tests/writer_test.cpp b/coding/coding_tests/writer_test.cpp index 545afeee62..4151f2ee41 100644 --- a/coding/coding_tests/writer_test.cpp +++ b/coding/coding_tests/writer_test.cpp @@ -1,5 +1,6 @@ #include "testing/testing.hpp" +#include "coding/buffered_file_writer.hpp" #include "coding/file_writer.hpp" #include "coding/file_reader.hpp" #include "coding/internal/file_data.hpp" @@ -212,16 +213,16 @@ void ReadTestData(Reader & r) string const sub = s.substr(CHUNKS_COUNT * CHUNK_SIZE); TEST_EQUAL(sub, TEST_STRING, (sub, TEST_STRING)); } - -UNIT_TEST(FileWriter_Chunks) +template +void WriteToFileAndTest() { string const TEST_FILE = "FileWriter_Chunks.test"; { - FileWriter fileWriter(TEST_FILE, FileWriter::OP_WRITE_TRUNCATE); + WriterType fileWriter(TEST_FILE, FileWriter::OP_WRITE_TRUNCATE); WriteTestData1(fileWriter); } { - FileWriter fileWriter(TEST_FILE, FileWriter::OP_WRITE_EXISTING); + WriterType fileWriter(TEST_FILE, FileWriter::OP_WRITE_EXISTING); WriteTestData2(fileWriter); } { @@ -231,6 +232,16 @@ UNIT_TEST(FileWriter_Chunks) FileWriter::DeleteFileX(TEST_FILE); } +UNIT_TEST(FileWriter_Chunks) +{ + WriteToFileAndTest(); +} + +UNIT_TEST(BufferedFileWriter_Smoke) +{ + WriteToFileAndTest(); +} + UNIT_TEST(MemWriter_Chunks) { string buffer; diff --git a/coding/file_writer.cpp b/coding/file_writer.cpp index 71dc75603e..0880526f27 100644 --- a/coding/file_writer.cpp +++ b/coding/file_writer.cpp @@ -12,7 +12,7 @@ FileWriter::FileWriter(string const & fileName, FileWriter::Op op) FileWriter::~FileWriter() { - Flush(); + m_pFileData->Flush(); } uint64_t FileWriter::Pos() const diff --git a/coding/file_writer.hpp b/coding/file_writer.hpp index 1e8147fbaf..d08c7eee43 100644 --- a/coding/file_writer.hpp +++ b/coding/file_writer.hpp @@ -47,8 +47,9 @@ public: uint64_t Pos() const override; void Write(void const * p, size_t size) override; - uint64_t Size() const; - void Flush(); + virtual uint64_t Size() const; + virtual void Flush(); + std::string const & GetName() const; static void DeleteFileX(std::string const & fName);