222 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_IO_STLSTREAM_HPP_INCLUDED)
 | |
| #define MIJIN_IO_STLSTREAM_HPP_INCLUDED 1
 | |
| 
 | |
| #include <array>
 | |
| #include <streambuf>
 | |
| #include "./stream.hpp"
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| 
 | |
| //
 | |
| // public defines
 | |
| //
 | |
| 
 | |
| //
 | |
| // public constants
 | |
| //
 | |
| 
 | |
| //
 | |
| // public types
 | |
| //
 | |
| 
 | |
| template<typename TChar, typename TTraits = std::char_traits<TChar>>
 | |
| class BasicStreambufAdapter : public std::basic_streambuf<TChar, TTraits>
 | |
| {
 | |
| private:
 | |
|     using base_t = std::basic_streambuf<TChar, TTraits>;
 | |
|     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<TChar, BUFFER_SIZE> inBuffer_;
 | |
|     std::array<TChar, BUFFER_SIZE> 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<char>;
 | |
| 
 | |
| template<typename TChar, typename TTraits = std::char_traits<TChar>>
 | |
| class BasicIOStreamAdapter : public std::basic_iostream<TChar, TTraits>
 | |
| {
 | |
| private:
 | |
|     StreambufAdapter streambuf_;
 | |
| public:
 | |
|     BasicIOStreamAdapter(Stream& stream) noexcept : std::basic_iostream<TChar, TTraits>(&streambuf_), streambuf_(stream) {}
 | |
| };
 | |
| 
 | |
| using IOStreamAdapter = BasicIOStreamAdapter<char>;
 | |
| 
 | |
| template<typename TChar, typename TTraits = std::char_traits<TChar>>
 | |
| class BasicSTLStream : public Stream
 | |
| {
 | |
| private:
 | |
|     std::basic_istream<TChar, TTraits>* istream_ = nullptr;
 | |
|     std::basic_ostream<TChar, TTraits>* ostream_ = nullptr;
 | |
| public:
 | |
|     explicit BasicSTLStream(std::basic_istream<TChar, TTraits>& istream) : istream_(&istream) {}
 | |
|     explicit BasicSTLStream(std::basic_ostream<TChar, TTraits>& ostream) : ostream_(&ostream) {}
 | |
|     explicit BasicSTLStream(std::basic_iostream<TChar, TTraits>& iostream) : istream_(&iostream), ostream_(&ostream_) {}
 | |
|     StreamError readRaw(std::span<std::uint8_t> 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<TChar*>(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<const std::uint8_t> 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<const TChar*>(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<char>;
 | |
| } // namespace mijin
 | |
| 
 | |
| #endif // !defined(MIJIN_IO_STLSTREAM_HPP_INCLUDED)
 |