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)
 |