Files
pip/libs/main/serialization/pibinarystream.h
2026-03-12 14:46:57 +03:00

794 lines
31 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*! \file pibinarystream.h
* \ingroup Serialization
* \~\brief
* \~english Binary serialization interface
* \~russian Интерфейс бинарной сериализации
*/
/*
PIP - Platform Independent Primitives
Binary serialization interface
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PIBINARYSTREAM_H
#define PIBINARYSTREAM_H
#include "pibitarray.h"
#include "pimap.h"
#include "pimemoryblock.h"
#include "piset.h"
#include "pivector2d.h"
#define PIP_BINARY_STREAM
#ifdef DOXYGEN
//! \relatesalso PIBinaryStream
//! \~\brief
//! \~english Declares templated binary stream operators as friends of \c T.
//! \~russian Объявляет шаблонные операторы бинарного потока дружественными для \c T.
# define BINARY_STREAM_FRIEND(T)
//! \relatesalso PIBinaryStream
//! \~\brief
//! \~english Starts definition of a templated binary write operator for \c T.
//! \~russian Начинает определение шаблонного оператора записи в бинарный поток для \c T.
# define BINARY_STREAM_WRITE(T)
//! \relatesalso PIBinaryStream
//! \~\brief
//! \~english Starts definition of a templated binary read operator for \c T.
//! \~russian Начинает определение шаблонного оператора чтения из бинарного потока для \c T.
# define BINARY_STREAM_READ(T)
#else
# define BINARY_STREAM_FRIEND(T) \
template<typename P> \
friend PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const T & v); \
template<typename P> \
friend PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, T & v);
# define BINARY_STREAM_WRITE(T) \
template<typename P> \
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const T & v)
# define BINARY_STREAM_READ(T) \
template<typename P> \
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, T & v)
#endif
//! \~\ingroup Serialization
//! \~\brief
//! \~english CRTP interface for binary serialization streams.
//! \~russian CRTP-интерфейс для потоков бинарной сериализации.
//! \~\details
//! \~english
//! Derived stream classes should implement:
//! \~\code
//! bool binaryStreamAppendImp (const void * d, size_t s);
//! bool binaryStreamTakeImp (void * d, size_t s);
//! ssize_t binaryStreamSizeImp () const;
//! \endcode
//! \a binaryStreamSizeImp() should return \c -1 when the remaining size is unknown. See details \ref iostream.
//! \~russian
//! Производные классы потока должны реализовать методы из примера выше. \a binaryStreamSizeImp() должен возвращать \c -1, когда оставшийся
//! размер неизвестен. Подробнее \ref iostream.
template<typename P>
class PIBinaryStream {
public:
//! \~english Writes raw bytes to the underlying stream.
//! \~russian Записывает сырые байты в нижележащий поток.
bool binaryStreamAppend(const void * d, size_t s) {
if (!static_cast<P *>(this)->binaryStreamAppendImp(d, s)) {
return false;
fprintf(stderr, "[PIBinaryStream] binaryStreamAppend() error\n");
}
return true;
}
//! \~english Reads raw bytes from the underlying stream and sets read error state on short reads.
//! \~russian Читает сырые байты из нижележащего потока и устанавливает состояние ошибки чтения при неполном чтении.
bool binaryStreamTake(void * d, size_t s) {
if (!static_cast<P *>(this)->binaryStreamTakeImp(d, s)) {
_was_read_error_ = true;
return false;
fprintf(stderr, "[PIBinaryStream] binaryStreamTake() error\n");
}
return true;
}
//! \~english Returns remaining readable byte count.
//! \~russian Возвращает число байтов, доступных для чтения.
//! \~\details
//! \~english
//! Returns \c -1 when the stream cannot report its remaining size.
//! \~russian
//! Возвращает \c -1, если поток не может сообщить оставшийся размер.
ssize_t binaryStreamSize() const { return static_cast<const P *>(this)->binaryStreamSizeImp(); }
//! \~english Writes one trivially copied value as raw bytes.
//! \~russian Записывает одно значение прямым копированием его байтов.
template<typename T>
void binaryStreamAppend(T v) {
binaryStreamAppend(&v, sizeof(v));
}
//! \~english Reads one \c int value from the stream.
//! \~russian Читает одно значение \c int из потока.
int binaryStreamTakeInt() {
int r = 0;
binaryStreamTake(&r, sizeof(r));
return r;
}
//! \~english Returns whether there has been an incomplete read since last \a resetReadError() or after the stream was created
//! \~russian Возвращает было ли неполное чтение с момента последнего вызова \a resetReadError() или создания потока
bool wasReadError() const { return _was_read_error_; }
//! \~english Reset incomplete read flag
//! \~russian Сбрасывает флаг неполного чтения
void resetReadError() { _was_read_error_ = false; }
private:
bool _was_read_error_ = false;
};
//! \~\ingroup Serialization
//! \~\brief
//! \~english Helper wrapper used to detect default trivial streaming operators.
//! \~russian Вспомогательная обертка для определения операторов потоковой обработки тривиальных типов по умолчанию.
//! \~english Helper class to detect default operators
//! \~russian Вспомогательный класс для обнаружения операторов по умолчанию
template<typename P>
class PIBinaryStreamTrivialRef {
public:
//! \~english Constructs helper reference for stream \a s.
//! \~russian Создает вспомогательную ссылку на поток \a s.
PIBinaryStreamTrivialRef(PIBinaryStream<P> & s): p(s) {}
//! \~english Referenced stream.
//! \~russian Связанный поток.
PIBinaryStream<P> & p;
};
template<typename P, typename T>
//! \~english Forwards write operation through %PIBinaryStreamTrivialRef.
//! \~russian Перенаправляет операцию записи через %PIBinaryStreamTrivialRef.
inline PIBinaryStream<P> & operator<<(PIBinaryStreamTrivialRef<P> s, const T & v) {
s.p << v;
return s.p;
}
template<typename P, typename T>
//! \~english Forwards read operation through %PIBinaryStreamTrivialRef.
//! \~russian Перенаправляет операцию чтения через %PIBinaryStreamTrivialRef.
inline PIBinaryStream<P> & operator>>(PIBinaryStreamTrivialRef<P> s, T & v) {
s.p >> v;
return s.p;
}
template<typename P>
//! \~english Forwards raw memory block write through %PIBinaryStreamTrivialRef.
//! \~russian Перенаправляет запись блока памяти через %PIBinaryStreamTrivialRef.
inline PIBinaryStream<P> & operator<<(PIBinaryStreamTrivialRef<P> s, const PIMemoryBlock v) {
s.p << v;
return s.p;
}
template<typename P>
//! \~english Forwards raw memory block read through %PIBinaryStreamTrivialRef.
//! \~russian Перенаправляет чтение блока памяти через %PIBinaryStreamTrivialRef.
inline PIBinaryStream<P> & operator>>(PIBinaryStreamTrivialRef<P> s, PIMemoryBlock v) {
s.p >> v;
return s.p;
}
// specify types
template<typename P>
//! \~english Stores \c bool as one unsigned byte.
//! \~russian Сохраняет \c bool как один беззнаковый байт.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const bool v) {
s.binaryStreamAppend((uchar)v);
return s;
}
template<typename P>
//! \~english Restores \c bool from one unsigned byte.
//! \~russian Восстанавливает \c bool из одного беззнакового байта.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, bool & v) {
uchar c = 0;
s.binaryStreamTake(&c, sizeof(c));
v = c;
return s;
}
template<typename P>
//! \~english Writes raw bytes from %PIMemoryBlock.
//! \~russian Записывает сырые байты из %PIMemoryBlock.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIMemoryBlock v) {
s.binaryStreamAppend(v.data(), v.size());
return s;
}
template<typename P>
//! \~english Reads raw bytes into %PIMemoryBlock.
//! \~russian Читает сырые байты в %PIMemoryBlock.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIMemoryBlock v) {
s.binaryStreamTake(v.data(), v.size());
return s;
}
// store simple types
template<typename P, typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
//! \~english Stores enum values as \c int.
//! \~russian Сохраняет значения перечислений как \c int.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const T & v) {
// piCout << "<< enum";
s.binaryStreamAppend((int)v);
return s;
}
template<typename P,
typename T,
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Stores trivially copyable values by raw memory copy and returns a helper marker for bulk container paths.
//! \~russian Сохраняет тривиально копируемые значения прямым копированием памяти и возвращает вспомогательный маркер для контейнерных путей
//! с пакетной записью.
inline PIBinaryStreamTrivialRef<P> operator<<(PIBinaryStream<P> & s, const T & v) {
s.binaryStreamAppend(&v, sizeof(v));
return s;
}
//! \~english Stores %PIVector of trivial elements in bulk.
//! \~russian Сохраняет %PIVector из тривиальных элементов пакетно.
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
std::is_same<decltype(std::declval<PIBinaryStream<P> &>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIVector<T> & v) {
// piCout << "<< vector trivial default";
s.binaryStreamAppend((int)v.size());
s.binaryStreamAppend(v.data(), v.size() * sizeof(T));
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
!std::is_same<decltype(std::declval<PIBinaryStream<P> &>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Stores %PIVector element-by-element when the element type has custom binary streaming.
//! \~russian Сохраняет %PIVector поэлементно, когда тип элемента использует собственные операторы бинарного потока.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIVector<T> & v) {
// piCout << "<< vector trivial custom";
s.binaryStreamAppend((int)v.size());
for (size_t i = 0; i < v.size(); ++i)
s << v[i];
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
std::is_same<decltype(std::declval<PIBinaryStream<P> &>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Stores %PIDeque of trivial elements in bulk.
//! \~russian Сохраняет %PIDeque из тривиальных элементов пакетно.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIDeque<T> & v) {
// piCout << "<< deque trivial default";
s.binaryStreamAppend((int)v.size());
s.binaryStreamAppend(v.data(), v.size() * sizeof(T));
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
!std::is_same<decltype(std::declval<PIBinaryStream<P> &>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Stores %PIDeque element-by-element when the element type has custom binary streaming.
//! \~russian Сохраняет %PIDeque поэлементно, когда тип элемента использует собственные операторы бинарного потока.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIDeque<T> & v) {
// piCout << "<< deque trivial custom";
s.binaryStreamAppend((int)v.size());
for (size_t i = 0; i < v.size(); ++i)
s << v[i];
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
std::is_same<decltype(std::declval<PIBinaryStream<P> &>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Stores %PIVector2D of trivial elements in bulk.
//! \~russian Сохраняет %PIVector2D из тривиальных элементов пакетно.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIVector2D<T> & v) {
// piCout << "<< vector2d trivial default";
s.binaryStreamAppend((int)v.rows());
s.binaryStreamAppend((int)v.cols());
s.binaryStreamAppend(v.data(), v.size() * sizeof(T));
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
!std::is_same<decltype(std::declval<PIBinaryStream<P> &>() << std::declval<const T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Stores %PIVector2D row data through the element streaming path.
//! \~russian Сохраняет данные %PIVector2D через путь потоковой обработки элементов.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIVector2D<T> & v) {
// piCout << "<< vector2d trivial custom";
s.binaryStreamAppend((int)v.rows());
s.binaryStreamAppend((int)v.cols());
s << v.toPlainVector();
return s;
}
template<typename P>
//! \~english Stores %PIBitArray content.
//! \~russian Сохраняет содержимое %PIBitArray.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIBitArray & v) {
s << v.size_ << v.data_;
return s;
}
template<typename P, typename Type0, typename Type1>
//! \~english Stores both members of %PIPair.
//! \~russian Сохраняет оба элемента %PIPair.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIPair<Type0, Type1> & v) {
s << v.first << v.second;
return s;
}
// restore simple types
template<typename P, typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
//! \~english Restores enum values from \c int.
//! \~russian Восстанавливает значения перечислений из \c int.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, T & v) {
// piCout << ">> enum";
v = (T)s.binaryStreamTakeInt();
return s;
}
template<typename P,
typename T,
typename std::enable_if<!std::is_enum<T>::value, int>::type = 0,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Restores trivially copyable values by raw memory copy and returns a helper marker for bulk container paths.
//! \~russian Восстанавливает тривиально копируемые значения прямым копированием памяти и возвращает вспомогательный маркер для контейнерных
//! путей с пакетным чтением.
inline PIBinaryStreamTrivialRef<P> operator>>(PIBinaryStream<P> & s, T & v) {
if (!s.binaryStreamTake(&v, sizeof(v))) {
fprintf(stderr, "error with %s\n", __PIP_TYPENAME__(T));
}
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
std::is_same<decltype(std::declval<PIBinaryStream<P> &>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Restores %PIVector of trivial elements in bulk.
//! \~russian Восстанавливает %PIVector из тривиальных элементов пакетно.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & v) {
// piCout << ">> vector trivial default";
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v._resizeRaw(sz);
if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) {
fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T));
v.clear();
}
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
!std::is_same<decltype(std::declval<PIBinaryStream<P> &>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Restores %PIVector element-by-element when the element type has custom binary streaming.
//! \~russian Восстанавливает %PIVector поэлементно, когда тип элемента использует собственные операторы бинарного потока.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & v) {
// piCout << ">> vector trivial custom";
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v._resizeRaw(sz);
for (int i = 0; i < sz; ++i) {
s >> v[i];
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
}
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
std::is_same<decltype(std::declval<PIBinaryStream<P> &>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Restores %PIDeque of trivial elements in bulk.
//! \~russian Восстанавливает %PIDeque из тривиальных элементов пакетно.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & v) {
// piCout << ">> deque trivial default";
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v._resizeRaw(sz);
if (!s.binaryStreamTake(v.data(), sz * sizeof(T))) {
fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
v.clear();
}
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
!std::is_same<decltype(std::declval<PIBinaryStream<P> &>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Restores %PIDeque element-by-element when the element type has custom binary streaming.
//! \~russian Восстанавливает %PIDeque поэлементно, когда тип элемента использует собственные операторы бинарного потока.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & v) {
// piCout << ">> deque trivial custom";
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v._resizeRaw(sz);
for (int i = 0; i < sz; ++i) {
s >> v[i];
if (s.wasReadError()) {
fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
}
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
std::is_same<decltype(std::declval<PIBinaryStream<P> &>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Restores %PIVector2D of trivial elements in bulk.
//! \~russian Восстанавливает %PIVector2D из тривиальных элементов пакетно.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIVector2D<T> & v) {
// piCout << ">> vector2d trivial default";
int r, c;
r = s.binaryStreamTakeInt();
c = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v._resizeRaw(r, c);
if (!s.binaryStreamTake(v.data(), v.size() * sizeof(T))) {
fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T));
v.clear();
}
return s;
}
template<typename P,
typename T,
typename std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0,
typename std::enable_if<
!std::is_same<decltype(std::declval<PIBinaryStream<P> &>() >> std::declval<T &>()), PIBinaryStreamTrivialRef<P>>::value,
int>::type = 0>
//! \~english Restores %PIVector2D through the element streaming path.
//! \~russian Восстанавливает %PIVector2D через путь потоковой обработки элементов.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIVector2D<T> & v) {
// piCout << ">> vector2d trivial custom";
int r, c;
PIVector<T> tmp;
r = s.binaryStreamTakeInt();
c = s.binaryStreamTakeInt();
s >> tmp;
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v = PIVector2D<T>(r, c, tmp);
return s;
}
template<typename P>
//! \~english Restores %PIBitArray content.
//! \~russian Восстанавливает содержимое %PIBitArray.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIBitArray & v) {
s >> v.size_ >> v.data_;
return s;
}
template<typename P, typename Type0, typename Type1>
//! \~english Restores both members of %PIPair.
//! \~russian Восстанавливает оба элемента %PIPair.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIPair<Type0, Type1> & v) {
s >> v.first >> v.second;
return s;
}
// store complex types
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Stores %PIVector of non-trivial elements one-by-one.
//! \~russian Сохраняет %PIVector из нетривиальных элементов по одному.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIVector<T> & v) {
// piCout << "<< vector complex";
s.binaryStreamAppend(int(v.size_s()));
for (size_t i = 0; i < v.size(); ++i)
s << v[i];
return s;
}
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Stores %PIDeque of non-trivial elements one-by-one.
//! \~russian Сохраняет %PIDeque из нетривиальных элементов по одному.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIDeque<T> & v) {
// piCout << "<< deque complex";
s.binaryStreamAppend(int(v.size_s()));
for (size_t i = 0; i < v.size(); ++i)
s << v[i];
return s;
}
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Stores %PIVector2D of non-trivial elements via its plain vector representation.
//! \~russian Сохраняет %PIVector2D из нетривиальных элементов через его плоское векторное представление.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIVector2D<T> & v) {
// piCout << "<< vector2d complex";
s.binaryStreamAppend(int(v.rows()));
s.binaryStreamAppend(int(v.cols()));
s << v.toPlainVector();
return s;
}
// restore complex types
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Restores %PIVector of non-trivial elements one-by-one.
//! \~russian Восстанавливает %PIVector из нетривиальных элементов по одному.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIVector<T> & v) {
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v.resize(sz);
for (size_t i = 0; i < v.size(); ++i) {
s >> v[i];
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
}
return s;
}
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Restores %PIDeque of non-trivial elements one-by-one.
//! \~russian Восстанавливает %PIDeque из нетривиальных элементов по одному.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIDeque<T> & v) {
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v.resize(sz);
for (size_t i = 0; i < v.size(); ++i) {
s >> v[i];
if (s.wasReadError()) {
fprintf(stderr, "error with PIDeque<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
}
return s;
}
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Restores %PIVector2D of non-trivial elements via an intermediate plain vector.
//! \~russian Восстанавливает %PIVector2D из нетривиальных элементов через промежуточный плоский вектор.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIVector2D<T> & v) {
int r, c;
PIVector<T> tmp;
r = s.binaryStreamTakeInt();
c = s.binaryStreamTakeInt();
s >> tmp;
if (s.wasReadError()) {
fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T));
v.clear();
return s;
}
v = PIVector2D<T>(r, c, tmp);
return s;
}
// other types
template<typename P, typename Key, typename T>
//! \~english Stores %PIMap keys and values.
//! \~russian Сохраняет ключи и значения %PIMap.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PIMap<Key, T> & v) {
s.binaryStreamAppend((int)v.pim_index.size_s());
for (uint i = 0; i < v.size(); ++i) {
s.binaryStreamAppend((int)v.pim_index[i].index);
s << v.pim_index[i].key;
}
s << v.pim_content;
return s;
}
template<typename P, typename Key, typename T>
//! \~english Restores %PIMap keys and values.
//! \~russian Восстанавливает ключи и значения %PIMap.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PIMap<Key, T> & v) {
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T));
v.clear();
return s;
}
v.pim_index.resize(sz);
int ind = 0;
for (int i = 0; i < sz; ++i) {
ind = s.binaryStreamTakeInt();
s >> v.pim_index[i].key;
if (s.wasReadError()) {
fprintf(stderr, "error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T));
v.clear();
return s;
}
v.pim_index[i].index = ind;
}
s >> v.pim_content;
if (s.wasReadError()) {
fprintf(stderr, "error with PIMap<%s, %s>\n", __PIP_TYPENAME__(Key), __PIP_TYPENAME__(T));
v.clear();
return s;
}
if (v.pim_content.size_s() != v.pim_index.size_s()) {
piCout << "Warning: loaded invalid PIMap, clear";
v.clear();
}
return s;
}
template<typename P, typename Key>
//! \~english Stores %PISet keys.
//! \~russian Сохраняет ключи %PISet.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const PISet<Key> & v) {
s.binaryStreamAppend((int)v.pim_index.size_s());
for (uint i = 0; i < v.size(); ++i) {
s.binaryStreamAppend((int)v.pim_index[i].index);
s << v.pim_index[i].key;
}
return s;
}
template<typename P, typename Key>
//! \~english Restores %PISet keys.
//! \~russian Восстанавливает ключи %PISet.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, PISet<Key> & v) {
int sz = s.binaryStreamTakeInt();
if (s.wasReadError()) {
fprintf(stderr, "error with PISet<%s>\n", __PIP_TYPENAME__(Key));
v.clear();
return s;
}
v.pim_index.resize(sz);
v.pim_content.resize(sz, 0);
int ind = 0;
for (int i = 0; i < sz; ++i) {
ind = s.binaryStreamTakeInt();
s >> v.pim_index[i].key;
if (s.wasReadError()) {
fprintf(stderr, "error with PISet<%s>\n", __PIP_TYPENAME__(Key));
v.clear();
return s;
}
v.pim_index[i].index = ind;
}
return s;
}
// non-defined complex types
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Fallback overload that intentionally fails for unsupported non-trivial write types.
//! \~russian Резервная перегрузка, которая намеренно завершает компиляцию ошибкой для неподдерживаемых нетривиальных типов записи.
inline PIBinaryStream<P> & operator<<(PIBinaryStream<P> & s, const T &) {
static_assert(std::is_trivially_copyable<T>::value, "[PIBinaryStream] Error: using undeclared operator << for complex type!");
return s;
}
template<typename P, typename T, typename std::enable_if<!std::is_trivially_copyable<T>::value, int>::type = 0>
//! \~english Fallback overload that intentionally fails for unsupported non-trivial read types.
//! \~russian Резервная перегрузка, которая намеренно завершает компиляцию ошибкой для неподдерживаемых нетривиальных типов чтения.
inline PIBinaryStream<P> & operator>>(PIBinaryStream<P> & s, T &) {
static_assert(std::is_trivially_copyable<T>::value, "[PIBinaryStream] Error: using undeclared operator >> for complex type!");
return s;
}
#endif