////////////////////////////////////////////////////////////////////////////////
/** 
  \file
  \brief \ru Сериализация:. Утилиты чтения и записи потоковых классов.
         \en Serialization: utilities for reading and writing of stream classes. \~

*/
////////////////////////////////////////////////////////////////////////////////

#ifndef __IO_TAPE_H
#define __IO_TAPE_H


/// \ru Для контроля чтения/записи char* и TCHAR* только через ReadTCHAR/WriteTCHAR() скомпилировать с данным дефайном и увидим все места, где незаконно пишутся/читаются данные через operator << и >>.
/// \en Compile with the given preprocessor define for control of reading/writing char* and TCHAR* only via ReadTCHAR/WriteTCHAR(), not via illegally operator << and >>. 
//#define DISABLE_RWTCHAR  

#include <io_tape_define.h>
#include <io_memory_buffer.h>
#include <templ_pointer.h>
#include <templ_sfdp_array.h>
#include <alg_indicator.h>
#include <tool_cstring.h>
#include <hash32.h>
#include <tool_mutex.h>
#include <map>
#include <typeinfo>


//----------------------------------------------------------------------------------------
/** \brief \ru Массив для регистрации объектов при чтении/записи.
           \en Array for object registration while reading/writing. \~
  \details \ru Массив для регистрации объектов при чтении/записи. \n
           \en Array for object registration while reading/writing. \n \~
  \ingroup Base_Tools_IO
*/ //---
class MATH_CLASS TapeRegistrator 
{
public:
  typedef std::map<const TapeBase *, size_t> TapeIndexMap;
  typedef std::map<size_t, TapeBase *>       IndexTapeMap;
protected:
  TapeIndexMap tapeIndexPairs; /// \ru Ассоциативный массив связок [указатель на объект]-[номер в массиве]. \en Map of [object]-[index] pairs.
  size_t       maxCount;       /// \ru Максимальное количество зарегистрированных объектов. \en Maximal number of registered objects.
  IndexTapeMap indexesAndObjs; /// \ru Ассоциативный массив связок [номер в массиве]-[указатель на объект]. \en Map of [index]-[object] pairs.

public:
  /// \ru Конструктор. \en Constructor. 
  TapeRegistrator();
  /// \ru Деструктор. \en Destructor. 
  virtual ~TapeRegistrator();

  /// \ru Получить количество зарегистрированных объектов. \en Get a number of registered objects. 
  size_t      Count() const;
  /// \ru Зарезервировать место под данное количество элементов. \en Reserve space for a given number of elements.
  bool        Reserve ( size_t n );
  
  /// \ru Найти объект в массиве. \en Find the object in array. 
  size_t      FindIt( const TapeBase * e ) const;
  /// \ru Существует ли объект в массиве. \en Whether the object is in the array. 
  bool        IsExist( const TapeBase * e ) const;
  /// \ru Узнать максимально возможное количество регистрируемых объектов. \en Get the maximal possible number of registered objects. 
  size_t      GetMaxCount() const;
  /// \ru Выдать из массива зарегистрированных объектов указатель по заданному индексу. \en Get the pointer from the registered object array by the given index. 
  TapeBase *  operator[]( size_t ind ) const ;

  ///< \ru Вставить элемент с определенным индексом. \en Insert an element with defined index. 
  void        AddAt( const TapeBase * e, size_t ind );
  /// \ru Добавить объект в массив. \en Add the object to the array. 
  size_t      Add( const TapeBase * e );

  /// \ru Выдать указатель на зарегистрированный объект по заданной позиции в кластере. \en Get the pointer of the registered object by the position in the cluster. 
  virtual TapeBase *        Get( const ClusterReference & ) const { return nullptr; } // unsupported
  /// \ru Выдать позицию в кластере по заданному индексу. \en Get position in the cluster by given index. 
  virtual ClusterReference  GetClusterRef( size_t )   const { return ClusterReference(); } // unsupported
  /// \ru Добавить позицию объекта в кластере. \en Add the object position in the cluster. 
  virtual void              AddClusterRef( size_t, const ClusterReference & ) {} // unsupported
  /// \ru Очистить массив зарегистрированных объектов. \en Flush the array of registered objects. 
  virtual void              FlushRegistered();
  /// \ru Очистить зарегистрированный объект \en Flush the registered object 
  virtual void              FlushObj( const TapeBase * );

  /// \ru Поменять местами массивы для регистрации
  void Swap ( TapeRegistrator & swapReg );

protected:
  /// \ru Очистить зарегистрированный объект \en Flush the registered object 
  void         FlushObjInd( size_t );

#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_
public:
  void * operator new    ( size_t size ) {
    return ::Allocate( size, typeid(TapeRegistrator).name() );
  }
  void   operator delete ( void * ptr, size_t size ) {
    ::Free( ptr, size, typeid(TapeRegistrator).name() );
  }
#endif // __DEBUG_MEMORY_ALLOCATE_FREE_

OBVIOUS_PRIVATE_COPY( TapeRegistrator )
};


//----------------------------------------------------------------------------------------
/** \brief \ru Массив для регистрации объектов с сохранениeм информации о позиции чтения/записи.
           \en Array for registration of objects with information about reading/writing position. \~
  \details \ru Массив для регистрации объектов с сохранениeм информации о позиции чтения/записи. \n
           \en Array for registration of objects with information about reading/writing position. \n \~
  \ingroup Base_Tools_IO
*/ //---
class MATH_CLASS TapeRegistratorEx : public TapeRegistrator {
public:
  typedef std::map<ClusterReference, size_t>        ClusterIndexMap;
  typedef std::map<size_t, const ClusterReference*> IndexClusterMap;
private:
  /// \ru Ассоциативный массив [cluster position] -> [object index]. Основное хранилище для значений ClusterReference.
  /// \en Map: [cluster position] -> [object index]. The primary container for ClusterReference values.
  ClusterIndexMap  refIndex;
  /// \ru Ассоциативный массив [object index] -> [pointer to cluster position]. Содержит указатели на объекты ClusterReference из массива refIndex.
  /// \en Map: [object index] -> [pointer to cluster position]. Contains pointers to object ClusterReference from refIndex map.
  IndexClusterMap  objReferences;

public:
  /// \ru Конструктор. \en Constructor. 
  TapeRegistratorEx();

  /// \ru Выдать указатель на зарегистрированный объект по заданной позиции в кластере.
  /// \en Get the pointer of the registered object by the position in the cluster. 
  virtual TapeBase *        Get ( const ClusterReference & ref ) const;
  /// \ru Выдать позицию в кластере по заданному индексу. \en Get position in the cluster by given index. 
  virtual ClusterReference  GetClusterRef ( size_t ind ) const;
  /// \ru Добавить позицию объекта в кластере. \en Add the object position in the cluster. 
  virtual void              AddClusterRef( size_t ind, const ClusterReference & ref );
  /// \ru Очистить массив зарегистрированных объектов. \en Flush the array of registered objects. 
  virtual void              FlushRegistered();
  /// \ru Очистить зарегистрированный объект \en Flush the registered object 
  virtual void              FlushObj ( const TapeBase * );

OBVIOUS_PRIVATE_COPY( TapeRegistratorEx )
};


//----------------------------------------------------------------------------------------
/** \brief \ru Индикатор прогресса в области видимости для reader.
           \en Scoped progress indicator for reader. \~
  \details \ru Индикатор прогресса в области видимости для чтения модели с помощью reader.
               Создается дочерний индикатор прогресса для reader. При выходе из области видимости 
               освобождается текущий индикатор прогресса и устанавливается родительский индикатор прогресса. \n
           \en Scoped progress indicator for reader.
               A scoped child progress indicator for reader is created.
               When exiting the scope, the current progress indicator is released and 
               the parent progress indicator is set. \n \~
  \ingroup Base_Tools_IO
*/ // ---
class MATH_CLASS ScopedReadProgress
{
  SPtr<ProgressBarWrapper>  _progress;
  reader &                  _reader;
public:
  ScopedReadProgress( reader & in );
  ~ScopedReadProgress();
  // \ru Доступ к индикатору прогресса. \en Access to progress indicator.
  ProgressBarWrapper * operator()();

private:
  ScopedReadProgress();
  void operator = ( const ScopedReadProgress & );
};


//----------------------------------------------------------------------------------------
/// \ru Шаблон функции создания нового экземпляра. \en Template of function of a new instance creation. \~ \ingroup Base_Tools_IO
//---
typedef TapeBase * ( CALL_DECLARATION * BUILD_FUNC ) ( void );

//----------------------------------------------------------------------------------------
/** \brief \ru Шаблон функции преобразования.
           \en Template of conversion function. \~
  \details \ru Шаблон функции преобразования из указателя на TapeBase к указателю на класс. \n
           \en Template of function of conversion from a pointer to TapeBase to a pointer to the class. \n \~
  \ingroup Base_Tools_IO
*/ //---
typedef void * ( CALL_DECLARATION * CAST_FUNC ) ( const TapeBase * );

//----------------------------------------------------------------------------------------
/**\ru Шаблон функции чтения экземпляра.
   \en Template of instance reading function. \~
   \ingroup Base_Tools_IO
*/ //---
typedef void ( CALL_DECLARATION * READ_FUNC ) ( reader & in, void * /*obj*/ );

//----------------------------------------------------------------------------------------
/// \ru Шаблон функции записи экземпляра. \en Template of instance writing function. \~ \ingroup Base_Tools_IO
//---
typedef void ( CALL_DECLARATION * WRITE_FUNC ) ( writer & out, void * /*obj*/ );


//----------------------------------------------------------------------------------------
/** \brief \ru Упакованное имя класса.
           \en Packed class name. \~
  \details \ru Упакованное имя одного класса - для набора массива потоковых классов в TapeClass. \n
           \en Packed name of one class - for array of stream classes in TapeClass. \n \~
  \ingroup Base_Tools_IO
*/ // ---
class MATH_CLASS ClassDescriptor
{
protected:
  uint16 val;     ///< \ru Хэш имени класса. \en The class name hash.
  MbUuid appID_;  ///< \ru Дополнительный идентификатор приложения. \en Additional application identifier.
private:
  /// \ru Признак записи appID. \en AppID record flag.
  static const uint16 rwIdFlag;

public:
  /// \ru Конструктор. \en Constructor. 
  ClassDescriptor();
  /// \ru Конструктор по хэшу. \en Constructor by hash. 
  ClassDescriptor( uint16 v );
  /// \ru Конструктор по имени. \en Constructor by name. 
  ClassDescriptor( const char * name );
  /// \ru Конструктор по хэшу. \en Constructor by hash. 
  ClassDescriptor( uint16 v, const MbUuid & appID );
  /// \ru Конструктор по имени. \en Constructor by name. 
  ClassDescriptor( const char * name, const MbUuid & appID );
  /// \ru Конструктор по хэшу. \en Constructor by hash. 
  ClassDescriptor( const ClassDescriptor & other );

  /// \ru Оператор присваивания. \en An assignment operator. 
  ClassDescriptor & operator = ( const ClassDescriptor & other );

  /// \ru Оператор равенства. \en The equality operator. 
  bool  operator == ( const ClassDescriptor & other ) const;
  /// \ru Оператор неравенства. \en The inequality operator. 
  bool  operator != ( const ClassDescriptor & other ) const;
  /// \ru Оператор сравнения. \en Comparison operator. 
  bool operator < ( const ClassDescriptor & other ) const;
  /// \ru Оператор сравнения. \en Comparison operator. 
  bool operator > ( const ClassDescriptor & other ) const;
#ifdef C3D_DEBUG
  /// \ru Оператор доступа. \en An access operator. 
  operator uint16() const { return val; }
#endif
  /// \ru Оператор записи. \en Write operator.
  void Write( writer & out );
  /// \ru Оператор чтения. \en Read operator.
  bool Read( reader & in );
};


//----------------------------------------------------------------------------------------
/** \brief \ru "Обертка" для одного потокового класса.
           \en "Wrapper" for one stream class. \~
  \details \ru "Обертка" для одного потокового класса ( не экземпляра! ).
    Xранит упакованное имя класса и адреса функций, необходимых при чтении/записи. \n
           \en "Wrapper" for one stream class ( not instance! ).
    Stores packed class name and addresses of functions necessary while reading/writing. \n \~
  \ingroup Base_Tools_IO
*/ // ---
class MATH_CLASS TapeClass {
protected:
  ClassDescriptor hashValue; ///< \ru Упакованное имя класса. \en Packed class name. 
  BUILD_FUNC     _builder;   ///< \ru Функция создания нового экземпляра. \en Functions of a new instance creation. 
  CAST_FUNC      _caster;    ///< \ru Функция преобразования от TapeBase к указателю на класс. \en Function of conversion from TapeBase to a pointer to a class. 
  READ_FUNC      _reader;    ///< \ru Функция чтения. \en Read function. 
  WRITE_FUNC     _writer;    ///< \ru Функция записи. \en Write function. 

public:
  /// \ru Конструктор. \en Constructor.
  /// \ru Конструктор. \en Constructor. 
  TapeClass( const char * name, BUILD_FUNC b, CAST_FUNC c, READ_FUNC r, WRITE_FUNC w );
  TapeClass( const char * name, MbUuid appID, BUILD_FUNC b, CAST_FUNC c, READ_FUNC r, WRITE_FUNC w );
  /// \ru Деструктор. \en Destructor. 
  virtual ~TapeClass();
  /// \ru Получить упакованное имя класса. \en Get the packed class name. 
  ClassDescriptor GetPackedClassName() const;
  /// \ru Получить упакованное имя класса для записи с учетом версии. \en Get the packed class name for writing subject to the version. 
  virtual ClassDescriptor GetPackedClassNameForWrite( VERSION ) const;

  friend class  TapeManager;
  friend struct TapeClassContainer;

  OBVIOUS_PRIVATE_COPY( TapeClass )
};


//----------------------------------------------------------------------------------------
/** \brief \ru Менеджер потоков.
           \en Stream manager. \~
  \details \ru Менеджер потоков чтения и записи. \n
           \en Reading and writing streams manager. \n \~
  \ingroup Base_Tools_IO
*/ // ---
class MATH_CLASS TapeManager {
private:
  //static TPointer<TapeManager> &  StaticTapeManager();
protected :
  TapeClass * currentClass; ///< \ru Указатель на объект потокового класса. \en A pointer to the object of stream class. 
protected :
  /// \ru Конструктор. \en Constructor. 
  TapeManager();
  /// \ru Конструктор копирования. \en Copy constructor. 
  TapeManager( const TapeManager & );
public :
  /// \ru Деструктор. \en Destructor. 
  virtual ~TapeManager() {}
public:
  /// \ru Чтение экземпляра. \en Reading of the instance. 
  bool            SpecimenReading             ( reader & r,       TapeBase & o ) const;
  /// \ru Запись экземпляра. \en Writing of the instance. 
  bool            SpecimenWriting             ( writer & w, const TapeBase & o ) const;
  /// \ru Установить текущим класс по упакованному имени класса. \en Set class as default one by packed class name. 
  bool            SetCurrentClassByDescriptor     ( const ClassDescriptor & descr );
  /// \ru Установить текущим класс по упакованному имени класса. \en Set class as default one by packed class name. 
  bool            SetCurrentClassByDescriptor ( const ClassDescriptor & descr, const VersionContainer & ver );
  /// \ru Установить текущим класс по имени класса. \en Set class to be current by class name. 
  //bool            SetCurrentClassByName       ( const char * name );
  /// \ru Создать объект потокового класса. \en Create an object of a stream class. 
  TapeBase *      BuildObject                 () const;   
  /// \ru Есть ли функция записи у класса? \en Is there the writing function in the class? 
  bool            HasWriter                   () const;
  /// \ru Получить упакованное имя класс для записи в версию. \en Get packed class name for writing to the version. 
  ClassDescriptor GetPackedClassNameForWrite  ( VERSION version ) const;

  /// \ru Отпустить менеджер потоков после использования. \en Release the stream manager after using. 
  virtual void    FreeTapeManager();

  /// \ru Получить ссылку на менеджер потоков. \en Get a reference to the stream manager. 
  static TapeManager & GetTapeManager();

private:
  TapeManager & operator = ( const TapeManager & );
};


//----------------------------------------------------------------------------------------
/** \brief \ru Массив регистрации потоковых классов.
           \en Array of stream classes registration. \~
  \details \ru Массив регистрации потоковых классов TapeClass. \n
           \en Array of stream TapeClass classes registration. \n \~
  \ingroup Base_Tools_IO
*/
// ---
struct TapeClassContainer 
{
  /// \ru Создать массив регистрации потоковых классов. \en Create an array of stream classes registration. 
  static EXPORT_DECLARATION TPointer< SFDPArray<TapeClass> > & CALL_DECLARATION StaticTapeClassContainer();

  /// \ru Добавить потоковый класс. \en Add a stream class. 
  static bool Add( TapeClass & tapeClass )
  {
    if ( !StaticTapeClassContainer() )
      StaticTapeClassContainer() = new SFDPArray<TapeClass>( 430, 1, TapeClass_Compare, nullptr ); // \ru не владеет \en doesn't own

    return StaticTapeClassContainer()->AddExact( tapeClass );
  }
  /// \ru Функция сравнения двух TapeClass для поиска. \en Function of two TapeClass comparison for a search. 
  static int  TapeClass_Search( const TapeClass & t1, size_t d ) 
  {
    return ( ( t1.hashValue == *(ClassDescriptor*)d ) ? 0 : (( t1.hashValue > *(ClassDescriptor*)d ) ? 1 : -1 ) );
  }
  /// \ru Функция сравнения двух TapeClass для сортировки при вставке. \en Function of two TapeClass comparison for sorting while inserting. 
  static int  TapeClass_Compare( const TapeClass & t1, const TapeClass & t2 ) 
  {
    return ( ( t1.hashValue == t2.hashValue ) ? 0 : (( t1.hashValue > t2.hashValue ) ? 1 : -1 ) );
  }
};


//----------------------------------------------------------------------------------------
/** \brief \ru Поток для чтения и записи.
           \en Stream for reading and writing. \~
  \details \ru Поток для чтения и записи. \n
           \en Stream for reading and writing. \n \~
  \deprecated \ru Класс устарел и будет удален в версии 2023.
              \en The class is deprecated and will be removed in version 2023. \~
  \ingroup Base_Tools_IO
*/ // ---
class MATH_CLASS rw : public writer, public reader {
public:
  typedef std::unique_ptr<rw> rw_ptr;
public:

  /// \ru Создать читатель/писатель для буфера в памяти. \en Create reader/writer for membuf.
  static rw_ptr CreateMemWriter( membuf & sb, uint8  om );

  /// \ru Конструктор. \en Constructor. 
  rw( iobuf & buf, uint16 om );

  virtual ~rw() {}

private:
  /// \ru Конструктор. \en Constructor. 
  rw( iobuf_Seq & sb, bool ownBuf, bool openSys, uint16 om, TapeRegistrator * reg );

  OBVIOUS_PRIVATE_COPY( rw )
};


//----------------------------------------------------------------------------------------
/// \ru Функция чтения базового класса. \en Function of reading the base class. \~ \ingroup Base_Tools_IO
// ---
template <class Base>
inline void ReadBase( reader & in, Base * base ) {
  Base::Read( in, base );
}

//----------------------------------------------------------------------------------------
/// \ru Функция записи базового класса. \en Function of writing the base class. \~ \ingroup Base_Tools_IO
// ---
template <class Base>
inline void WriteBase( writer & out, const Base * base ) {
  Base::Write( out, base );
}


//----------------------------------------------------------------------------------------
/// \ru Функция чтения виртуального базового класса. \en Function of reading of a virtual base class. \~ \ingroup Base_Tools_IO
// ---
template <class Base>
void ReadVBase( reader & in, Base * base )
{
  switch( in.readByte() ) {
    case tape::otIndexed:
      break;
    case tape::otObject :
      in.registrate( dynamic_cast<TapeBase*>(base) );
      Base::Read( in, base );
      break;
  }
}


//----------------------------------------------------------------------------------------
/// \ru Функция записи виртуального базового класса. \en Function of writing of a virtual base class. \~ \ingroup Base_Tools_IO
// ---
template <class Base>
void WriteVBase( writer & out, const Base * base )
{
  if ( !out.good() )
    return;
  if ( out.exist( dynamic_cast<TapeBase*>(base) ) )
    out.writeByte( tape::otIndexed );
  else {
    out.registrate( dynamic_cast<TapeBase*>(base) );
    out.writeByte( tape::otObject );
    Base::Write( out, base );
  }
}


//----------------------------------------------------------------------------------------
// \ru Удаление пробелов и записей перед пробелами. \en Deleting of spaces and records before spaces.  \~
// \ingroup Base_Tools_IO
// \ru Для совместимости с предыдущими компиляторами по именам возвращаемым typeid(a).name().
// \en For compatibility with the previous compilers by names returned by typeid(a).name().
// \ru Для определения того, что надо делать, скомпилируйте и запустите из консоли код:
// \en Compile and run the following code from console to define what is to do:
// #include <typeinfo>
// #include <iostream>
// class CLASS_A {
//   public: virtual ~CLASS_A() {} 
// };
//
// int main(int argc, char **argv) {
//   CLASS_A a;
//   std::cout << typeid(a).name() << '\n';
//   return 0;
// }
//
// \ru Выдаваемые значения \en Returned values  
// MS Visual C++ 6.0 ... 2010:     "class CLASS_A"
// gcc (Linux):                    "7CLASS_A"
// Embarcadero C++ 7.20 for Win32  "$CLASS_A"
// \ru Интересующая нас функция должна выдавать "CLASS_A" \en The desired function must write "CLASS_A"
//
// \ru Для имени шаблонного класса функция возвращает строку, состоящую из имени класса и имен параметров.
// \en For a template class name, the function returns a string consisting of the class name and parameter names.
// \ru Например, для имени "class ClassX<class ClassA,class ClassB>" функция возвращает "ClassXClassAClassB".
// \ru For example, for the name "class ClassX<class ClassA,class ClassB>" the function returns "ClassXClassAClassB".
// ---
MATH_FUNC( const char * ) pureName( const char * name );

//----------------------------------------------------------------------------------------
/// \ru Упаковать строку(имя класса) в uint16. \en Pack the string (class name) into uint16. \~ \ingroup Base_Tools_IO
// ---
inline
uint16 hash( const char * name )
{
  const uint16 * c = (const uint16 *)name;

  uint16 h = uint16(strlen(name));    // Mix in the string length.
  uint16 l = h;
  uint16 i = uint16(h / sizeof(uint16));  // Could do "<<" here, but less portable.

  while ( i-- )
    h ^= *c++;  // XOR in the characters.

                // If there are any remaining characters,
                // then XOR in the rest, using a mask:
  if ( (i = uint16(l % sizeof(uint16))) != 0 ) // SKIP_SA
    h ^= uint16(*c & 0xff);

  return h;
}


//----------------------------------------------------------------------------------------
/** \brief \ru Локальное задание локали.
           \en Local setting of the locale. \~
  \details \ru Поддержка RAII (Resource Acquisition Is Initialization) для задания локали. \n
           \en Support of RAII (Resource Acquisition Is Initialization) for the setting of the locale. \~
  \ingroup Base_Tools_IO
*/ // ---
class MATH_CLASS C3DLocaleSwitcher {
  static const char * UNSET;
  
  std::string _locale;
  int         _category;
public:
  /// \ru Конструктор. При fix == false это просто заглушка.
  /// \en Constructor. With fix == false, this is just a stub.
  C3DLocaleSwitcher( const char * loc, bool fix = true, int cat = LC_CTYPE ) : _locale( UNSET ), _category( cat ) {
    if ( fix ) {
      _locale = ::setlocale( cat, nullptr );
      ::setlocale( cat, loc );
    }
  }
  /// \ru Деструктор. \en Destructor.
  ~C3DLocaleSwitcher() {
    if ( _locale != UNSET )
      ::setlocale( _category, _locale.c_str() ); // \ru Вернуть предыдущую локаль. \en Return to the previous locale.
  }
private:
  C3DLocaleSwitcher();
  OBVIOUS_PRIVATE_COPY( C3DLocaleSwitcher )
};

#define FIX_ANSI1251 "ru_RU.CP1251"

//----------------------------------------------------------------------------------------
// \ru Чтение CHAR строки из потока. (в кодировке ANSI, с русской локалью) \en Reading of CHAR string from the stream. (in ANSI coding, with Russian locale) 
// \ru И нулевой указатель и пустая строка возвращаются как нулевой указатель! \en Both null pointer and an empty string are returned as null pointer!  
// \ru Длина строки не может превышать SYS_MAX_UINT16 - 1 \en The string length cannot exceed SYS_MAX_UINT16 - 1 
// \ru Созданную строку кто-то потом должен уничтожить (через delete[]) \en Created string must be deleted by someone then (using delete[]) 
// ---
inline
reader & __readChar( reader & ps, char *& s )
{
  s = nullptr;

  if ( ps.good() )
  {
    uint16 len = 0;
    ps.readBytes( &len, sizeof(len) );  // \ru длина строки - uint16 \en string length - uint16 

    if ( len == 0              ||
         len == SYS_MAX_UINT16 ||
         (len > 0 && ps.eof()) ||
         !ps.good() )
    {
      s = nullptr;
    }
    else // good
    {
      s = new char[len + 1];
      if ( s )
      {
        if ( ps.readBytes(s, len) )
        {
          // \ru прочли сколько нужно - добавить ограничивающий 0 \en have read as much as necessary - the terminating 0 is to be added 
          s[len] = 0;
        }
        else
        {
          // \ru прочли не все, скорее всего ошибка - очистить строку \en not everything has been read, must be an error - clear the string 
          delete [] s;
          s = nullptr;
        }
      }
      else 
        ps.setState( io::outOfMemory );
    }
  }

  return ps;
}


//----------------------------------------------------------------------------------------
// \ru Чтение WCHAR строки из потока. (в потоке хранится как UTF-16) \en Reading of WCHAR string from the stream. (stored in the stream as UTF-16) 
// \ru И нулевой указатель и пустая строка возвращаются как нулевой указатель! \en Both null pointer and an empty string are returned as null pointer!  
// \ru Длина строки не может превышать SYS_MAX_UINT32 - 1 \en The string length cannot exceed SYS_MAX_UINT32 - 1 
// \ru Созданную строку кто-то потом должен уничтожить \en Created string should be deleted by someone then 
// ---
inline
reader & __readWchar( reader & ps, TCHAR * & s )
{
  s = nullptr; // \ru на случай, если ничего не прочитаем \en for case if nothing will be read
  if ( ps.good() )
  {
    uint32 len = 0; 
    if ( ps.readBytes(&len, sizeof(len)) &&  // \ru длина строки в символах без терминального нуля - uint32 \en the string length in symbols without the termination zero - uint32 
         len != 0               &&
         len != SYS_MAX_UINT32  &&
         !ps.eof() )
    {
      uint16 * readBuf = new uint16[(size_t)len + 1]; // \ru длина (количество символов) вычитываемой строки с терминальным нулем \en length (number of symbols) of string being read with terminating 0 
      size_t size = sizeof(uint16) * (size_t)len;     // \ru длина (в байтах) вычитываемой строки без терминального нуля \en length (in bytes) of string being read without terminating null 

      if ( ps.readBytes(readBuf, size) ) 
        // \ru прочли сколько нужно - добавить ограничивающий 0 \en have read as much as necessary - the terminating 0 is to be added 
        readBuf[(size_t)len] = 0; 
      else {
        // \ru прочли не все, скорее всего ошибка - очистить строку \en not everything has been read, must be an error - clear the string 
        delete [] readBuf;
        readBuf = nullptr;
      }

      if ( readBuf ) { // is OK
#ifdef _UNICODE  // TCHAR == wchar_t
 #if __SIZEOF_WCHAR_T__ == 2  // sizeof(wchar_t) == sizeof(uint16)
        s = (TCHAR *)readBuf; // \ru собственно ничего конвертировать не нужно \en nothing to convert 
 #else // sizeof(wchar_t) == sizeof(uint32)
        s = (TCHAR *)Utf16ToUcs4(readBuf); // \ru Конвертировать UTF-16 в WCHAR \en Convert from UTF-16 to WCHAR 
        delete [] readBuf;
 #endif
#else // _UNICODE 
 #if __SIZEOF_WCHAR_T__ == 2  // sizeof(wchar_t) == sizeof(uint16)
        s = wcsnewmbs( (const wchar_t*)readBuf );  // \ru Конвертировать WCHAR в CHAR строку. \en Convert WCHAR-string to CHAR-string. 
 #else // sizeof(wchar_t) == sizeof(uint32)
  uint32* readBuf32 = Utf16ToUcs4(readBuf); // \ru Конвертировать UTF-16 в WCHAR \en Convert from UTF-16 to WCHAR 
  s = wcsnewmbs( (const wchar_t*)readBuf32 );  // \ru Конвертировать WCHAR в CHAR строку. \en Convert WCHAR-string to CHAR-string. 
  delete [] readBuf32;
 #endif
        delete [] readBuf;
#endif // _UNICODE 
      }
    }
  }

  return ps;
}


//----------------------------------------------------------------------------------------
// \ru Чтение WCHAR строки из потока. (в потоке хранится как UTF-16) \en Reading of WCHAR string from the stream. (stored in the stream as UTF-16) 
// \ru И нулевой указатель и пустая строка возвращаются как нулевой указатель! \en Both null pointer and an empty string are returned as null pointer!  
// \ru Длина строки не может превышать SYS_MAX_UINT32 - 1 \en The string length cannot exceed SYS_MAX_UINT32 - 1 
// \ru Созданную строку кто-то потом должен уничтожить \en Created string should be deleted by someone then 
// ---
inline
reader & __readWcharT( reader & ps, wchar_t * & s )
{
  s = nullptr; // \ru на случай, если ничего не прочитаем \en for case if nothing will be read
  if ( ps.good() )
  {
    uint32 len = 0; 
    if ( ps.readBytes(&len, sizeof(len)) &&  // \ru длина строки в символах без терминального нуля - uint32 \en the string length in symbols without the termination zero - uint32 
         len != 0               &&
         len != SYS_MAX_UINT32  &&
         !ps.eof() )
    {
      uint16 * readBuf = new uint16[(size_t)len + 1]; // \ru длина (количество символов) вычитываемой строки с терминальным нулем \en length (number of symbols) of string being read with terminating 0 
      size_t size = sizeof(uint16) * (size_t)len;     // \ru длина (в байтах) вычитываемой строки без терминального нуля \en length (in bytes) of string being read without terminating null 

      if ( ps.readBytes(readBuf, size) ) 
        // \ru прочли сколько нужно - добавить ограничивающий 0 \en have read as much as necessary - the terminating 0 is to be added 
        readBuf[(size_t)len] = 0; 
      else {
        // \ru прочли не все, скорее всего ошибка - очистить строку \en not everything has been read, must be an error - clear the string 
        delete [] readBuf;
        readBuf = nullptr;
      }

      if ( readBuf ) { // is OK
#if __SIZEOF_WCHAR_T__ == 2  // sizeof(wchar_t) == sizeof(uint16)
        s = (wchar_t *)readBuf; // \ru собственно ничего конвертировать не нужно \en nothing to convert 
#else // sizeof(wchar_t) == sizeof(uint32)
        s = (wchar_t *)Utf16ToUcs4(readBuf); // \ru Конвертировать UTF-16 в WCHAR \en Convert from UTF-16 to WCHAR 
        delete [] readBuf;
#endif
      }
    }
  }

  return ps;
}


#ifdef C3D_WINDOWS //_MSC_VER // \ru Код для поддержки КОМПАС \en Code for KOMPAS support 
//----------------------------------------------------------------------------------------
/// \ru Чтение CHAR строки из потока. \en Reading of CHAR string from the stream. \~ \ingroup Base_Tools_IO
// \ru И нулевой указатель и пустая строка возвращаются как нулевой указатель! \en Both null pointer and an empty string are returned as null pointer!  
// ---
#ifndef DISABLE_RWTCHAR
#ifdef _UNICODE
inline
reader & operator >> ( reader & ps, char *& s )
{
  return __readChar( ps, s );
}
#endif // _UNICODE
#endif // DISABLE_RWTCHAR


//----------------------------------------------------------------------------------------
/// \ru Запись CHAR строки в поток. \en Writing CHAR string to the stream. \~ \ingroup Base_Tools_IO
// ---
#ifndef DISABLE_RWTCHAR
#ifdef _UNICODE
inline
writer & operator << ( writer & ps, const char * s )
{
  return ps.__writeChar( s );
}
#endif // _UNICODE
#endif // DISABLE_RWTCHAR

//----------------------------------------------------------------------------------------
/// \ru Чтение WCHAR строки из потока. \en Reading of WCHAR string from the stream. \~ \ingroup Base_Tools_IO
// \ru И нулевой указатель и пустая строка возвращаются как нулевой указатель! \en Both null pointer and an empty string are returned as null pointer!  
// \ru длина строки не может превышать SYS_MAX_UINT32 - 1 \en length of string can't exceed SYS_MAX_UINT32 - 1 
// ---
#ifndef DISABLE_RWTCHAR
inline
reader & operator >> ( reader & ps, TCHAR *& s )
{
  return __readWchar( ps, s );
}
#endif // DISABLE_RWTCHAR


//----------------------------------------------------------------------------------------
/// \ru Запись WCHAR строки в поток. \en Writing WCHAR string to the stream. \~ \ingroup Base_Tools_IO
// \ru длина строки не может превышать SYS_MAX_UINT32 - 1 \en string length can't exceed SYS_MAX_UINT32 - 1 
// ---
#ifndef DISABLE_RWTCHAR
inline
writer & operator << ( writer & ps, const TCHAR * s )
{
  return ps.__writeWchar( s );
}
#endif // DISABLE_RWTCHAR

#endif // C3D_WINDOWS

#ifdef __MOBILE_VERSION__
#ifdef _UNICODE
inline reader & operator >> ( reader & ps, char *& s ){  return __readChar( ps, s );}
inline writer & operator << ( writer & ps, const char * s ){  return ps.__writeChar( s );}
inline reader & operator >> ( reader & ps, TCHAR *& s ){  return __readWchar( ps, s );}
inline writer & operator << ( writer & ps, const TCHAR * s ){  return ps.__writeWchar( s );}
#endif // _UNICODE
#endif // __MOBILE_VERSION__

//----------------------------------------------------------------------------------------
/// \ru Запись bool в поток. \en Writing bool to the stream. \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, bool i )
{
  //unsigned char val = i;
  uint8 val = i ? 1 : 0;
  ps.writeByte( val );
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение bool в поток. \en Reading of bool to the stream. \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, bool & i )
{
  if ( IsVersion16bit( ps.MathVersion() ) )
  {
    unsigned short tmp = 0;
    ps.readBytes( &tmp, sizeof(unsigned short) );
    i = !!tmp;
  }
  else 
    i = !!ps.readByte();
  
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись signed int в поток. \en Writing signed int to the stream. \~ \ingroup Base_Tools_IO
// \ru оператор записи для типов: int (не поддерживает int32, long, LONG - для них есть своя реализация) \en write operator for types: int (doesn't support int32, long, LONG - there is a separate implementation for them)  
// \ru поддерживает запись в 32 и 16-битный формат файла \en supports writing to 32- and 16-bit format of file 
// ---
inline
writer & operator << ( writer & ps, signed int i )
{
#ifdef C3D_WINDOWS //_MSC_VER 
  if ( IsVersion16bit( ps.MathVersion() ) )
  {
    // \ru чтение из 16-битной версии файла \en reading from 16-bit version of file 
    // \ru ради этого куска чтение/запись int отделено от чтения/записи long \en reading/writing of int is separated from reading/writing of long for the sake of this fragment 
    int16 val = (int16)i;
    ps.writeBytes( &val, sizeof(val) );
  }
  else {
    int32 val = (int32)i;
    ps.writeBytes( &val, sizeof(val) );
  }
  return ps;
#else // C3D_WINDOWS
  if ( IsVersion16bit( ps.MathVersion() ) )
    ps.setState( io::fail );  // \ru в Linux-версии 16-битные файлы не поддерживаются \en 16-bit files are not supported in Linux 
  else {
    int32 val = (int32)i;
    ps.writeBytes( &val, sizeof(val) );
  }
  return ps;
#endif // C3D_WINDOWS 
}


//----------------------------------------------------------------------------------------
/// \ru Запись unsigned int в поток. \en Writing unsigned int to the stream.  \~ \ingroup Base_Tools_IO
// \ru оператор записи для типов: uint (не поддерживает uint32, ulong - для них есть своя реализация) \en write operator for types: uint (doesn't support uint32, ulong - there is a separate implementation for them)  
// \ru поддерживает запись в 32 и 16-битный формат файла \en supports writing to 32- and 16-bit format of file 
// ---
inline
writer & operator << ( writer & ps, unsigned int i )
{
#ifdef C3D_WINDOWS //_MSC_VER 
  if ( IsVersion16bit( ps.MathVersion() ) ) { 
    // \ru чтение из 16-битной версии файла \en reading from 16-bit version of file 
    // \ru ради этого куска чтение/запись int отделено от чтения/записи long \en reading/writing of int is separated from reading/writing of long for the sake of this fragment 
    uint16 val = (uint16)i;
    ps.writeBytes( &val, sizeof(val) );
  }
  else {
    uint32 val = i;
    ps.writeBytes( &val, sizeof(val) );
  }
  return ps;
#else // C3D_WINDOWS 
  if ( IsVersion16bit( ps.MathVersion() ) ) 
    ps.setState( io::fail );  // \ru в Linux-версии 16-битные файлы не поддерживаются \en 16-bit files are not supported in Linux 
  else {
    uint32 val = i;
    ps.writeBytes( &val, sizeof(val) );
  }
  return ps;
#endif // C3D_WINDOWS 
}


//----------------------------------------------------------------------------------------
/// \ru Чтение signed int в поток. \en Reading signed int to the stream. \~ \ingroup Base_Tools_IO
// \ru оператор чтения для типов: int (не поддерживает int32, long, LONG - для них есть своя реализация) \en read operator for types: int (doesn't support int32, long, LONG - there is a separate implementation for them)  
// \ru поддерживает чтение из 32 и 16-битного формата файла \en supports reading from 32- and 16-bit format of file 
// ---
inline
reader & operator >> ( reader & ps, signed int & i )
{
#ifdef C3D_WINDOWS //_MSC_VER // Linux identical int/uint and int32/uint32
  if ( IsVersion16bit( ps.MathVersion() ) ) { 
    // \ru чтение из 16-битной версии файла \en reading from 16-bit version of file 
    // \ru ради этого куска чтение/запись int отделено от чтения/записи long \en reading/writing of int is separated from reading/writing of long for the sake of this fragment 
    int16 val = 0;
    ps.readBytes( &val, sizeof(val) );
    i = val;
  }
  else {
    int32 val = 0;
    ps.readBytes( &val, sizeof(val) );
    i = (signed int)val;
  }
  return ps;
#else // C3D_WINDOWS
  if ( IsVersion16bit( ps.MathVersion() ) ) 
    ps.setState( io::fail );  // \ru в Linux-версии 16-битные файлы не поддерживаются \en 16-bit files are not supported in Linux 
  else {
    int32 val = 0;
    ps.readBytes( &val, sizeof(val) );
    i = (signed int)val;
  }
  return ps;
#endif // C3D_WINDOWS
}


//----------------------------------------------------------------------------------------
/// \ru Чтение unsigned int в поток. \en Reading unsigned int to the stream. \~ \ingroup Base_Tools_IO
// \ru оператор чтения для типов: uint (не поддерживает uint32, ulong - для них есть своя реализация) \en read operator for types: uint (doesn't support uint32, ulong - there is a separate implementation for them)  
// \ru поддерживает чтение из 32 и 16-битного формата файла \en supports reading from 32- and 16-bit format of file 
// ---
inline
reader & operator >> ( reader & ps, unsigned int & i )
{
#ifdef C3D_WINDOWS //_MSC_VER // Linux identical int/uint and int32/uint32
  if ( IsVersion16bit( ps.MathVersion() ) )
  {
    // \ru чтение из 16-битной версии файла \en reading from 16-bit version of file 
    // \ru ради этого куска чтение/запись int отделено от чтения/записи long \en reading/writing of int is separated from reading/writing of long for the sake of this fragment 
    uint16 val = 0;
    ps.readBytes( &val, sizeof(val) );
    i = val;
  }
  else {
    uint32 val = 0;
    ps.readBytes( &val, sizeof(val) );
    i = (unsigned int)val;
  }
  return ps;
#else // C3D_WINDOWS
  if ( IsVersion16bit( ps.MathVersion() ) ) 
    ps.setState( io::fail );  // \ru в Linux-версии 16-битные файлы не поддерживаются \en 16-bit files are not supported in Linux 
  else {
    uint32 val = 0;
    ps.readBytes( &val, sizeof(val) );
    i = (unsigned int)val;
  }
  return ps;
#endif // C3D_WINDOWS
}


//----------------------------------------------------------------------------------------
/// \ru Запись int32 в поток. \en Writing int32 to the stream. \~ \ingroup Base_Tools_IO
// \ru оператор записи для типов: long, int32, LONG (не поддерживает int - для него есть своя реализация) \en write operator for types: long, int32, LONG (does not support int - there is a separate implementation for it)  
// \ru данные всегда пишутся в 32-разрядном формате \en data are always written in 32-bit format 
// ---
#ifdef C3D_WINDOWS //_MSC_VER // Linux identical int/uint and int32/uint32
// \ru ВНИМАНИЕ!!! В целях совместимости данных для задач скомпилированных под Windows и Linux \en NOTE!!! To provide data compatibility for tasks compiled for Windows and Linux  
// \ru ЗАПРЕЩАЕТСЯ использовать тип данных long и unsigned long. Используйте int32 и uint32 \en IT IS FORBIDDEN to use long and unsigned long data types. Use int32 and uint32 
inline
writer & operator << ( writer & ps, int32 l )
{
  ps.writeBytes( &l, sizeof(l) );
  return ps;
}
#endif // C3D_WINDOWS


//----------------------------------------------------------------------------------------
/// \ru Запись uint32 в поток. \en Writing uint32 to the stream. \~ \ingroup Base_Tools_IO
// \ru оператор записи для типов: ulong, uint32 (не поддерживает uint - для него есть своя реализация) \en write operator for types: ulong, uint32 (does not support uint - there is a separate implementation for it)  
// \ru данные всегда пишутся в 32-разрядном формате \en data are always written in 32-bit format 
// ---
#ifdef C3D_WINDOWS // Linux identical int/uint and int32/uint32
// \ru ВНИМАНИЕ!!! В целях совместимости данных для задач скомпилированных под Windows и Linux \en NOTE!!! To provide data compatibility for tasks compiled for Windows and Linux  
// \ru ЗАПРЕЩАЕТСЯ использовать тип данных long и unsigned long. Используйте int32 и uint32 \en IT IS FORBIDDEN to use long and unsigned long data types. Use int32 and uint32 
inline
writer & operator << ( writer & ps, uint32 l )
{
  ps.writeBytes( &l, sizeof(l) );
  return ps;
}
#endif // C3D_WINDOWS


//----------------------------------------------------------------------------------------
/// \ru Чтение int32 в поток. \en Reading int32 to the stream. \~ \ingroup Base_Tools_IO
// \ru оператор чтения для типов: long, int32, LONG  (не поддерживает int - для него есть своя реализация) \en read operator for types: long, int32, LONG (does not support int - there is a separate implementation for it)  
// \ru данные всегда читаются в 32-разрядном формате \en data are always read in 32-bit format 
// ---
#ifdef C3D_WINDOWS //_MSC_VER // Linux identical int/uint and int32/uint32
// \ru ВНИМАНИЕ!!! В целях совместимости данных для задач скомпилированных под Windows и Linux \en NOTE!!! To provide data compatibility for tasks compiled for Windows and Linux  
// \ru ЗАПРЕЩАЕТСЯ использовать тип данных long и unsigned long. Используйте int32 и uint32 \en IT IS FORBIDDEN to use long and unsigned long data types. Use int32 and uint32 
inline
reader & operator >> ( reader & ps, int32 & l )
{
  size_t size = sizeof(int32);
  if ( !ps.readBytes(&l, size) )
    l = 0;

  return ps;
}
#endif // C3D_WINDOWS


//----------------------------------------------------------------------------------------
/// \ru Чтение uint32 в поток. \en Reading uint32 to the stream. \~ \ingroup Base_Tools_IO
// \ru оператор чтения для типов: ulong, uint32 (не поддерживает uint - для него есть своя реализация) \en read operator for types: ulong, uint32 (does not support uint - there is a separate implementation for it)  
// \ru данные всегда читаются в 32-разрядном формате \en data are always read in 32-bit format 
// ---
#ifdef C3D_WINDOWS //_MSC_VER // Linux identical int/uint and int32/uint32
// \ru ВНИМАНИЕ!!! В целях совместимости данных для задач скомпилированных под Windows и Linux \en NOTE!!! To provide data compatibility for tasks compiled for Windows and Linux  
// \ru ЗАПРЕЩАЕТСЯ использовать тип данных long и unsigned long. Используйте int32 и uint32 \en IT IS FORBIDDEN to use long and unsigned long data types. Use int32 and uint32 
inline
reader & operator >> ( reader & ps, uint32 & l )
{
  size_t size = sizeof(uint32);
  if ( !ps.readBytes(&l, size) )
    l = 0;

  return ps;
}
#endif // C3D_WINDOWS


//----------------------------------------------------------------------------------------
/// \ru Запись int64 в поток. \en Writing int64 to the stream. \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, int64 val )
{
  ps.writeInt64( val );
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение int64 в поток. \en Reading int64 to the stream. \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, int64 & val )
{
  ps.readInt64( val );
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение signed char в поток. \en Reading signed char to the stream. \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, signed char & ch )
{
  ch = (signed char)ps.readByte();
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение unsigned char в поток. \en Reading unsigned char to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, unsigned char & ch )
{
  ch = (unsigned char)ps.readByte();
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение char в поток. \en Reading char to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, char & ch )
{
  ch = (char)ps.readByte();
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение signed short в поток. \en Reading signed short to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, signed short & sh )
{
  const size_t size = sizeof(sh);
  
  if ( !ps.readBytes(&sh, size) )
    sh = 0;

  return ps;
}

//----------------------------------------------------------------------------------------
/// \ru Чтение unsigned short в поток. \en Reading unsigned short to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, unsigned short & sh ) 
{
  const size_t size = sizeof(sh);
  
  if ( !ps.readBytes(&sh, size) )
    sh = 0;

  return ps;
}


#ifndef __ATS_BUILD__ // Нужно для сборки решения Tools\ATS...
//----------------------------------------------------------------------------------------
/// \ru Чтение wchar_t в поток. \en Reading wchar_t to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, wchar_t & sh )
{
  sh = 0; //Обнулить, т.к. размер 4 байта, а читаются только 2
  //size_t size = sizeof(sh);
  size_t size = 2; //В windows sizeof(wchar_t) = 2, в Android  sizeof(wchar_t) = 4

  if ( !ps.readBytes(&sh, size) )

  sh = 0;

  return ps;
}
#endif // __ATS_BUILD__


//----------------------------------------------------------------------------------------
/// \ru Чтение float в поток. \en Reading float to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, float & f )
{
  size_t size = sizeof(f);
  
  if ( !ps.readBytes(&f, size) )
    f = 0;

  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение double в поток. \en Reading double to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, double & d )
{
  size_t size = sizeof(d);

  if ( !ps.readBytes(&d, size) )
    d = 0;

  // \ru Проверка числа на определенность \en Check if the number is defined
  if ( c3d_isnan(d) )
    d = 0;

  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение long double из потока. \en Reading long double from the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, long double & l )
{
  size_t size = sizeof(l);

  if ( !ps.readBytes(&l, size) )
    l = 0;

  return ps;
}

//----------------------------------------------------------------------------------------
/// \ru Чтение smart-указателя из потока. \en Reading a smart pointer from the stream. \~ \ingroup Base_Tools_IO
// ---
template<class _Class>
inline
reader & operator >> ( reader & ps, SPtr<_Class> & sPtr )
{
  _Class * ptr = nullptr;
  ps >> ptr;
  sPtr.assign( ptr );
  return ps;
}

//----------------------------------------------------------------------------------------
/// \ru Запись smart-указателя в поток. \en Writing a smart pointer to the stream. \~ \ingroup Base_Tools_IO
// ---
template<class _Class>
inline
writer & operator << ( writer & ps, const SPtr<_Class> & sPtr )
{
  ps << sPtr.get();
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись signed char в поток. \en Write signed char to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, signed char ch )
{
  ps.writeByte( ch ); // \ru байт \en byte 
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись unsigned char в поток. \en Write unsigned char to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, unsigned char ch )
{
  ps.writeByte( ch ); // \ru байт \en byte 
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись char в поток. \en Write char to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, char ch )
{
  ps.writeByte( ch ); // \ru байт \en byte 
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись signed short в поток. \en Write signed short to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, signed short sh )
{
  ps.writeBytes( &sh, sizeof(sh) );
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись unsigned short в поток. \en Write unsigned short to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, unsigned short sh )
{
  ps.writeBytes( &sh, sizeof(sh) );
  return ps;
}


#ifndef __ATS_BUILD__ // Нужно для сборки решения Tools\ATS...
//----------------------------------------------------------------------------------------
/// \ru Запись wchar_t в поток. \en Write wchar_t to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, wchar_t sh )
{
  size_t size = 2; //В windows sizeof(wchar_t) = 2, в Android  sizeof(wchar_t) = 4
  ps.writeBytes( &sh, size );
  return ps;
}
#endif // __ATS_BUILD__


//----------------------------------------------------------------------------------------
/// \ru Запись float в поток. \en Write float to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, float f )
{
  ps.writeBytes( &f, sizeof(f) );
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись double в поток. \en Write double to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, const double & d )
{
  ps.writeBytes( &d, sizeof(d) );
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись long double в поток. \en Write long double to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, const long double & l )
{
  ps.writeBytes( &l, sizeof(l) );
  return ps;
}

//----------------------------------------------------------------------------------------
/// \ru Записать TCHAR строку в поток. \en Write TCHAR string to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
void WriteTCHAR( writer & out, const TCHAR * ts, bool directSingleByte = false ) 
{
  if ( directSingleByte || out.MathVersion() < UNICODE_VERSION ) 
  {
    // \ru пишем WCHAR* как CHAR* \en write WCHAR* as CHAR* 
    char * s = _tcsNstr( ts );  // \ru создаем ANSI из TCHAR (если TCHAR == char, то просто дублируем) \en create ANSI from TCHAR (if TCHAR == char, then simply duplicate) 
    out.__writeChar( s );      // \ru пишем строку в формате ANSI \en write string in ANSI format 
    delete [] s;
  }
  else 
  {
    // \ru пишем WCHAR* \en write WCHAR* 
    out.__writeWchar( ts ); 
  }
}

//----------------------------------------------------------------------------------------
/// \ru Прочитать TCHAR строку из потока. \en Read TCHAR string from the stream.  \~ \ingroup Base_Tools_IO
//---
inline
void ReadTCHAR( reader & in, TCHAR *& ts, bool directSingleByte = false ) 
{
  if ( directSingleByte || in.MathVersion() < UNICODE_VERSION )
  {
#ifndef C3D_WINDOWS
    C3DLocaleSwitcher lh( FIX_ANSI1251, in.MathVersion() < UNICODE_VERSION );
#endif
    // \ru читаем WCHAR* из CHAR* \en read WCHAR* from CHAR* 
    char * s = nullptr;
    __readChar( in, s );  // \ru читаем строку в формате ANSI \en read string in ANSI format 
    ts = _strNtcs( s );   // \ru создаем TCHAR из ANSI (если TCHAR == char, то просто дублируем) \en create TCHAR from ANSI (if TCHAR == char, then simply duplicate) 
    delete [] s;
  }
  else 
  {
    // \ru читаем WCHAR* \en read WCHAR* 
    __readWchar( in, ts ); 
  }
}


//----------------------------------------------------------------------------------------
/// \ru Записать wchar_t строку в поток. \en Write wchar_t string to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
void WriteWcharT( writer & out, const wchar_t * ts ) 
{
  out.__writeWcharT( ts ); 
}

//----------------------------------------------------------------------------------------
/// \ru Прочитать TCHAR строку из потока. \en Read TCHAR string from the stream.  \~ \ingroup Base_Tools_IO
//---
inline
void ReadWcharT( reader& in, wchar_t *& ts ) 
{
  __readWcharT( in, ts ); 
}


//----------------------------------------------------------------------------------------
/// \ru Запись size_t в зависимости от версии потока. \en Write size_t subject to the stream version.  \~ \ingroup Base_Tools_IO
// ---
inline
void WriteCOUNT( writer & out, size_t count )
{
  if ( IsVersion64bit( out.MathVersion() ) )
  {
    uint64 count64 = count;

    if ( out.MathVersion() >= 0x0F001001L ) {
      if ( count64 == SYS_MAX_T )
        count64 = SYS_MAX_UINT64;
    }

    out.writeUInt64( count64 );
  }
  else
  {
    // \ru OV_x64 проверить переполнение при записи 64-битных данных в 32-битный поток \en OV_x64 check for overflow while writing 64-bit data to 32-bit stream 
    if ( HiUint32( count ) != 0 )
      out.setState( io::underflow64to32 );

    uint32 _count = (uint32)LoUint32(count);
    out << _count;
  }
}

//----------------------------------------------------------------------------------------
/// \ru Запись ptrdiff_t в зависимости от версии потока. \en Writing ptrdiff_t subject to the stream version.  \~ \ingroup Base_Tools_IO
// ---
inline
void WriteINT_T( writer & out, ptrdiff_t count )
{
  if ( IsVersion64bit( out.MathVersion() ) )
  {
    out.writeInt64( count );
  }
  else
  {
    // \ru OV_x64 проверить переполнение при записи 64-битных данных в 32-битный поток \en OV_x64 check for overflow while writing 64-bit data to 32-bit stream 
    if ( (int64)count > (int64)SYS_MAX_INT32 || (int64)count < (int64)SYS_MIN_INT32 )
      out.setState( io::underflow64to32 );

    int32 _count = (int32)LoUint32(count);
    out << _count;
  }
}

//----------------------------------------------------------------------------------------
/// \ru Чтение size_t в зависимости от версии потока. \en Reading size_t subject to the stream version.  \~ \ingroup Base_Tools_IO
//
// \ru Т.к. операторы чтения/записи для uint и uint32 разные нужно \en Since reading/writing operators are different for uint and uint32,  
// \ru уметь читать оба, в зависимости от места где вызывается. \en there should be capability for reading both of them subject to the place where it is called. 
// \ru Решено запись не менять, т.к. в 16 битовую задачу не записываем. \en Decided not to modify writing since we do not write to 16-bit task. 
// ---
inline
size_t ReadCOUNT ( reader & in, bool uint_val = true )
{
  size_t count = 0;
  if ( IsVersion64bit( in.MathVersion() ) )
  {
    uint64  _count = 0;
    in.readUInt64( _count );

    if ( in.MathVersion() >= 0x0F001001L ) {
      if ( _count == SYS_MAX_UINT64 )
        _count = SYS_MAX_T;
    }
    else {
      // \Mapping\a_17356\17356_Телега.c3d PRECONDITION( _count != SYS_MAX_UINT32 ); // неоднозначность интерпретации значения // Primery_Set/Models_8/_58720. 
      if ( _count == SYS_MAX_UINT32 || _count == SYS_MAX_UINT64 )
        _count = SYS_MAX_T;
    }

    count = (size_t)_count; 

    // \ru OV_x64 проверить переполнение при чтении 64-битных данных в 32-битной задаче \en OV_x64 check for overflow while reading 64-bit data in 32-bit task 
    if ( HiUint32(count) != HiUint32(_count) )
      in.setState( io::underflow64to32 );
  }
  else
  {
    // \ru оператор чтения в uint (в отличии от uint32) поддерживает чтение 16-битных версий файла \en operator of reading to uint (in contrast to uint32) supports reading 16-bit versions of file 
    if ( uint_val )
    {
      uint _count = 0;
      in >> _count;

      count = (size_t)_count; 
    }
    else
    {
      uint32 _count = 0;
      in >> _count;
      count = (size_t)_count; 
    }

    if ( count == SYS_MAX_UINT32 )
      count = SYS_MAX_T;
  }

  return count;
}

//----------------------------------------------------------------------------------------
/// \ru Чтение ptrdiff_t в зависимости от версии потока. \en Reading ptrdiff_t subject to the stream version.  \~ \ingroup Base_Tools_IO
//
// \ru Т.к. операторы чтения/записи для uint и uint32 разные нужно \en Since reading/writing operators are different for uint and uint32,  
// \ru уметь читать оба, в зависимости от места где вызывается. \en there should be capability for reading both of them subject to the place where it is called. 
// \ru Решено запись не менять, т.к. в 16 битовую задачу не записываем. \en Decided not to modify writing since we do not write to 16-bit task. 
// ---
// \ru САА K13 31.8.2010 Исправление BUG 52091 \en CAA K13 31.8.2010 Fix for BUG 52091 
// \ru 77 вызовов и из них только 2 с false!!! - поэтому по умолчанию для всех \en 77 calls and only 2 of them with false!!! - so it is default for all 
inline
ptrdiff_t ReadINT_T( reader & in, bool uint_val = true )
{
  ptrdiff_t count = 0;
  if ( IsVersion64bit( in.MathVersion() ) )
  {
    int64  _count = 0;
    in.readInt64( _count );
    count = (ptrdiff_t)_count; 

    // \ru OV_x64 проверить переполнение при чтении 64-битных данных в 32-битной задаче \en OV_x64 check for overflow while reading 64-bit data in 32-bit task 
    if ( HiInt32(count) != HiInt32(_count) )
      in.setState( io::underflow64to32 );
  }
  else
  {
    // \ru оператор чтения в uint (в отличии от uint32) поддерживает чтение 16-битных версий файла \en operator of reading to uint (in contrast to uint32) supports reading 16-bit versions of file 
    if ( uint_val )
    {
      int _count = 0;
      in >> _count;
      count = (ptrdiff_t)_count;
    }
    else
    {
      int32 _count = 0;
      in >> _count;
      count = (ptrdiff_t)_count; 
    }
  }

  return count;
}


//----------------------------------------------------------------------------------------
/// \ru Запись size_t в память в зависимости от версии потока. \en Writing size_t to the memory subject to the stream version.  \~ \ingroup Base_Tools_IO
// ---
inline
void WriteCOUNT( void * out, VERSION version, size_t count )
{
  if ( IsVersion64bit(version) )
  {
    const uint64 count64 = (uint64)count;
    ::memcpy( out, &count64, sizeof(count64) );
  }
  else
  {
    // \ru OV_x64 проверить переполнение при записи 64-битных данных в 32-битный поток \en OV_x64 check for overflow while writing 64-bit data to 32-bit stream 
    //OV_x64 if ( count > (int64)_I32_MAX || count < (int64)_I32_MIN )
    //OV_x64   out.setState( io::underflow64to32 );

    PRECONDITION( count <= (size_t)SYS_MAX_UINT32/*_UI32_MAX*/ );
    const uint32 count32 = (uint32)LoUint32(count);

    ::memcpy( out, &count32, sizeof(count32) );
  }
}

//----------------------------------------------------------------------------------------
/// \ru Запись ptrdiff_t в память в зависимости от версии потока. \en Writing ptrdiff_t to the memory subject to the stream version.  \~ \ingroup Base_Tools_IO
// ---
inline
void WriteCOUNT( void * out, VERSION version, ptrdiff_t count )
{
  if ( IsVersion64bit(version) )
  {
    const int64 count64 = (int64)count;
    ::memcpy( out, &count64, sizeof(count64) );
  }
  else
  {
    // \ru OV_x64 проверить переполнение при записи 64-битных данных в 32-битный поток \en OV_x64 check for overflow while writing 64-bit data to 32-bit stream 
    //OV_x64 if ( count > (int64)_I32_MAX || count < (int64)_I32_MIN )
    //OV_x64   out.setState( io::underflow64to32 );

    PRECONDITION( count <= (ptrdiff_t)SYS_MAX_INT32/*_I32_MAX*/ && count >= (ptrdiff_t)SYS_MIN_INT32/*_I32_MIN*/ );
    const int32 count32 = (int32)LoUint32(count);

    ::memcpy( out, &count32, sizeof(count32) );
  }
}

//----------------------------------------------------------------------------------------
/// \ru Чтение size_t в память в зависимости от версии потока. \en Reading of size_t to the memory subject to the stream version.  \~ \ingroup Base_Tools_IO
// ---
inline
size_t ReadCOUNT ( void * in, VERSION version )
{
  size_t count = 0;

  if ( IsVersion64bit(version) )
  {
    uint64 count64 = 0;
    ::memcpy( &count64, in, sizeof(count64) );
    PRECONDITION( count64 <= SYS_MAX_T/*SIZE_MAX*/ );
    count = (size_t)count64; 

    // \ru OV_x64 проверить переполнение при чтении 64-битных данных в 32-битной задаче \en OV_x64 check for overflow while reading 64-bit data in 32-bit task 
    //OV_x64 if ( HiUint32( count ) != HiUint32( _count ) )
    //OV_x64   in.setState( io::underflow64to32 );
  }
  else
  {
    uint32 count32 = 0;
    ::memcpy( &count32, in, sizeof(count32) );
    PRECONDITION( sizeof(count32) <= sizeof(count)); // Cannot readCOUNT because sizof(uint32) > sizeof(size_t)
    count = /*AR (size_t)*/count32;   //-V101
  }

  return count;
}


//----------------------------------------------------------------------------------------
/** \brief \ru Получить упакованное имя класса по значению хэша записанному в поток.
           \en Get the packed class name from the hash value written to the stream. \~
  \details \ru Получить упакованное имя класса по значению хэша записанному в поток. \n
           \en Get the packed class name from the hash value written to the stream. \n \~
  \param[in] classDescr - \ru Обертка хэша.
                          \en Hash value wrapper. \~
  \param[in] ver  - \ru Версия потока в котором записан хэш. 
                    \en The version of the stream in which the hash is written. \~
  \result \ru Возвращает упакованное имя класса.
          \en Returns the packed class name. \~
  \ingroup Base_Tools_IO
*/ // ---
MATH_FUNC (ClassDescriptor) GetPackedClassName( const ClassDescriptor & classDescr, const VersionContainer & ver ); 


//----------------------------------------------------------------------------------------
/** \brief \ru Добавить новое соответствие значения хэша записанного в поток упакованному имени класса.
           \en Add a new mapping of the hash value written to the stream to the packed class name. \~
  \details \ru Добавить новое соответствие значения хэша записанного в поток упакованному имени класса. \n
           \en Add a new mapping of the hash value written to the stream to the packed class name. \n \~
  \param[in]  newClassName - \ru Новое имя класса.
                             \en New class name. \~
  \param[in]  oldClassName - \ru Старое имя класса.
                             \en Old class name. \~
  \param[in]  appIndex     - \ru Индекс приложения, которому принадлежит класс.
                             \en The index of the application that the class belongs to. \~
  \param[in]  lowVersion   - \ru Нижняя граница версии.
                             \en Version lower bound. \~
  \param[in]  highVersion  - \ru Верхняя граница версии.
                             \en Version upper bound. \~
  \ingroup Base_Tools_IO
*/ // ---
MATH_FUNC (void) AddPackedClassNameForVersion( const ClassDescriptor & newClassName, 
                                               const ClassDescriptor & oldClassName, 
                                                     uint              appIndex, 
                                                     VERSION           lowVersion, 
                                                     VERSION           highVersion );


//----------------------------------------------------------------------------------------
/** \brief \ru Диагностика коллизий имени нового класса с зарегистрированными классами.
           \en Diagnostics of collisions of new class name with registered classes. \~
  \details \ru Диагностика коллизий имени нового класса с зарегистрированными классами. \n
           \en Diagnostics of collisions of new class name with registered classes. \n \~
  \param[in]  className - \ru Имя класса. \en The class name. \~
  \param[in]  appID     - \ru Идентификатор приложения. \en The application id. \~
  \return \ru Признак уникальности класса с заданным именем при регистрации с указанным идентификатором приложения.
          \en Whether the class with the specified name is unique if registered with specified application id. \~
  \ingroup Base_Tools_IO
*/ // ---
MATH_FUNC( bool ) IsValidStreamClassName( const char * className, const MbUuid & appID );


#ifndef SIMPLENAME_AS_CLASS

//----------------------------------------------------------------------------------------
/// \ru Запись простого имени. \en Writing of a simple name.  \~ \ingroup Base_Tools_IO
// ---
inline void WriteSimpleName( writer & out, const SimpleName & s ) { out << s; }

//----------------------------------------------------------------------------------------
/// \ru Чтение простого имени. \en Reading of a simple name.  \~ \ingroup Base_Tools_IO
// ---
inline SimpleName ReadSimpleName( reader & in ) { SimpleName s = 0; in >> s; return s; }

#else // SIMPLENAME_AS_CLASS

//----------------------------------------------------------------------------------------
/// \ru Запись простого имени. \en Writing of a simple name.  \~ \ingroup Base_Tools_IO
// ---
inline
void WriteSimpleName( writer & out, const SimpleName & s )
{
  size_t sn = s;
  WriteCOUNT( out, sn );
}

//----------------------------------------------------------------------------------------
/// \ru Чтение простого имени. \en Reading of a simple name.  \~ \ingroup Base_Tools_IO
// ---
inline
SimpleName ReadSimpleName( reader & in )
{
  size_t sn = ReadCOUNT( in );
  return SimpleName(sn);
}

#endif // SIMPLENAME_AS_CLASS

namespace c3d // namespace C3D
{

//----------------------------------------------------------------------------------------
/// \ru Оператор записи хэша. \en Operator of writing hash.  \~ \ingroup Base_Tools_IO
//---
inline
writer & operator << ( writer & out, const StrHash & strHash )
{
  // \ru Нельзя допускать хеш с неопределенным типом и с определенным значением \en Hash with undefined type and with specified value cannot be allowed 
  PRECONDITION( !(IsGoodSimpleName(strHash.m_val) && strHash.m_type == c3d::StrHash::htp_undef) );

  WriteSimpleName( out, strHash.m_val );

  if ( out.MathVersion() >= 0x0A001005L )
    out << strHash.m_type;
  return out;
}

//----------------------------------------------------------------------------------------
/// \ru Оператор чтения хэша. \en Operator of hash reading.  \~ \ingroup Base_Tools_IO
//---
inline
reader & operator >> ( reader & in, c3d::StrHash & strHash )
{
  strHash.m_val = ReadSimpleName( in );

  if ( in.MathVersion() >= UNICODE_VERSION )
    if ( in.MathVersion() >= 0x0A001005L /*\ru введена запись типа хеша \en record of hash type is set */)
      in >> strHash.m_type;
    else
      strHash.m_type = c3d::StrHash::htp_wchar; // \ru !!! Переходный период, могут быть разные \en !!! Period of transition, may be different 
  else
    strHash.m_type = c3d::StrHash::htp_char;

  // \ru Нельзя допускать хеш с неопределенным типом и с определенным значением \en Hash with undefined type and with specified value cannot be allowed 
  PRECONDITION( !(IsGoodSimpleName(strHash.m_val) && strHash.m_type == c3d::StrHash::htp_undef) );

  return in;
}

} // namespace C3D


//----------------------------------------------------------------------------------------
/// \ru Запись строки в поток. \en Writing a string to the stream.  \~ \ingroup Base_Tools_IO
//---
inline
writer & operator << ( writer & ps, const std::string & s )
{
  if ( ps.MathVersion() < UNICODE_VERSION ) 
  {
    // \ru пишем WCHAR* как CHAR* \en write WCHAR* as CHAR* 
    ps.__writeChar( s.c_str() );      // \ru пишем строку в формате ANSI \en write string in ANSI format 
  } else {
    wchar_t* buf = mbsnewwcs( s.c_str() );
    WriteWcharT( ps, buf );
    delete[] buf;
  }
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение строки из потока. \en Reading a string from the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, std::string & s )
{
  if ( ps.MathVersion() < UNICODE_VERSION ) 
  {
#ifndef C3D_WINDOWS
    C3DLocaleSwitcher lh( FIX_ANSI1251 );
#endif
    char * str( nullptr );
    __readChar( ps, str );   // \ru читаем строку в формате ANSI \en read string in ANSI format 
    if ( str )
      s = str;
    else
      s.clear();
    delete [] str;
  } else {
    wchar_t * p (nullptr);
    ReadWcharT( ps, p ); // \ru в зависимости от версии потока \en subject to the stream version 
    if ( p ) {
      char* str = wcsnewmbs(p);    
      if ( str )
        s = str;
      else
        s.clear();
      delete[] str;
    }
    else
      s.clear();
    delete [] p;
  }
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись строки в поток. \en Writing a string to the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
writer & operator << ( writer & ps, const std::wstring & s )
{
  if ( ps.MathVersion() < UNICODE_VERSION ) 
  {
    // \ru пишем WCHAR* как CHAR* \en write WCHAR* as CHAR* 
    char * str = wcsnewmbs( s.c_str() );  // \ru создаем ANSI из TCHAR (если TCHAR == char, то просто дублируем) \en create ANSI from TCHAR (if TCHAR == char, then simply duplicate) 
    ps.__writeChar( str );      // \ru пишем строку в формате ANSI \en write string in ANSI format 
    delete [] str;
  }
  else
    WriteWcharT( ps, s.c_str() ); // \ru в зависимости от версии потока \en subject to the stream version 
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Чтение строки из потока. \en Reading a string from the stream.  \~ \ingroup Base_Tools_IO
// ---
inline
reader & operator >> ( reader & ps, std::wstring & s )
{
  if ( ps.MathVersion() < UNICODE_VERSION ) 
  {
#ifndef C3D_WINDOWS
    C3DLocaleSwitcher lh( FIX_ANSI1251 );
#endif
    char * str( nullptr );
    __readChar( ps, str );   // \ru читаем строку в формате ANSI \en read string in ANSI format 
    wchar_t* p = mbsnewwcs( str );
    if ( p )
      s = p;
    else
      s.clear();
    delete [] p;
    delete [] str;
  } else {
    wchar_t * p = nullptr;
    ReadWcharT( ps, p ); // \ru в зависимости от версии потока \en subject to the stream version 
    if ( p )
      s = p;
    else
      s.clear();
    delete [] p;
  }
  return ps;
}


//----------------------------------------------------------------------------------------
/// \ru Запись строки в поток. \en Writing a string to the stream.  \~ \ingroup Base_Tools_IO
//---
inline
writer & operator << ( writer & ps, const std::wstring * s )
{
  WriteWcharT( ps, (s ? s->c_str() : nullptr) ); // \ru в зависимости от версии потока \en subject to the stream version
  return ps;
}

//----------------------------------------------------------------------------------------
/// \ru Прочитать кластер. \en Read the cluster.  \~ \ingroup Base_Tools_IO
// ---
inline
void ReadCluster( reader & in, uint16 clusterSize, Cluster & cl ) 
{
  // \ru очистить поле указателя, т.к. в AllocMem есть проверка на 0 \en clear the pointer field since there is a check for 0 in AllocMem 
  cl.SetClusterOffset( 0 ); 
  // \ru распределяем кластер стандартной длины ... \en allocate a cluster of a standard length ... 
  cl.AllocMem( clusterSize );

  uint16 length = 0;
  in >> length;
  PRECONDITION( length <= clusterSize );
  cl.m_l = length;

  // \ru ...а читаем - сколько было занято \en ...but read as much as has been occupied 
  in.readBytes( (uint8*)cl.m_f, length );
}

//----------------------------------------------------------------------------------------
/// \ru Записать кластер, параметр clusterSize используется для проверки корректности длины кластера.
/// \en Write the cluster, the clusterSize parameter is used to checking the cluster length.
//  \~ \ingroup Base_Tools_IO
// ---
#ifdef C3D_DEBUG
inline
void WriteCluster( writer & out, const Cluster & cl, uint16 clusterSize )
#else
inline
void WriteCluster( writer & out, const Cluster & cl, uint16 /*clusterSize*/ )
#endif
{
  uint16 len = cl.m_l;
  PRECONDITION( len <= clusterSize );
  out << len;
  out.writeBytes( (const uint8*)cl.m_f, len );
}

//----------------------------------------------------------------------------------------
/// \ru Записать информацию о кластере. \en Write the information about the cluster.  \~ \ingroup Base_Tools_IO
// ---
inline
size_t WriteClusterInfo( void * out, VERSION version, const Cluster & obj )
{
  WriteCOUNT( out, version, obj.m_f );

  uint16 len = obj.m_l; 
  ::memcpy( (uint8*)out + LenCOUNT(version), &len, sizeof( len ) );

  return Cluster::SizeOf( version );
}

//----------------------------------------------------------------------------------------
/// \ru Прочитать информацию о кластере. \en Read the information about the cluster.  \~ \ingroup Base_Tools_IO
// ---
inline
size_t ReadClusterInfo( void * in, VERSION version, Cluster & obj )
{
  size_t off = ReadCOUNT( in, version );

  uint16 len = 0; 
  ::memcpy( &len, (uint8*)in + LenCOUNT(version), sizeof( len ) );

  obj.AllocFile( off, len ); // \ru запомнить смещение в файле и кол-во байт \en memorize the shift in file and the number of bytes 

  return Cluster::SizeOf( version );
}

//------------------------------------------------------------------------------
// \ru Записать содержимое кластера. Возвращает размер данных кластера или -1, если длина кластера больше заявленной.
// \en Write the cluster's contents. Return size of cluster's data or -1, if the cluster length is greater than the defined one.
// ---
inline
size_t WriteClusterBody( void * out, VERSION version, const Cluster & obj, uint16 clusterSize )
{
  uint8 * m = (uint8 *)out;

  // \ru кол-во заполненных байт в кластере \en the number of filled bytes in the cluster 
  uint16 l = obj._len();
  PRECONDITION( l <= clusterSize );
  *(uint16*)m = l;
  m += sizeof(uint16);

  if ( l > clusterSize )
    return (size_t)-1;

  if ( l ) {
    memcpy( m, obj._ptr(), l ); // \ru теперь данные кластера \en the cluster data now 
                                // не используется далее m += l;
  }

  return getMemLen( obj, version ) ;
}

//------------------------------------------------------------------------------
// \ru Прочитать содержимое кластера. Возвращает размер данных кластера или -1, если длина кластера больше заявленной.
// \en Read the cluster's contents. Return size of cluster's data or -1, if the cluster length is greater than the defined one.
// ---
inline
size_t ReadClusterBody( void * in, VERSION version, Cluster & obj, uint16 clusterSize )
{
  uint8 * m = (uint8 *)in;

  // \ru прочитать кол-во заполненных байт кластера и заполнить соответствующее поле в нем \en read the number of filled bytes in the cluster and fill its corresponding field 
  uint16 l = *(uint16*)m;
  PRECONDITION( l <= clusterSize );
  obj.SetClusterLength( l );
  m += sizeof(uint16);

  if ( l > clusterSize )
    return (size_t)-1;

  if ( l ) {
    memcpy( obj._getMemPointer(), m, l ); // \ru теперь данные кластера \en the cluster data now 
                                          // не используется далее m += l;
  }

  return getMemLen( obj, version );
}

//------------------------------------------------------------------------------
/**
  \brief \ru Стартовать try-catch регион для I/O операций. \en Start try-catch region for I/O operations. \~
  \ingroup Base_Tools
*/
//---
#define C3D_IO_CATCH_START  \
  try {

//------------------------------------------------------------------------------
/**
  \brief \ru Завершить try-catch регион для I/O операций (аргумент iostrm - поток чтения или записи).
         \en Complete try-catch region for I/O operations (argument iostrm - reading or writing stream). \~
  \ingroup Base_Tools
*/
//---
#define C3D_IO_CATCH_END(iostrm)        \
  }                                     \
  catch ( const std::bad_alloc & ) {    \
    iostrm.setState( io::outOfMemory ); \
  }                                     \
  catch ( ... ) {                       \
    iostrm.setState( io::fail );        \
  }


//------------------------------------------------------------------------------
// \ru Реализация функций записи по неконстантной ссылке/указателю (объявленных в KNOWN_OBJECTS_RW_REF_OPERATORS_BASE, KNOWN_OBJECTS_RW_PTR_OPERATORS_BASE)
//     для объектов, для которых при записи и чтении точно известен тип.
// \en Implementation of writing functions by non-constant reference/pointer (declared in KNOWN_OBJECTS_RW_REF_OPERATORS_BASE, KNOWN_OBJECTS_RW_PTR_OPERATORS_BASE)
//     for objects which type is exactly known while reading and writing.
//---
#define IMP_KNOWN_OBJECTS_RW_REF_OPERATORS(Class)                                        \
  writer & CALL_DECLARATION operator << ( writer & out,       Class & ref ) { return operator << ( out, (const Class &)ref ); }

#define IMP_KNOWN_OBJECTS_RW_PTR_OPERATORS(Class)                                        \
  writer & CALL_DECLARATION operator << ( writer & out,       Class * ptr ) { return operator << ( out, (const Class *)ptr ); }

//------------------------------------------------------------------------------
// \ru Реализация функций записи по неконстантной ссылке/указателю (объявленных в KNOWN_OBJECTS_RW_REF_OPERATORS_EX_BASE, KNOWN_OBJECTS_RW_PTR_OPERATORS_EX_BASE)
//     для объектов, для которых при записи и чтении точно известен тип.
// \en Implementation of writing functions by non-constant reference/pointer (declared in KNOWN_OBJECTS_RW_REF_OPERATORS_EX_BASE, KNOWN_OBJECTS_RW_PTR_OPERATORS_EX_BASE)
//     for objects which type is exactly known while reading and writing.
//---
#define IMP_KNOWN_OBJECTS_RW_REF_OPERATORS_EX(Class, DLLFUNC)                                    \
  DLLFUNC writer & CALL_DECLARATION operator << ( writer & out,       Class & ref ) { return operator << ( out, (const Class &)ref ); }

#define IMP_KNOWN_OBJECTS_RW_PTR_OPERATORS_EX(Class, DLLFUNC)                                    \
  DLLFUNC writer & CALL_DECLARATION operator << ( writer & out,       Class * ptr ) { return operator << ( out, (const Class *)ptr ); }


//----------------------------------------------------------------------------------------
/**
  \brief \ru Реализация дружественных операторов чтения и записи указателей и ссылок, объявленных в макросе DECLARE_PERSISTENT_OPS_BASE.
         \en Implementation of friend operators of reading and writing of pointers and references declared in DECLARE_PERSISTENT_OPS_BASE macro. \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_OPS_BASE( Class, DLLFUNC )                                              \
  DLLFUNC reader & CALL_DECLARATION operator >> ( reader & in, Class & ref ) {        \
    in.readObject( dynamic_cast<TapeBase *>(&ref) );                                  \
    return in;                                                                        \
  }                                                                                   \
  DLLFUNC reader & CALL_DECLARATION operator >> ( reader & in, Class *& ptr ) {       \
    ptr = dynamic_cast<Class *>( in.readObjectPointer() );                            \
    return in;                                                                        \
  }                                                                                   \
  DLLFUNC reader & CALL_DECLARATION operator >> ( reader & in, const Class *& ptr )   \
  {                                                                                   \
    ptr = dynamic_cast<Class *>( in.readObjectPointer() );                            \
    return in;                                                                        \
  }                                                                                   \
  DLLFUNC writer & CALL_DECLARATION operator << ( writer & out, const Class & ref ) { \
    out.writeObject( dynamic_cast<const TapeBase *>(&ref) );                          \
    return out;                                                                       \
  }                                                                                   \
  DLLFUNC writer & CALL_DECLARATION operator << ( writer & out, const Class * ptr ) { \
    out.writeObjectPointer( dynamic_cast<const TapeBase*>(ptr) );                     \
    return out;                                                                       \
  }                                                                                   \
  DLLFUNC writer & CALL_DECLARATION operator << ( writer & out, Class & ref ) {       \
    out.writeObject( dynamic_cast<TapeBase *>(&ref) );                                \
    return out;                                                                       \
  }                                                                                   \
  DLLFUNC writer & CALL_DECLARATION operator << ( writer& out, Class * ptr ) {        \
    out.writeObjectPointer( dynamic_cast<TapeBase *>(ptr) );                          \
    return out;                                                                       \
  }

//------------------------------------------------------------------------------
/// \ru Функции получения дескриптора (хэш + APP UID) класса. \~ \ingroup Base_Tools_IO
/// \ru Реализация макроса DECLARE_CLASS_DESC_FUNC. \en Implementation of DECLARE_CLASS_DESC_FUNC marco.
// ---
#define IMP_CLASS_DESC_FUNC( AppID, Class )                                   \
  ClassDescriptor Class::GetClassDescriptor( const VersionContainer & v) const \
    { return ClassDescriptor( GetPureName(v), AppID ); }


//----------------------------------------------------------------------------------------
/** \brief \ru Конструирование нового экземпляра класса.
           \en Construction of a new instance of the class. \~
  \details \ru Конструирование нового экземпляра класса. \n
    Определяются функция конструирования нового экземпляра класса,
    функция преобразования от указателя на TapeBase к указателю на класс
    и класс (не экземпляр!) добавляется в массив потоковых
    путем создания переменной r ## Class типа TapeClass
    (а в конструкторе TapeClass производится
    добавление в массив потоковых классов).
    Символ ## - это указание препроцессору о необходимости "склейки"
    текущего идентификатора с последующим.
           \en Construction of a new instance of the class. \n
    Definition of functions of construction a new instance of the class,
    function of conversion from a pointer to TapeBase to a pointer to the class
    and addition of the class (not an instance) to the array of stream classes
    by creating variable r ## Class of type TapeClass
    (and in constructor of TapeClass
    addition to array of stream classes is performed).
    Symbol ## is a directive for preprocessor about the necessity of "gluing"
    of the current identifier with the next one. \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_REGISTRATION( AppID, Class )                     \
  TapeBase * CALL_DECLARATION make ## _ ## Class () {                   \
    return new Class(tapeInit);                                         \
  }                                                                     \
  void * CALL_DECLARATION cast ## _ ## Class ( const TapeBase * obj ) { \
    return dynamic_cast<Class *>(const_cast<TapeBase *>(obj) );         \
  }                                                                     \
                                                                        \
  TapeClass  r ## Class(                                                \
    typeid(Class).name(),                                               \
    AppID,                                                              \
    (BUILD_FUNC) make ## _ ## Class,                                    \
    (CAST_FUNC ) cast ## _ ## Class,                                    \
    (READ_FUNC ) Class::Read,                                           \
    (WRITE_FUNC) Class::Write                                           \
  )


//------------------------------------------------------------------------------
// \ru Как записать переименованный класс в старую версию (с) Столяров А.Г. \en How to write the renamed class to the old version (c) Stolyarov A.G. 
/* #define IMP_PERSISTENT_REGISTRATION_OLDCLASS( Class, OldClass )         \
  TapeBase * CALL_DECLARATION make ## _ ## Class () {                   \
    return dynamic_cast<TapeBase *>( new Class(tapeInit) );             \
  }                                                                     \
  void * CALL_DECLARATION cast ## _ ## Class ( const TapeBase * obj ) { \
    return dynamic_cast<Class *>(const_cast<TapeBase *>(obj) );         \
  }                                                                     \
  TapeClass  r ## Class(                                                \
    typeid(Class).name(),                                               \
    typeid(OldClass).name(),                                            \
    (BUILD_FUNC) make ## _ ## Class,                                    \
    (CAST_FUNC ) cast ## _ ## Class,                                    \
    (READ_FUNC ) Class::Read,                                           \
    (WRITE_FUNC) Class::Write                                           \
  )

#define IMP_PERSISTENT_OLDCLASS( Class, OldClass )         \
  IMP_PERSISTENT_REGISTRATION_OLDCLASS( Class, OldClass ); \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class )

IMP_PERSISTENT_OLDCLASS( Class, OldClass );

class TapeClassForNewObjects : public TapeClass {
protected :
  ClassDescriptor hashValueOld;  // \ru упакованное имя класса для старой версии файла \en packed class name for the old version of file
public :
  TapeClassForNewObjects( const char * name, const char * oldName, BUILD_FUNC b, CAST_FUNC c, READ_FUNC r, WRITE_FUNC w );
  virtual ~TapeClassForNewObjects();
  virtual ClassDescriptor GetPackedClassNameForWrite( long version ) const;
  OBVIOUS_PRIVATE_COPY(TapeClassForNewObjects);
};

TapeClassForNewObjects::TapeClassForNewObjects( const char * name, const char * oldName,
                                                BUILD_FUNC b, CAST_FUNC c, READ_FUNC r, WRITE_FUNC w )
  : TapeClass( name, b, c, r, w )
  , hashValueOld( ::hash(::pureName( oldName ) ) )
{
}

ClassDescriptor TapeClassForNewObjects::GetPackedClassNameForWrite( long version ) const {
  uint16 res = version > CHANGE_VERSION ? TapeClass::GetPackedClassName() : uint16(hashValueOld);
  return res;
}
*/


#ifdef __DEBUG_MEMORY_ALLOCATE_FREE_

//--------------------------------------------------------------------------------------
/// \ru Реализация функций new, delete и операторов доступа (объявленных в DECLARE_NEW_DELETE_CLASS).
/// \en Implementation of functions new, delete and access operators (declared in DECLARE_NEW_DELETE_CLASS).
/// \~ \ingroup Base_Tools_IO
// ---
#define IMP_PERSISTENT_NEW_DELETE_CLASS( Class )

//--------------------------------------------------------------------------------------
/// \ru Реализация операторов new и delete, обеспечивающих последовательное обращение
///  к функциям выделения/освобождения памяти из разных потоков (объявленных в DECLARE_NEW_DELETE_CLASS_EX).
/// \en Implementation of new and delete operators which provide sequential access
/// to the allocation/deallocation functions from different threads (declared in DECLARE_NEW_DELETE_CLASS_EX).  \~
/// \ingroup Base_Tools_IO
// ---
#define IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class )            \
    void * Class::operator new( size_t size ) {                  \
      return ::Allocate( size, typeid(Class).name() ); }         \
    void Class::operator delete ( void *ptr, size_t size ) {     \
      ::Free( ptr, size, typeid(Class).name() ); }               \
                                                                 \
    void * Class::operator new[] ( size_t size ) {               \
      return ::AllocateArray( size, typeid(Class[]).name()); }   \
    void Class::operator delete[] ( void *ptr ) {                \
      ::FreeArray( ptr, typeid(Class[]).name() ); }              

#else // __DEBUG_MEMORY_ALLOCATE_FREE_

//--------------------------------------------------------------------------------------
/// \ru Реализация функций new, delete и операторов доступа (объявленных в DECLARE_NEW_DELETE_CLASS).
/// \en Implementation of functions new, delete and access operators (declared в DECLARE_NEW_DELETE_CLASS).
/// \~ \ingroup Base_Tools_IO
// ---
#define IMP_PERSISTENT_NEW_DELETE_CLASS( Class )

#if defined(__OVERLOAD_MEMORY_ALLOCATE_FREE_) && !defined(C3D_DEBUG)

  //--------------------------------------------------------------------------------------
  /// \ru Реализация операторов new и delete, обеспечивающая последовательное обращение
  ///  к функциям выделения/освобождения памяти из разных потоков (объявленных в DECLARE_NEW_DELETE_CLASS_EX).
  /// Перегружаются все стандартные операторы new и delete.
  /// \en Implementation of new and delete operators which provides sequential access
  /// to the allocation/deallocation functions from different threads (declared in DECLARE_NEW_DELETE_CLASS_EX).
  /// All standard new and delete operators are overloaded. \~
  /// \ingroup Base_Tools_IO
  // ---
#define IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class )          \
    void* Class::operator new( size_t size ) {                 \
      SET_MEMORY_SCOPED_LOCK;                                  \
      return ::operator new( size ); }                         \
    void Class::operator delete( void *ptr ) {                 \
      SET_MEMORY_SCOPED_LOCK;                                  \
      ::operator delete( ptr ); }                              \
                                                               \
    void* Class::operator new( size_t size, void *ptr ) {      \
      SET_MEMORY_SCOPED_LOCK;                                  \
      return ::operator new( size, ptr ); }                    \
    void Class::operator delete( void *ptr, void *ptr2 ) {     \
      SET_MEMORY_SCOPED_LOCK;                                  \
      ::operator delete( ptr, ptr2 ); }                        \
                                                               \
    void* Class::operator new[]( size_t size ) {               \
      SET_MEMORY_SCOPED_LOCK;                                  \
      return ::operator new[]( size ); }                       \
    void Class::operator delete[]( void *ptr ) {               \
      SET_MEMORY_SCOPED_LOCK;                                  \
      ::operator delete[]( ptr ); }                            \
                                                               \
    void* Class::operator new []( size_t size, void *ptr ) {   \
      SET_MEMORY_SCOPED_LOCK;                                  \
      return ::operator new[]( size, ptr ); }                  \
    void Class::operator delete []( void *ptr, void *ptr2 ) {  \
      SET_MEMORY_SCOPED_LOCK;                                  \
      ::operator delete[]( ptr, ptr2 ); }                      \
                                                               \
    void* Class::operator new( size_t size, const std::nothrow_t &nt ) throw() {    \
      SET_MEMORY_SCOPED_LOCK;                                                       \
      return ::operator new( size, nt ); }                                          \
    void Class::operator delete( void *ptr, const std::nothrow_t &nt ) throw() {    \
      SET_MEMORY_SCOPED_LOCK;                                                       \
      ::operator delete( ptr, nt ); }                                               \
                                                                                    \
    void* Class::operator new []( size_t size, const std::nothrow_t &nt ) throw() { \
      SET_MEMORY_SCOPED_LOCK;                                                       \
      return ::operator new[]( size, nt ); }                                        \
    void Class::operator delete []( void *ptr, const std::nothrow_t &nt ) throw() { \
      SET_MEMORY_SCOPED_LOCK;                                                       \
      ::operator delete[]( ptr, nt ); }

#else // __OVERLOAD_MEMORY_ALLOCATE_FREE_

  //--------------------------------------------------------------------------------------
  /// \ru Реализация операторов new и delete, обеспечивающая последовательное обращение
  ///  к функциям выделения/освобождения памяти из разных потоков (объявленных в DECLARE_NEW_DELETE_CLASS_EX).
  /// \en Implementation of new and delete operators which provides sequential access
  /// to the allocation/deallocation functions from different threads (declared in DECLARE_NEW_DELETE_CLASS_EX).  \~
  /// \ingroup Base_Tools_IO
  // ---
#define IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class )

#endif // __OVERLOAD_MEMORY_ALLOCATE_FREE_
#endif // __DEBUG_MEMORY_ALLOCATE_FREE_


//------------------------------------------------------------------------------
/** \brief \ru Реализация объявления DECLARE_PERSISTENT_CLASS_BASE.
           \en Implementation of DECLARE_PERSISTENT_CLASS_BASE declaration. \~
  \details \ru Реализация объявления DECLARE_PERSISTENT_CLASS_BASE.
    Описывает необходимые действия для поточного класса.
    Устанавливается в любой .cpp файл.
    Class должен наследовать от TapeBase.
    Должны быть реализованы функции чтения Read и записи Write. \n
           \en Implementation of DECLARE_PERSISTENT_CLASS_BASE declaration.
    Describes the necessary operations for a stream class.
    It is set into any .cpp file.
    Class must be inherited from TapeBase.
    Function Read of reading and function Write of writing should be implemented. \n \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_CLASS_OPS( AppID, Class, DLLFUNC ) \
  IMP_PERSISTENT_REGISTRATION( AppID, Class );            \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class );               \
  IMP_PERSISTENT_OPS_BASE( Class, DLLFUNC );              \
  IMP_CLASS_DESC_FUNC( AppID, Class )


//------------------------------------------------------------------------------
/** \brief \ru Реализация объявления DECLARE_PERSISTENT_CLASS_BASE для абстрактного поточного класса.
           \en Implementation of DECLARE_PERSISTENT_CLASS_BASE declaration for an abstract stream class. \~
  \details \ru Реализация объявления DECLARE_PERSISTENT_CLASS_BASE для абстрактного поточного класса.
    Описывает необходимые действия для поточного класса.
    Устанавливается в любой .cpp файл.
    Class должен наследовать от TapeBase.
    Должны быть реализованы функции чтения Read и записи Write. \n
           \en Implementation of DECLARE_PERSISTENT_CLASS_BASE declaration for an abstract stream class.
    Describes the necessary operations for a stream class.
    It is set into any .cpp file.
    Class must be inherited from TapeBase.
    Function Read of reading and function Write of writing should be implemented. \n \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_A_PERSISTENT_CLASS_OPS( AppID, Class, DLLFUNC ) \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class );               \
  IMP_PERSISTENT_OPS_BASE( Class, DLLFUNC );              \
  IMP_CLASS_DESC_FUNC( AppID, Class )


//------------------------------------------------------------------------------
/** \brief \ru Реализация объявления DECLARE_PERSISTENT_CLASS_NEW_DEL_BASE.
           \en Implementation of DECLARE_PERSISTENT_CLASS_NEW_DEL_BASE declaration. \~
  \details \ru Реализация объявления DECLARE_PERSISTENT_CLASS_NEW_DEL_BASE.
    Описывает необходимые действия для поточного класса.
    Устанавливается в любой .cpp файл.
    Class должен наследовать от TapeBase.
    Должны быть реализованы функции чтения Read и записи Write. \n
           \en Implementation of DECLARE_PERSISTENT_CLASS_NEW_DEL_BASE declaration.
    Describes the necessary operations for a stream class.
    It is set into any .cpp file.
    Class must be inherited from TapeBase.
    Function Read of reading and function Write of writing should be implemented. \n \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_CLASS_NEW_DEL_OPS( AppID, Class, DLLFUNC ) \
  IMP_PERSISTENT_CLASS_OPS( AppID, Class, DLLFUNC );            \
  IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class );


//----------------------------------------------------------------------------------------
// \ru Описывает необходимые операции для абстрактного поточного класса, \en Describes the necessary operations for the abstract stream class 
// \ru не наследующего ни от кого кроме TapeBase, и у которого \en which is not inherited from any class except TapeBase and which 
// \ru нет своих полей данных для записи в поток \en has no its own data fields for writing to stream 
// \ru Устанавливается в любой .cpp файл \en It is set to any .cpp file 
// \ru Примечание : \en Note: 
// \ru 1. Class должен наследовать от TapeBase \en 1. Class must be inherited from TapeBase 
// \ru 2. Требуется (!!!) отсутствие (!!!) функций \en 2. The following functions must be (!!!) absent (!!!) 
//      - void Class::Read( reader& in, Class* obj );
//      - void Class::Write( writer& out, const Class* obj );
// \ru где Class - имя класса \en where Class is a class name 
// ---
#define IMP_A_PERSISTENT_CLASS_WD( AppID, Class )   \
  void Class::Read( reader &, Class * )         {}  \
  void Class::Write( writer &, const Class * )  {}  \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class )          \
  IMP_CLASS_DESC_FUNC( AppID, Class )


//----------------------------------------------------------------------------------------
// \ru Описывает необходимые операции для абстрактного поточного класса с проверкой главной версии потока (версии математического ядра),
// \en Describes the necessary operations for the abstract stream class with check of the main stream version (the mathenatical kernel version),
// \ru не наследующего ни от кого кроме TapeBase, и у которого \en which is not inherited from any class except TapeBase and which 
// \ru нет своих полей данных для записи в поток. \en has no its own data fields for writing to stream. 
// \ru Устанавливается в любой .cpp файл \en It is set to any .cpp file 
// \ru Примечание : \en Note: 
// \ru 1. Class должен наследовать от TapeBase \en 1. Class must be inherited from TapeBase 
// \ru 2. Требуется (!!!) отсутствие (!!!) функций \en 2. The following functions must be (!!!) absent (!!!) 
//      - void Class::Read( reader& in, Class* obj );
//      - void Class::Write( writer& out, const Class* obj );
// \ru где Class - имя класса \en where Class is a class name 
// ---
#define IMP_A_PERSISTENT_MATH_CLASS_WD( AppID, Class )   \
  void Class::Read( reader &, Class * )         {}  \
  void Class::Write( writer & out, const Class * )  \
  { if ( out.MathVersion() < wrv_FirstRelease ) out.setState( io::cantWriteObject ); }  \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class )          \
  IMP_CLASS_DESC_FUNC( AppID, Class )

//----------------------------------------------------------------------------------------
// \ru Описывает необходимые операции для абстрактного \en Describes the necessary operations for abstract 
// \ru поточного класса \en stream class 
// \ru Устанавливается в любой .cpp файл \en It is set to any .cpp file 
// \ru Примечание : \en Note: 
// \ru 1. Class должен наследовать от TapeBase \en 1. Class must be inherited from TapeBase 
// \ru 2. Требуется наличие функций \en 2. There must be the following functions 
//      - void Class::Read( reader& in, Class* obj );
//      - void Class::Write( writer& out, const Class* obj );
// \ru где Class - имя класса \en where Class is a class name 
// ---
#define IMP_A_PERSISTENT_CLASS( AppID, Class ) \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class )     \
  IMP_CLASS_DESC_FUNC( AppID, Class )

//----------------------------------------------------------------------------------------
// \ru Описывает необходимые операции абстрактного поточного класса, \en Describes the necessary operations for the abstract stream class 
// \ru наследующего от другого такого же, и у которого \en inherited from another class which is the same and which 
// \ru нет своих полей данных для записи в поток \en has no its own data fields for writing to stream 
// \ru Устанавливается в любой .cpp файл \en It is set to any .cpp file 
// \ru Примечание : \en Note: 
// \ru 1. Class должен наследовать от TapeBase \en 1. Class must be inherited from TapeBase 
// \ru 2. Требуется (!!!) отсутствие (!!!) функций \en 2. The following functions must be (!!!) absent (!!!) 
//      - void Class::Read( reader& in, Class* obj );
//      - void Class::Write( writer& out, const Class* obj );
// \ru где Class - имя класса \en where Class is a class name 
// \ru эти функции генерируются автоматически \en these functions are generated automatically 
// ---
#define IMP_A_PERSISTENT_CLASS_FROM_BASE( AppID, Class, Base )   \
  void Class::Read( reader & in, Class * obj ) {                 \
    Base::Read( in, obj );                                       \
  }                                                              \
  void Class::Write( writer & out, const Class * obj ) {         \
    Base::Write( out, obj );                                     \
  }                                                              \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class )                       \
  IMP_CLASS_DESC_FUNC( AppID, Class )

//----------------------------------------------------------------------------------------
// \ru Описывает необходимые операции поточного класса, \en Describes the necessary operations of the stream class 
// \ru наследующего от другого такого же, и у которого \en inherited from another class which is the same and which 
// \ru нет своих полей данных для записи в поток \en has no its own data fields for writing to stream 
// \ru Устанавливается в любой .cpp файл \en It is set to any .cpp file 
// \ru Примечание : \en Note: 
// \ru 1. Class должен наследовать от TapeBase \en 1. Class must be inherited from TapeBase 
// \ru 2. Требуется (!!!) отсутствие (!!!) функций \en 2. The following functions must be (!!!) absent (!!!) 
//      - void Class::Read( reader& in, Class* obj );
//      - void Class::Write( writer& out, const Class* obj );
// \ru где Class - имя класса \en where Class is a class name 
// \ru эти функции генерируются автоматически \en these functions are generated automatically 
// ---
#define IMP_PERSISTENT_CLASS_FROM_BASE( AppID, Class, Base ) \
  IMP_PERSISTENT_REGISTRATION( AppID, Class );               \
  IMP_A_PERSISTENT_CLASS_FROM_BASE( AppID, Class, Base )

//------------------------------------------------------------------------------
/** \brief \ru Аналог макроса IMP_PERSISTENT_CLASS_FROM_BASE
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков.
           \en Analog of IMP_PERSISTENT_CLASS_FROM_BASE macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads. \~
  \details \ru Аналог макроса IMP_PERSISTENT_CLASS_FROM_BASE
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков
           (включается переменной __OVERLOAD_MEMORY_ALLOCATE_FREE_).
           \en Analog of IMP_PERSISTENT_CLASS_FROM_BASE macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads
           (enabled by defining __OVERLOAD_MEMORY_ALLOCATE_FREE_). \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_CLASS_FROM_BASE_NEW_DEL( AppID, Class, Base ) \
  IMP_PERSISTENT_CLASS_FROM_BASE( AppID, Class, Base )             \
  IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class )

//----------------------------------------------------------------------------------------
// \ru Описывает необходимые операции для поточного класса, \en Describes the necessary operations for the stream class 
// \ru не наследующего ни от кого кроме TapeBase, и у которого \en which is not inherited from any class except TapeBase and which 
// \ru нет своих полей данных для записи в поток \en has no its own data fields for writing to stream 
// \ru Устанавливается в любой .cpp файл \en It is set to any .cpp file 
// \ru Примечание : \en Note: 
// \ru 1. Class должен наследовать от TapeBase \en 1. Class must be inherited from TapeBase 
// \ru 2. Требуется (!!!) отсутствие (!!!) функций \en 2. The following functions must be (!!!) absent (!!!) 
//      - void Class::Read( reader& in, Class* obj );
//      - void Class::Write( writer& out, const Class* obj );
// \ru где Class - имя класса \en where Class is a class name 
// ---
#define IMP_PERSISTENT_CLASS_WD( AppID, Class )     \
  IMP_PERSISTENT_REGISTRATION( AppID, Class );      \
  void Class::Read( reader &, Class * )         {}  \
  void Class::Write( writer &, const Class * )  {}  \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class )          \
  IMP_CLASS_DESC_FUNC( AppID, Class )

//----------------------------------------------------------------------------------------
/** \brief \ru Аналог макроса IMP_PERSISTENT_CLASS_WD
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков.
           \en Analog of IMP_PERSISTENT_CLASS_WD macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads. \~
  \details \ru Аналог макроса IMP_PERSISTENT_CLASS_WD
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков
           (включается переменной __OVERLOAD_MEMORY_ALLOCATE_FREE_).
           \en Analog of IMP_PERSISTENT_CLASS_WD macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads
           (enabled by defining __OVERLOAD_MEMORY_ALLOCATE_FREE_). \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_CLASS_WD_NEW_DEL( AppID, Class ) \
  IMP_PERSISTENT_CLASS_WD( AppID, Class );              \
  IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class )


//------------------------------------------------------------------------------
/** \brief \ru Реализация объявления DECLARE_PERSISTENT_CLASS.
           \en Implementation of DECLARE_PERSISTENT_CLASS declaration. \~
  \details \ru Реализация объявления DECLARE_PERSISTENT_CLASS.
    Описывает необходимые действия для поточного класса.
    Устанавливается в любой .cpp файл.
    Class должен наследовать от TapeBase.
    Должны быть реализованы функции чтения Read и записи Write. \n
           \en Implementation of DECLARE_PERSISTENT_CLASS declaration.
    Describes the necessary operations for a stream class.
    It is set into any .cpp file.
    Class must be inherited from TapeBase.
    Function Read of reading and function Write of writing should be implemented. \n \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_CLASS( AppID, Class )   \
  IMP_PERSISTENT_REGISTRATION( AppID, Class ); \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class );    \
  IMP_CLASS_DESC_FUNC( AppID, Class )

/** \brief \ru Аналог макроса IMP_PERSISTENT_CLASS
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков.
           \en Analog of IMP_PERSISTENT_CLASS macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads. \~
  \details \ru Аналог макроса IMP_PERSISTENT_CLASS
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков
           (включается переменной __OVERLOAD_MEMORY_ALLOCATE_FREE_).
           \en Analog of IMP_PERSISTENT_CLASS macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads
           (enabled by defining __OVERLOAD_MEMORY_ALLOCATE_FREE_). \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_CLASS_NEW_DEL( AppID, Class ) \
  IMP_PERSISTENT_CLASS( AppID, Class );              \
  IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class );


//----------------------------------------------------------------------------------------
/// \ru Конструирование нового экземпляра класса для класса без записи. \en Construction of a new instance of the class for a class without writing. \~ \ingroup Base_Tools_IO
// ---
#define IMP_PERSISTENT_RO_REGISTRATION( AppID, Class )                    \
  TapeBase * CALL_DECLARATION make ## _ ## Class () {                     \
    return new Class(tapeInit);                                           \
  }                                                                       \
  void * CALL_DECLARATION cast ## _ ## Class ( const TapeBase * obj ) {   \
    return dynamic_cast<Class*>(const_cast<TapeBase *>(obj) );            \
  }                                                                       \
  TapeClass  r ## Class(                                                  \
    typeid(Class).name(),                                                 \
    AppID,                                                                \
    (BUILD_FUNC) make ## _ ## Class,                                      \
    (CAST_FUNC ) cast ## _ ## Class,                                      \
    (READ_FUNC ) Class::Read,                                             \
    (WRITE_FUNC) 0                                                        \
  )


//----------------------------------------------------------------------------------------
// \ru Описывает необходимые действия для поточного класса без записи \en Describes the necessary operations for a stream class without writing. 
// \ru Устанавливается в любой .cpp файл \en It is set to any .cpp file 
// \ru Примечание : \en Note: 
// \ru 1. Class должен наследовать от TapeBase \en 1. Class must be inherited from TapeBase 
// \ru 2. Требуется наличие функций \en 2. There must be the following functions 
//      - void Class:Read( reader& in, Class* obj );
//      - void Class::Write( writer& out, const Class* obj );
// \ru где Class - имя класса \en where Class is a class name 
// ---
#define IMP_PERSISTENT_RO_CLASS( AppID, Class )      \
  IMP_PERSISTENT_RO_REGISTRATION( AppID, Class );    \
  IMP_PERSISTENT_NEW_DELETE_CLASS( Class )

//------------------------------------------------------------------------------
/** \brief \ru Аналог макроса IMP_PERSISTENT_RO_CLASS
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков.
           \en Analog of IMP_PERSISTENT_RO_CLASS macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads. \~
  \details \ru Аналог макроса IMP_PERSISTENT_RO_CLASS
           с возможностью перегрузки операторов new/delete,
           обеспечивающий последовательное обращение к функциям
           выделения/освобождения памяти из разных потоков
           (включается переменной __OVERLOAD_MEMORY_ALLOCATE_FREE_).
           \en Analog of IMP_PERSISTENT_RO_CLASS macro
           with support of new/delete operators overloading which provides
           sequential access to the allocation/deallocation functions
           from different threads
           (enabled by defining __OVERLOAD_MEMORY_ALLOCATE_FREE_). \~
  \ingroup Base_Tools_IO
*/
// ---
#define IMP_PERSISTENT_RO_CLASS_NEW_DEL( AppID, Class ) \
  IMP_PERSISTENT_RO_CLASS( AppID, Class );              \
  IMP_PERSISTENT_NEW_DELETE_CLASS_EX( Class )


/**
  \brief  \ru Объявление операторов чтения и записи указателей и ссылок.
          \en Declaration of operators of reading and writing of pointers and references. \~
  \deprecated
\ingroup Base_Tools_IO
*/
// ---
#define DECLARE_PERSISTENT_OPS_B( Class )                                                     \
  friend inline reader & CALL_DECLARATION operator >> ( reader & in, Class & ref );         \
  friend inline reader & CALL_DECLARATION operator >> ( reader & in, Class *& ptr );        \
  friend inline reader & CALL_DECLARATION operator >> ( reader & in, const Class *& ptr );  \
  friend inline writer & CALL_DECLARATION operator << ( writer & out, const Class & ref );  \
  friend inline writer & CALL_DECLARATION operator << ( writer & out, const Class * ptr );  \
  friend inline writer & CALL_DECLARATION operator << ( writer & out, Class & ref );        \
  friend inline writer & CALL_DECLARATION operator << ( writer& out, Class * ptr );

//----------------------------------------------------------------------------------------
/**
  \brief  \ru Операторы чтения указателей и ссылок для класса без записи.
          \en Operators of reading pointers and references for a class without writing. \~
  \deprecated
  \ingroup Base_Tools_IO
*/
// ---
#define DECLARE_PERSISTENT_RO_OPS( Class )                                            \
  friend inline reader & CALL_DECLARATION operator >> ( reader & in, Class & ref ) {  \
    in.readObject( dynamic_cast<TapeBase *>(&ref) );                                  \
    return in;                                                                        \
  }                                                                                   \
  friend inline reader & CALL_DECLARATION operator >> ( reader & in, Class *& ptr ) { \
    ptr = dynamic_cast<Class *>( in.readObjectPointer() );                            \
    return in;                                                                        \
  }

//----------------------------------------------------------------------------------------
/// \ru Функции чтения для класса без записи. \en Function of reading for class without writing. \~ \ingroup Base_Tools_IO
/// \deprecated
// ---
#define DECLARE_PERSISTENT_RO_FUNCS( Class )      \
  public:                                         \
    static void Read( reader & in, Class * obj )


//----------------------------------------------------------------------------------------
/// \ru Конструктор для потокового класса. \en Constructor for a stream class. \~ \ingroup Base_Tools_IO
// ---
#define IMP_PERSISTENT_CTOR( Class )  \
  Class::Class( TapeInit ) {}

//----------------------------------------------------------------------------------------
/// \ru Конструктор для класса с одной потоковой базой. \en Constructor for a class with one stream base. \~ \ingroup Base_Tools_IO
// ---
#define IMP_PERSISTENT_CTOR1( Class, Base )       \
  Class::Class( TapeInit ) : Base( tapeInit ) {}

//----------------------------------------------------------------------------------------
/// \ru Конструктор для класса с двумя потоковыми базами. \en Constructor for a class with two stream bases. \~ \ingroup Base_Tools_IO
// ---
#define IMP_PERSISTENT_CTOR2( Class, Base1, Base2 )                   \
  Class::Class( TapeInit ) : Base1( tapeInit ), Base2( tapeInit ) {}


#endif // __IO_TAPE_H

