Files
bitsery/include/bitsery/adapter/stream.h
michael-mueller-git bcd03b4d68 Add Missing header (#82)
* add missing header to develop
* add test to check include files
* fix missing includes
* improve test target names

Co-authored-by: arch <arch@local>
2021-08-24 09:28:15 +03:00

299 lines
12 KiB
C++

//MIT License
//
//Copyright (c) 2017 Mindaugas Vinkelis
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.
#ifndef BITSERY_ADAPTER_STREAM_H
#define BITSERY_ADAPTER_STREAM_H
#include "../details/adapter_common.h"
#include "../traits/array.h"
#include <ios>
#include <limits>
namespace bitsery {
template <typename TChar, typename Config, typename CharTraits>
class BasicInputStreamAdapter: public details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>> {
public:
friend details::InputAdapterBaseCRTP<BasicInputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
BasicInputStreamAdapter(std::basic_ios<TChar, CharTraits>& istream)
:_ios{std::addressof(istream)} {}
BasicInputStreamAdapter(const BasicInputStreamAdapter&) = delete;
BasicInputStreamAdapter& operator = (const BasicInputStreamAdapter&) = delete;
BasicInputStreamAdapter(BasicInputStreamAdapter&&) = default;
BasicInputStreamAdapter& operator = (BasicInputStreamAdapter&&) = default;
void currentReadPos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
}
size_t currentReadPos() const {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
return {};
}
void currentReadEndPos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
}
size_t currentReadEndPos() const {
static_assert(std::is_void<TChar>::value, "setting read position is not supported with StreamAdapter");
return {};
}
ReaderError error() const {
return _err;
}
bool isCompletedSuccessfully() const {
if (error() == ReaderError::NoError) {
return _ios->rdbuf()->sgetc() == CharTraits::eof();
}
return false;
}
void error(ReaderError error) {
if (_err == ReaderError::NoError) {
_err = error;
_zeroIfNoErrors = std::numeric_limits<size_t>::max();
}
}
private:
template <size_t SIZE>
void readInternalValue(TValue* data) {
readChecked(data, SIZE, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readInternalBuffer(TValue* data, size_t size) {
readChecked(data, size, std::integral_constant<bool, Config::CheckAdapterErrors>{});
}
void readChecked(TValue* data, size_t size, std::true_type) {
if (size - static_cast<size_t>(_ios->rdbuf()->sgetn(data, static_cast<std::streamsize>(size))) != _zeroIfNoErrors) {
*data = {};
if (_zeroIfNoErrors == 0) {
error(_ios->rdstate() == std::ios_base::badbit
? ReaderError::ReadingError
: ReaderError::DataOverflow);
}
}
}
void readChecked(TValue* data, size_t size, std::false_type) {
_ios->rdbuf()->sgetn(data , static_cast<std::streamsize>(size));
}
std::basic_ios<TChar, CharTraits>* _ios;
size_t _zeroIfNoErrors{};
ReaderError _err = ReaderError::NoError;
};
template <typename TChar, typename Config, typename CharTraits>
class BasicOutputStreamAdapter: public details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>> {
public:
friend details::OutputAdapterBaseCRTP<BasicOutputStreamAdapter<TChar, Config, CharTraits>>;
using TConfig = Config;
using TValue = TChar;
BasicOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream)
:_ostream{std::addressof(ostream)} {}
void currentWritePos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
}
size_t currentWritePos() const {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
return {};
}
void flush() {
_ostream->flush();
}
size_t writtenBytesCount() const {
static_assert(std::is_void<TChar>::value, "`writtenBytesCount` cannot be used with stream adapter");
//streaming doesn't return written bytes
return 0u;
}
private:
template <size_t SIZE>
void writeInternalValue(const TValue* data) {
_ostream->rdbuf()->sputn( data , SIZE );
}
void writeInternalBuffer(const TValue* data, size_t size) {
_ostream->rdbuf()->sputn( data , size );
}
std::basic_ostream<TChar, CharTraits>* _ostream;
};
template <typename TChar, typename Config, typename CharTraits, typename TBuffer = std::array<TChar, 256>>
class BasicBufferedOutputStreamAdapter:
public details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>> {
public:
friend details::OutputAdapterBaseCRTP<BasicBufferedOutputStreamAdapter<TChar, Config, CharTraits, TBuffer>>;
using TConfig = Config;
using Buffer = TBuffer;
using BufferIt = typename traits::BufferAdapterTraits<TBuffer>::TIterator;
static_assert(details::IsDefined<BufferIt>::value, "Please define BufferAdapterTraits or include from <bitsery/traits/...> to use as buffer for BasicBufferedOutputStreamAdapter");
static_assert(traits::ContainerTraits<Buffer>::isContiguous, "BasicBufferedOutputStreamAdapter only works with contiguous containers");
using TValue = TChar;
//bufferSize is used when buffer is dynamically allocated
BasicBufferedOutputStreamAdapter(std::basic_ostream<TChar, CharTraits>& ostream, size_t bufferSize = 256)
:_ostream(std::addressof(ostream)),
_buf{},
_beginIt{std::begin(_buf)},
_currOffset{0}
{
init(bufferSize, TResizable{});
// buffer size must be atleast 16, because writeIntervalValue expect that at least one value fits to buffer.
assert(_bufferSize >= 16);
}
//we need to explicitly declare move logic, because after move buffer might be invalidated
BasicBufferedOutputStreamAdapter(const BasicBufferedOutputStreamAdapter&) = delete;
BasicBufferedOutputStreamAdapter& operator = (const BasicBufferedOutputStreamAdapter&) = delete;
BasicBufferedOutputStreamAdapter(BasicBufferedOutputStreamAdapter&& rhs)
: _ostream{rhs._ostream},
_buf{std::move(rhs._buf)},
_beginIt{std::begin(_buf)},
_currOffset{rhs._currOffset},
_bufferSize{rhs._bufferSize}
{
};
BasicBufferedOutputStreamAdapter& operator = (BasicBufferedOutputStreamAdapter&& rhs) {
_ostream = rhs._ostream;
_buf = std::move(rhs._buf);
_beginIt = std::begin(_buf);
_currOffset = rhs._currOffset;
_bufferSize = rhs._bufferSize;
return *this;
};
void currentWritePos(size_t ) {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
}
size_t currentWritePos() const {
static_assert(std::is_void<TChar>::value, "setting write position is not supported with StreamAdapter");
return {};
}
void flush() {
writeBufferToStream();
_ostream->flush();
}
size_t writtenBytesCount() const {
static_assert(std::is_void<TChar>::value, "`writtenBytesCount` cannot be used with stream adapter");
//streaming doesn't return written bytes
return 0u;
}
private:
using TResizable = std::integral_constant<bool, traits::ContainerTraits<TBuffer>::isResizable>;
using diff_t = typename std::iterator_traits<BufferIt>::difference_type;
template <size_t SIZE>
void writeInternalValue(const TValue* data) {
auto newOffset = _currOffset + SIZE;
if (newOffset > _bufferSize) {
writeBufferToStream();
newOffset = SIZE;
}
std::copy_n(data, SIZE, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
}
void writeInternalBuffer(const TValue* data, size_t size) {
const auto newOffset = _currOffset + size;
if (newOffset <= _bufferSize) {
std::copy_n(data, size, _beginIt + static_cast<diff_t>(_currOffset));
_currOffset = newOffset;
} else {
writeBufferToStream();
// write buffer directly to stream
_ostream->rdbuf()->sputn(data, size);
}
}
void writeBufferToStream() {
_ostream->rdbuf()->sputn(std::addressof(*_beginIt), static_cast<std::streamsize>(_currOffset));
_currOffset = 0;
}
void init (size_t buffSize, std::true_type) {
// resize buffer
_bufferSize = buffSize;
_buf.resize(_bufferSize);
_beginIt = std::begin(_buf);
}
void init (size_t , std::false_type) {
// ignore buffer size parameter, and instead take actual buffer size
_bufferSize = traits::ContainerTraits<Buffer>::size(_buf);
}
std::basic_ostream<TChar, CharTraits>* _ostream;
TBuffer _buf;
BufferIt _beginIt;
size_t _currOffset;
size_t _bufferSize{0};
};
template <typename TChar, typename Config, typename CharTraits>
class BasicIOStreamAdapter
: public BasicInputStreamAdapter<TChar, Config, CharTraits>
, public BasicOutputStreamAdapter<TChar, Config, CharTraits>
{
public:
using TValue = TChar;
//both bases contain reference to same iostream, so no need to do anything
BasicIOStreamAdapter(std::basic_iostream<TChar, CharTraits>& iostream)
:BasicInputStreamAdapter<TChar, Config, CharTraits>{iostream}
,BasicOutputStreamAdapter<TChar, Config, CharTraits>{iostream}
{}
};
//helper types for most common implementations for std streams
using OutputStreamAdapter = BasicOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using InputStreamAdapter = BasicInputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using IOStreamAdapter = BasicIOStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
using OutputBufferedStreamAdapter = BasicBufferedOutputStreamAdapter<char, DefaultConfig, std::char_traits<char>>;
}
#endif //BITSERY_ADAPTER_STREAM_H