/*! \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
//! \~english
//! \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() || sz < 0) {
		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() || sz < 0) {
		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() || sz < 0) {
		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() || sz < 0) {
		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() || r < 0 || c < 0) {
		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();
	if (s.wasReadError() || r < 0 || c < 0) {
		fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T));
		v.clear();
		return s;
	}
	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() || sz < 0) {
		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() || sz < 0) {
		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();
	if (s.wasReadError() || r < 0 || c < 0) {
		fprintf(stderr, "error with PIVector2D<%s>\n", __PIP_TYPENAME__(T));
		v.clear();
		return s;
	}
	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() || sz < 0) {
		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() || sz < 0) {
		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
