#pragma once #if !defined(MIJIN_IO_STLSTREAM_HPP_INCLUDED) #define MIJIN_IO_STLSTREAM_HPP_INCLUDED 1 #include #include #include "./stream.hpp" namespace mijin { // // public defines // // // public constants // // // public types // template> class BasicStreambufAdapter : public std::basic_streambuf { private: using base_t = std::basic_streambuf; using char_type = base_t::char_type; using int_type = base_t::int_type; using traits_type = base_t::traits_type; static const std::size_t BUFFER_SIZE = 4096; Stream* stream_ = nullptr; std::array inBuffer_; std::array outBuffer_; public: explicit BasicStreambufAdapter(Stream& stream) noexcept : stream_(&stream) { base_t::setp(outBuffer_.data(), outBuffer_.data() + outBuffer_.size()); } int_type underflow() override { std::size_t bytesRead = 0; while (bytesRead == 0) { if (stream_->isAtEnd()) { return traits_type::eof(); } throwOnError(stream_->readRaw(inBuffer_, {.partial = true}, &bytesRead)); } base_t::setg(inBuffer_.data(), inBuffer_.data(), inBuffer_.data() + bytesRead); return inBuffer_[0]; } int_type overflow(int_type c) override { if (base_t::pptr() - base_t::pbase() > 0) { throwOnError(stream_->writeRaw(base_t::pbase(), base_t::pptr() - base_t::pbase())); } outBuffer_[0] = traits_type::to_char_type(c); base_t::setp(outBuffer_.data() + 1, outBuffer_.data() + outBuffer_.size()); return traits_type::not_eof(c); } int sync() override { stream_->flush(); return 0; } }; using StreambufAdapter = BasicStreambufAdapter; template> class BasicIOStreamAdapter : public std::basic_iostream { private: StreambufAdapter streambuf_; public: BasicIOStreamAdapter(Stream& stream) noexcept : std::basic_iostream(&streambuf_), streambuf_(stream) {} }; using IOStreamAdapter = BasicIOStreamAdapter; template> class BasicSTLStream : public Stream { private: std::basic_istream* istream_ = nullptr; std::basic_ostream* ostream_ = nullptr; public: explicit BasicSTLStream(std::basic_istream& istream) : istream_(&istream) {} explicit BasicSTLStream(std::basic_ostream& ostream) : ostream_(&ostream) {} explicit BasicSTLStream(std::basic_iostream& iostream) : istream_(&iostream), ostream_(&ostream_) {} StreamError readRaw(std::span buffer, const ReadOptions& options = {}, std::size_t* outBytesRead = nullptr) override { (void) options; if (istream_ != nullptr) { if (buffer.size() % sizeof(TChar) != 0) { // buffer size must be divisible by character size return StreamError::IO_ERROR; } istream_->read(std::bit_cast(buffer.data()), buffer.size() / sizeof(TChar)); if (!istream_->good()) { return StreamError::IO_ERROR; } if (outBytesRead != nullptr) { *outBytesRead = buffer.size(); } return StreamError::SUCCESS; } return StreamError::NOT_SUPPORTED; } StreamError writeRaw(std::span buffer) override { if (ostream_ != nullptr) { if (buffer.size() % sizeof(TChar) != 0) { // buffer size must be divisible by character size return StreamError::IO_ERROR; } ostream_->write(std::bit_cast(buffer.data()), buffer.size() / sizeof(TChar)); return StreamError::SUCCESS; } return StreamError::NOT_SUPPORTED; } std::size_t tell() override { // kind of weird case, since the streams can have different positions // this is fine, I guess if (istream_ != nullptr) { return istream_->tellg(); } if (ostream_ != nullptr) { return ostream_->tellp(); } return 0; } StreamError seek(std::intptr_t pos, mijin::SeekMode seekMode = SeekMode::ABSOLUTE) override { std::ios_base::seekdir seekDir = std::ios_base::beg; switch (seekMode) { case mijin::SeekMode::ABSOLUTE: break; case mijin::SeekMode::RELATIVE: seekDir = std::ios_base::cur; break; case mijin::SeekMode::RELATIVE_TO_END: seekDir = std::ios_base::end; break; } if (istream_ != nullptr) { istream_->seekg(pos, seekDir); if (!istream_->good()) { return StreamError::IO_ERROR; } } if (ostream_ != nullptr) { ostream_->seekp(pos, seekDir); if (!ostream_->good()) { return StreamError::IO_ERROR; } } return StreamError::SUCCESS; } bool isAtEnd() override { if (istream_ != nullptr) { return istream_->peek() == TTraits::eof(); } return false; } StreamFeatures getFeatures() override { return { .read = istream_ != nullptr, .write = ostream_ != nullptr, .tell = istream_ != nullptr || ostream_ != nullptr, .seek = istream_ != nullptr || ostream_ != nullptr, .async = false, .readOptions = { .partial = false, .peek = false, .noBlock = false } }; } }; using STLStream = BasicSTLStream; } // namespace mijin #endif // !defined(MIJIN_IO_STLSTREAM_HPP_INCLUDED)