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