\~english \page iostream Input/Output stream
\~russian \page iostream Поток ввода/вывода

\~english

\a PIBinaryStream is the binary serialization interface. For versioned, extensible formats with chunk ids see \ref chunk_stream. It is not used standalone; only as a mixin or via concrete classes such as \a PIByteArray and \a PIIOBinaryStream. Use it to save or load any data. Trivial types are read/written as memory blocks unless custom operators are defined; non-trivial types must have stream operators or the code will not compile. Containers are supported under the same rules. Enums are treated as int, bool as one byte. Write operators append to the stream; read operators consume from the beginning. Macros: \c BINARY_STREAM_FRIEND(T), \c BINARY_STREAM_WRITE(T), \c BINARY_STREAM_READ(T) (inside them \c s is the stream, \c v is the value).

\~russian
%PIBinaryStream представляет собой интерфейс бинарной сериализации. Для версионных расширяемых форматов с id чанков см. \ref chunk_stream.
Не может быть использован в чистом виде, только в виде миксина или
готовых классов: PIByteArray и PIIOBinaryStream.


Используется для сохранения или чтения любых данных. Простые типы читаются/пишутся
как блоки памяти, если не созданы конкретные операторы. Сложные типы
([нетривиальные](https://ru.cppreference.com/w/cpp/types/is_trivially_copyable))
обязаны иметь операторы ввода/вывода, иначе возникнет ошибка компиляции.

Также поддерживаются контейнеры с типами по таким же правилам.

Перечисления интерпретируются как int, логические типы как один байт.

Операторы сохранения добавляют данные в конец потока, а операторы извлечения
берут данные из его начала.

Для облегчения написания операторов есть макросы:
 * BINARY_STREAM_FRIEND(T) - объявить операторы с доступом к приватному
 содержимому типа T, необязателен;
 * BINARY_STREAM_WRITE(T) - запись в поток, "s" - объект потока, "v" - объект типа T;
 * BINARY_STREAM_READ(T) - чтение из потока, "s" - объект потока, "v" - объект типа T.
 
Пример:
\code{.cpp}
#include <pibytearray.h>

class MyType {
	BINARY_STREAM_FRIEND(MyType);
public:

	void setInt(int v) {m_i = v;}
	int getInt() const {return m_i;}

	void setString(PIString v) {m_s = v;}
	PIString getString() const {return m_s;}

private:
	int m_i = 0;
	PIString m_s;

};

BINARY_STREAM_WRITE(MyType) {s << v.m_i << v.m_s; return s;}
BINARY_STREAM_READ (MyType) {s >> v.m_i >> v.m_s; return s;}

int main(int argc, char * argv[]) {
	MyType t_read, t_write;
	t_write.setInt(10);
	t_write.setString("text");

	PIByteArray data;
	data << t_write;

	piCout << data.toHex();

	data >> t_read;
	piCout << t_read.getInt() << t_read.getString();

	piCout << data.toHex();
}
\endcode

\~english Result:
\~russian Результат:
\code{.cpp}
0a000000040000007400650078007400
10 text

\endcode


\~english
For store/restore custom data blocks this is PIMemoryBlock class. Stream
operators of this class simply store/restore data block to/from stream:

\~russian
Для сохранения/извлечения блоков произвольных данных используется класс PIMemoryBlock.
Потоковые операторы для него просто сохраняют/извлекают блоки байтов в/из потока:

\code{.cpp}
float a_read[10], a_write[10];
for (int i = 0; i < 10; ++i) {
	a_read [i] = 0.f;
	a_write[i] = i / 10.f;
}

PIByteArray data;
data << PIMemoryBlock(a_write, 10 * sizeof(float));

piCout << data.toHex();

data >> PIMemoryBlock(a_read, 10 * sizeof(float));
for (int i = 0; i < 10; ++i)
	piCout << a_read[i];
\endcode

\~english Result:
\~russian Результат:
\code{.cpp}
00000000cdcccc3dcdcc4c3e9a99993ecdcccc3e0000003f9a99193f3333333fcdcc4c3f6666663f
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
\endcode

\~english

If a read runs out of data (e.g. end of array or file), the stream's \c wasReadError() returns \c true. Check it after reads to handle errors correctly.

\~russian
Если при чтении из потока не хватило данных (например, закончился массив или файл), то проверка
объекта потока на \c wasReadError() вернёт \c true. Рекомендуется делать эту проверку после чтения
данных для корректной обработки ошибки.
