/* SPDX-License-Identifier: BSL-1.0 OR BSD-3-Clause */ #ifndef MPT_IO_WRITE_BUFFER_HPP #define MPT_IO_WRITE_BUFFER_HPP #include "mpt/base/memory.hpp" #include "mpt/base/namespace.hpp" #include "mpt/base/integer.hpp" #include "mpt/io/base.hpp" #include namespace mpt { inline namespace MPT_INLINE_NS { namespace IO { // WriteBuffer class that avoids calling to the underlying file writing // functions for every operation, which would involve rather slow un-inlinable // virtual calls in the iostream and FILE* cases. It is the users responabiliy // to call HasWriteError() to check for writeback errors at this buffering // level. template class WriteBuffer { private: mpt::byte_span buffer; std::size_t size = 0; Tfile & f; bool writeError = false; public: WriteBuffer(const WriteBuffer &) = delete; WriteBuffer & operator=(const WriteBuffer &) = delete; public: inline WriteBuffer(Tfile & f_, mpt::byte_span buffer_) : buffer(buffer_) , f(f_) { } inline ~WriteBuffer() noexcept(false) { if (!writeError) { FlushLocal(); } } public: inline Tfile & file() const { if (IsDirty()) { FlushLocal(); } return f; } public: inline bool HasWriteError() const { return writeError; } inline void ClearError() { writeError = false; } inline bool IsDirty() const { return size > 0; } inline bool IsClean() const { return size == 0; } inline bool IsFull() const { return size == buffer.size(); } inline std::size_t GetCurrentSize() const { return size; } inline bool Write(mpt::const_byte_span data) { bool result = true; for (std::size_t i = 0; i < data.size(); ++i) { buffer[size] = data[i]; size++; if (IsFull()) { FlushLocal(); } } return result; } inline void FlushLocal() { if (IsClean()) { return; } try { if (!mpt::IO::WriteRaw(f, mpt::as_span(buffer.data(), size))) { writeError = true; } } catch (const std::exception &) { writeError = true; throw; } size = 0; } }; template struct FileOperations> { private: WriteBuffer & f; public: FileOperations(WriteBuffer & f_) : f(f_) { return; } public: inline bool IsValid() { return IsValid(f.file()); } inline bool IsReadSeekable() { return IsReadSeekable(f.file()); } inline bool IsWriteSeekable() { return IsWriteSeekable(f.file()); } inline IO::Offset TellRead() { f.FlushLocal(); return TellRead(f.file()); } inline IO::Offset TellWrite() { return TellWrite(f.file()) + f.GetCurrentSize(); } inline bool SeekBegin() { f.FlushLocal(); return SeekBegin(f.file()); } inline bool SeekEnd() { f.FlushLocal(); return SeekEnd(f.file()); } inline bool SeekAbsolute(IO::Offset pos) { f.FlushLocal(); return SeekAbsolute(f.file(), pos); } inline bool SeekRelative(IO::Offset off) { f.FlushLocal(); return SeekRelative(f.file(), off); } inline mpt::byte_span ReadRawImpl(mpt::byte_span data) { f.FlushLocal(); return ReadRawImpl(f.file(), data); } inline bool WriteRawImpl(mpt::const_byte_span data) { return f.Write(data); } inline bool IsEof() { f.FlushLocal(); return IsEof(f.file()); } inline bool Flush() { f.FlushLocal(); return Flush(f.file()); } }; } // namespace IO } // namespace MPT_INLINE_NS } // namespace mpt #endif // MPT_IO_WRITE_BUFFER_HPP