mirror of
https://github.com/fraillt/bitsery.git
synced 2026-06-08 00:03:54 +00:00
* add missing header to develop * add test to check include files * fix missing includes * improve test target names Co-authored-by: arch <arch@local>
299 lines
12 KiB
C++
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
|