// Created on: 2002-10-30
// Created by: Michael SAZONOV
// Copyright (c) 2002-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.

#include <BinObjMgt_Persistent.hxx>
#include <BinObjMgt_Position.hxx>
#include <Standard_GUID.hxx>
#include <TDF_Data.hxx>
#include <TDF_Label.hxx>
#include <TDF_Tool.hxx>
#include <FSD_BinaryFile.hxx>

#define BP_INTSIZE ((int)sizeof(int))
#define BP_EXTCHARSIZE ((int)sizeof(char16_t))
#define BP_REALSIZE ((int)sizeof(double))
#define BP_SHORTREALSIZE ((int)sizeof(float))
#define BP_UUIDSIZE ((int)sizeof(BinObjMgt_UUID))

// We define a GUID structure that is different from Standard_UUID because
// the latter contains a 'unsigned long' member that has variables size
// (4 or 8 bits) thus making the persistent files non-portable.
// This structure below ensures the portability.
typedef struct
{
  unsigned int   Data1;    // 4-bytes long on all OS
  unsigned short Data2;    // 2-bytes long on all OS
  unsigned short Data3;    // 2-bytes long on all OS
  unsigned char  Data4[8]; // 8-bytes long on all OS
} BinObjMgt_UUID;

//=================================================================================================

BinObjMgt_Persistent::BinObjMgt_Persistent()
    : myIndex(1),
      myOffset(BP_HEADSIZE),
      mySize(BP_HEADSIZE),
      myIsError(false),
      myOStream(nullptr),
      myIStream(nullptr),
      myDirectWritingIsEnabled(false)
{
  Init();
}

//=================================================================================================

void BinObjMgt_Persistent::Init()
{
  if (myData.IsEmpty())
  {
    void* aPiece = Standard::Allocate(BP_PIECESIZE);
    myData.Append(aPiece);
  }
  int* aData               = (int*)myData(1);
  aData[0]                 = 0; // Type Id
  aData[1]                 = 0; // Object Id
  aData[2]                 = 0; // Data length
  myIndex                  = 1;
  myOffset                 = BP_HEADSIZE;
  mySize                   = BP_HEADSIZE;
  myIsError                = false;
  myDirectWritingIsEnabled = false;
}

//=======================================================================
// function : Write
// purpose  : Stores <me> to the stream.
//           inline Standard_OStream& operator<< (Standard_OStream&,
//           const BinObjMgt_Persistent&) is also available
//=======================================================================

Standard_OStream& BinObjMgt_Persistent::Write(Standard_OStream& theOS, const bool theDirectStream)
{
  if (myDirectWritingIsEnabled)
  { // if direct writing was enabled, everything is already written, just pass this stage
    myDirectWritingIsEnabled = false;
    return theOS;
  }
  int  nbWritten = 0;
  int* aData     = (int*)myData(1);
  // update data length
  aData[2] = mySize - BP_HEADSIZE;
  if (theDirectStream)
  {
    aData[1] = -aData[1];
  }
#if DO_INVERSE
  aData[0] = InverseInt(aData[0]);
  aData[1] = InverseInt(aData[1]);
  aData[2] = InverseInt(aData[2]);
#endif
  for (int i = 1; theOS && nbWritten < mySize && i <= myData.Length(); i++)
  {
    int nbToWrite = std::min(mySize - nbWritten, BP_PIECESIZE);
    theOS.write((char*)myData(i), nbToWrite);
    nbWritten += nbToWrite;
  }
  myIndex   = 1;
  myOffset  = BP_HEADSIZE;
  mySize    = BP_HEADSIZE;
  myIsError = false;
  return theOS;
}

//=======================================================================
// function : Read
// purpose  : Retrieves <me> from the stream.
//           inline Standard_IStream& operator>> (Standard_IStream&,
//           BinObjMgt_Persistent&) is also available
//=======================================================================

Standard_IStream& BinObjMgt_Persistent::Read(Standard_IStream& theIS)
{
  myIndex   = 1;
  myOffset  = BP_HEADSIZE;
  mySize    = BP_HEADSIZE;
  myIsError = false;

  int* aData = (int*)myData(1);
  aData[0]   = 0; // Type Id
  aData[1]   = 0; // Object Id
  aData[2]   = 0; // Data length

  // read TypeId
  theIS.read((char*)&aData[0], BP_INTSIZE);
#ifdef DO_INVERSE
  aData[0] = InverseInt(aData[0]);
#endif
  if (theIS && aData[0] > 0)
  {
    // read Id and Length
    theIS.read((char*)&aData[1], 2 * BP_INTSIZE);
#ifdef DO_INVERSE
    aData[1] = InverseInt(aData[1]);
    aData[2] = InverseInt(aData[2]);
#endif
    myDirectWritingIsEnabled = aData[1] < 0;
    if (myDirectWritingIsEnabled)
    {
      aData[1] = -aData[1];
    }
    if (theIS && aData[2] > 0)
    {
      mySize += aData[2];
      // read remaining data
      int nbRead = BP_HEADSIZE;
      for (int i = 1; theIS && nbRead < mySize; i++)
      {
        if (i > myData.Length())
        {
          // grow myData dynamically
          void* aPiece = Standard::Allocate(BP_PIECESIZE);
          myData.Append(aPiece);
        }
        int   nbToRead = std::min(mySize - nbRead, BP_PIECESIZE);
        char* ptr      = (char*)myData(i);
        if (i == 1)
        {
          // 1st piece: reduce the number of bytes by header size
          ptr += BP_HEADSIZE;
          if (nbToRead == BP_PIECESIZE)
          {
            nbToRead -= BP_HEADSIZE;
          }
        }
        theIS.read(ptr, nbToRead);
        nbRead += nbToRead;
      }
    }
    else
    {
      aData[2] = 0;
    }
  }
  return theIS;
}

//=================================================================================================

void BinObjMgt_Persistent::Destroy()
{
  for (int i = 1; i <= myData.Length(); i++)
  {
    Standard::Free(myData(i));
  }
  myData.Clear();
  myIndex = myOffset = mySize = 0;
}

//=======================================================================
// function : incrementData
// purpose  : Allocates theNbPieces more pieces
//=======================================================================

void BinObjMgt_Persistent::incrementData(const int theNbPieces)
{
  for (int i = 1; i <= theNbPieces; i++)
  {
    void* aPiece = Standard::Allocate(BP_PIECESIZE);
    myData.Append(aPiece);
  }
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutCharacter(const char theValue)
{
  alignOffset(1);
  prepareForPut(1);
  char* aData = (char*)myData(myIndex) + myOffset;
  *aData      = theValue;
  myOffset++;
  return *this;
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutByte(const uint8_t theValue)
{
  alignOffset(1);
  prepareForPut(1);
  uint8_t* aData = (uint8_t*)myData(myIndex) + myOffset;
  *aData         = theValue;
  myOffset++;
  return *this;
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutExtCharacter(const char16_t theValue)
{
  alignOffset(BP_EXTCHARSIZE, true);
  prepareForPut(BP_EXTCHARSIZE);
  char16_t* aData = (char16_t*)((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  *aData = InverseExtChar(theValue);
#else
  *aData = theValue;
#endif
  myOffset += BP_EXTCHARSIZE;
  return *this;
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutInteger(const int theValue)
{
  alignOffset(BP_INTSIZE, true);
  prepareForPut(BP_INTSIZE);
  int* aData = (int*)((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  *aData = InverseInt(theValue);
#else
  *aData = theValue;
#endif
  myOffset += BP_INTSIZE;
  return *this;
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutReal(const double theValue)
{
  alignOffset(BP_INTSIZE, true);
  int nbPieces = prepareForPut(BP_REALSIZE);
  if (nbPieces > 0)
  {
    // the value intersects a piece boundary => go a long way
#ifdef DO_INVERSE
    int aStartIndex  = myIndex;
    int aStartOffset = myOffset;
#endif
    putArray((void*)&theValue, BP_REALSIZE);
#ifdef DO_INVERSE
    inverseRealData(aStartIndex, aStartOffset, BP_REALSIZE);
#endif
  }
  else
  {
    // the value fits in the current piece => put it quickly
    double* aData = (double*)((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
    *aData = InverseReal(theValue);
#else
    *aData = theValue;
#endif
    myOffset += BP_REALSIZE;
  }
  return *this;
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutShortReal(const float theValue)
{
  alignOffset(BP_INTSIZE, true);
  prepareForPut(BP_SHORTREALSIZE);
  float* aData = (float*)((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  *aData = InverseShortReal(theValue);
#else
  *aData = theValue;
#endif
  myOffset += BP_SHORTREALSIZE;
  return *this;
}

//=======================================================================
// function : PutCString
// purpose  : Offset in output buffer is not aligned
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutCString(const char* const theValue)
{
  alignOffset(1);
  int aSize = (int)(strlen(theValue) + 1);
  prepareForPut(aSize);
  putArray((void*)theValue, aSize);
  return *this;
}

//=======================================================================
// function : PutAsciiString
// purpose  : Offset in output buffer is word-aligned
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutAsciiString(const TCollection_AsciiString& theValue)
{
  alignOffset(BP_INTSIZE, true);
  int aSize = theValue.Length() + 1;
  prepareForPut(aSize);
  putArray((void*)theValue.ToCString(), aSize);
  return *this;
}

//=======================================================================
// function : PutExtendedString
// purpose  : Offset in output buffer is word-aligned
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutExtendedString(
  const TCollection_ExtendedString& theValue)
{
  alignOffset(BP_INTSIZE, true);
  int aSize = (theValue.Length() + 1) * BP_EXTCHARSIZE;
  prepareForPut(aSize);
#ifdef DO_INVERSE
  int aStartIndex  = myIndex;
  int aStartOffset = myOffset;
#endif
  putArray((void*)theValue.ToExtString(), aSize);
#ifdef DO_INVERSE
  inverseExtCharData(aStartIndex, aStartOffset, aSize - BP_EXTCHARSIZE);
#endif
  return *this;
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutLabel(const TDF_Label& theValue)
{
  alignOffset(BP_INTSIZE, true);
  int aLen = (theValue.IsNull() ? 0 : theValue.Depth() + 1);
  prepareForPut((aLen + 1) * BP_INTSIZE);
  int* aData = (int*)((char*)myData(myIndex) + myOffset);
  // store nb of tags
#ifdef DO_INVERSE
  *aData++ = InverseInt(aLen);
#else
  *aData++ = aLen;
#endif
  myOffset += BP_INTSIZE;
  if (!theValue.IsNull())
  {
    NCollection_List<int> aTagList;
    TDF_Tool::TagList(theValue, aTagList);
    NCollection_List<int>::Iterator itTag(aTagList);
    for (; itTag.More(); itTag.Next())
    {
      if (myOffset >= BP_PIECESIZE)
      {
        myOffset = 0;
        myIndex++;
        aData = (int*)((char*)myData(myIndex) + myOffset);
      }
#ifdef DO_INVERSE
      *aData++ = InverseInt(itTag.Value());
#else
      *aData++ = itTag.Value();
#endif
      myOffset += BP_INTSIZE;
    }
  }
  return *this;
}

//=================================================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutGUID(const Standard_GUID& theValue)
{
  alignOffset(BP_INTSIZE, true);
  prepareForPut(BP_UUIDSIZE);
  const Standard_UUID aStandardUUID = theValue.ToUUID();
  BinObjMgt_UUID      anUUID;
  anUUID.Data1    = (unsigned int)aStandardUUID.Data1;
  anUUID.Data2    = (unsigned short)aStandardUUID.Data2;
  anUUID.Data3    = (unsigned short)aStandardUUID.Data3;
  anUUID.Data4[0] = aStandardUUID.Data4[0];
  anUUID.Data4[1] = aStandardUUID.Data4[1];
  anUUID.Data4[2] = aStandardUUID.Data4[2];
  anUUID.Data4[3] = aStandardUUID.Data4[3];
  anUUID.Data4[4] = aStandardUUID.Data4[4];
  anUUID.Data4[5] = aStandardUUID.Data4[5];
  anUUID.Data4[6] = aStandardUUID.Data4[6];
  anUUID.Data4[7] = aStandardUUID.Data4[7];
#ifdef DO_INVERSE
  anUUID.Data1 = (unsigned int)InverseInt(anUUID.Data1);
  anUUID.Data2 = (unsigned short)InverseExtChar(anUUID.Data2);
  anUUID.Data3 = (unsigned short)InverseExtChar(anUUID.Data3);
#endif
  putArray(&anUUID, BP_UUIDSIZE);
  return *this;
}

//=======================================================================
// function : PutCharArray
// purpose  : Put C array of char, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutCharArray(const BinObjMgt_PChar theArray,
                                                         const int             theLength)
{
  alignOffset(1);
  prepareForPut(theLength);
  putArray(theArray, theLength);
  return *this;
}

//=======================================================================
// function : PutByteArray
// purpose  : Put C array of byte, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutByteArray(const BinObjMgt_PByte theArray,
                                                         const int             theLength)
{
  alignOffset(1);
  prepareForPut(theLength);
  putArray(theArray, theLength);
  return *this;
}

//=======================================================================
// function : PutExtCharArray
// purpose  : Put C array of ExtCharacter, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutExtCharArray(const BinObjMgt_PExtChar theArray,
                                                            const int                theLength)
{
  alignOffset(BP_EXTCHARSIZE, true);
  int aSize = theLength * BP_EXTCHARSIZE;
  prepareForPut(aSize);
#ifdef DO_INVERSE
  int aStartIndex  = myIndex;
  int aStartOffset = myOffset;
#endif
  putArray(theArray, aSize);
#ifdef DO_INVERSE
  inverseExtCharData(aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=======================================================================
// function : PutIntArray
// purpose  : Put C array of int, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutIntArray(const BinObjMgt_PInteger theArray,
                                                        const int                theLength)
{
  alignOffset(BP_INTSIZE, true);
  int aSize = theLength * BP_INTSIZE;
  prepareForPut(aSize);
#ifdef DO_INVERSE
  int aStartIndex  = myIndex;
  int aStartOffset = myOffset;
#endif
  putArray(theArray, aSize);
#ifdef DO_INVERSE
  inverseIntData(aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=======================================================================
// function : PutRealArray
// purpose  : Put C array of double, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutRealArray(const BinObjMgt_PReal theArray,
                                                         const int             theLength)
{
  alignOffset(BP_INTSIZE, true);
  int aSize = theLength * BP_REALSIZE;
  prepareForPut(aSize);
#ifdef DO_INVERSE
  int aStartIndex  = myIndex;
  int aStartOffset = myOffset;
#endif
  putArray(theArray, aSize);
#ifdef DO_INVERSE
  inverseRealData(aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=======================================================================
// function : PutShortRealArray
// purpose  : Put C array of float, theLength is the number of elements
//=======================================================================

BinObjMgt_Persistent& BinObjMgt_Persistent::PutShortRealArray(const BinObjMgt_PShortReal theArray,
                                                              const int                  theLength)
{
  alignOffset(BP_INTSIZE, true);
  int aSize = theLength * BP_SHORTREALSIZE;
  prepareForPut(aSize);
#ifdef DO_INVERSE
  int aStartIndex  = myIndex;
  int aStartOffset = myOffset;
#endif
  putArray(theArray, aSize);
#ifdef DO_INVERSE
  inverseShortRealData(aStartIndex, aStartOffset, aSize);
#endif
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetCharacter(char& theValue) const
{
  alignOffset(1);
  if (noMoreData(1))
  {
    return *this;
  }
  char* aData = (char*)myData(myIndex) + myOffset;
  theValue    = *aData;
  ((BinObjMgt_Persistent*)this)->myOffset++;
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetByte(uint8_t& theValue) const
{
  alignOffset(1);
  if (noMoreData(1))
  {
    return *this;
  }
  uint8_t* aData = (uint8_t*)myData(myIndex) + myOffset;
  theValue       = *aData;
  ((BinObjMgt_Persistent*)this)->myOffset++;
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetExtCharacter(char16_t& theValue) const
{
  alignOffset(BP_EXTCHARSIZE);
  if (noMoreData(BP_EXTCHARSIZE))
  {
    return *this;
  }
  char16_t* aData = (char16_t*)((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  theValue = InverseExtChar(*aData);
#else
  theValue = *aData;
#endif
  ((BinObjMgt_Persistent*)this)->myOffset += BP_EXTCHARSIZE;
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetInteger(int& theValue) const
{
  alignOffset(BP_INTSIZE);
  if (noMoreData(BP_INTSIZE))
  {
    return *this;
  }
  int* aData = (int*)((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  theValue = InverseInt(*aData);
#else
  theValue = *aData;
#endif
  ((BinObjMgt_Persistent*)this)->myOffset += BP_INTSIZE;
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetReal(double& theValue) const
{
  alignOffset(BP_INTSIZE);
  if (noMoreData(BP_REALSIZE))
  {
    return *this;
  }
  int nbPieces = (myOffset + BP_REALSIZE - 1) / BP_PIECESIZE;
  if (nbPieces > 0)
  {
    // the value intersects a piece boundary => go a long way
    getArray((void*)&theValue, BP_REALSIZE);
  }
  else
  {
    // the value fits in the current piece => get it quickly
    double* aData = (double*)((char*)myData(myIndex) + myOffset);
    theValue      = *aData;
    ((BinObjMgt_Persistent*)this)->myOffset += BP_REALSIZE;
  }
#ifdef DO_INVERSE
  theValue = InverseReal(theValue);
#endif
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetShortReal(float& theValue) const
{
  alignOffset(BP_INTSIZE);
  if (noMoreData(BP_SHORTREALSIZE))
  {
    return *this;
  }
  float* aData = (float*)((char*)myData(myIndex) + myOffset);
#ifdef DO_INVERSE
  theValue = InverseShortReal(*aData);
#else
  theValue = *aData;
#endif
  ((BinObjMgt_Persistent*)this)->myOffset += BP_SHORTREALSIZE;
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetAsciiString(
  TCollection_AsciiString& theValue) const
{
  alignOffset(BP_INTSIZE);
  int                   aStartIndex  = myIndex;
  int                   aStartOffset = myOffset;
  BinObjMgt_Persistent* me           = (BinObjMgt_Persistent*)this;
  char*                 aData        = (char*)myData(myIndex) + myOffset;

  // count the string length
  while (!noMoreData(1) && *aData++)
  {
    me->myOffset++;
    if (myOffset >= BP_PIECESIZE)
    {
      me->myOffset = 0;
      aData        = (char*)myData(++me->myIndex);
    }
  }
  if (IsError())
  {
    me->myIndex  = aStartIndex;
    me->myOffset = aStartOffset;
    return *this;
  }
  me->myOffset++; // count the end null char

  if (myIndex == aStartIndex)
  {
    // all string is in one piece => simply copy
    theValue = aData - myOffset + aStartOffset;
  }
  else
  {
    // work through buffer string
    int   aSize   = (myIndex - aStartIndex) * BP_PIECESIZE + myOffset - aStartOffset;
    void* aString = Standard::Allocate(aSize);
    me->myIndex   = aStartIndex;
    me->myOffset  = aStartOffset;
    getArray(aString, aSize);
    theValue = (char*)aString;
    Standard::Free(aString);
  }

  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetExtendedString(
  TCollection_ExtendedString& theValue) const
{
  alignOffset(BP_INTSIZE);
  int                   aStartIndex  = myIndex;
  int                   aStartOffset = myOffset;
  BinObjMgt_Persistent* me           = (BinObjMgt_Persistent*)this;
  char16_t*             aData        = (char16_t*)((char*)myData(myIndex) + myOffset);

  // count the string length
  while (!noMoreData(1) && *aData++)
  {
    me->myOffset += BP_EXTCHARSIZE;
    if (myOffset >= BP_PIECESIZE)
    {
      me->myOffset = 0;
      aData        = (char16_t*)myData(++me->myIndex);
    }
  }
  if (IsError())
  {
    me->myIndex  = aStartIndex;
    me->myOffset = aStartOffset;
    return *this;
  }
  me->myOffset += BP_EXTCHARSIZE; // count the end null char

  if (myIndex == aStartIndex)
  {
    // all string is in one piece => simply copy
    theValue = aData - (myOffset - aStartOffset) / BP_EXTCHARSIZE;
  }
  else
  {
    // work through buffer string
    int   aSize   = (myIndex - aStartIndex) * BP_PIECESIZE + (myOffset - aStartOffset);
    void* aString = Standard::Allocate(aSize);
    me->myIndex   = aStartIndex;
    me->myOffset  = aStartOffset;
    getArray(aString, aSize);
    theValue = (char16_t*)aString;
    Standard::Free(aString);
  }
#ifdef DO_INVERSE
  Standard_PExtCharacter aString = (Standard_PExtCharacter)theValue.ToExtString();
  for (int i = 0; i < theValue.Length(); i++)
    aString[i] = InverseExtChar(aString[i]);
#endif

  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetLabel(const occ::handle<TDF_Data>& theDS,
                                                           TDF_Label& theValue) const
{
  theValue.Nullify();
  alignOffset(BP_INTSIZE);
  if (noMoreData(BP_INTSIZE))
  {
    return *this;
  }
  BinObjMgt_Persistent* me = (BinObjMgt_Persistent*)this;
  // retrieve nb of tags
  int* aData = (int*)((char*)myData(myIndex) + myOffset);
  int  aLen  = *aData++;
#ifdef DO_INVERSE
  aLen = InverseInt(aLen);
#endif
  me->myOffset += BP_INTSIZE;
  if (noMoreData(aLen * BP_INTSIZE))
  {
    return *this;
  }

  if (aLen > 0)
  {
    // retrieve tag list
    NCollection_List<int> aTagList;
    while (aLen > 0)
    {
      if (myOffset >= BP_PIECESIZE)
      {
        me->myOffset = 0;
        me->myIndex++;
        aData = (int*)((char*)myData(myIndex) + myOffset);
      }
#ifdef DO_INVERSE
      aTagList.Append(InverseInt(*aData++));
#else
      aTagList.Append(*aData++);
#endif
      me->myOffset += BP_INTSIZE;
      aLen--;
    }
    // find label by entry
    TDF_Tool::Label(theDS, aTagList, theValue, true);
  }
  return *this;
}

//=================================================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetGUID(Standard_GUID& theValue) const
{
  alignOffset(BP_INTSIZE);
  if (noMoreData(BP_UUIDSIZE))
  {
    return *this;
  }
  BinObjMgt_UUID anUUID;
  getArray(&anUUID, BP_UUIDSIZE);
#ifdef DO_INVERSE
  anUUID.Data1 = (unsigned int)InverseInt(anUUID.Data1);
  anUUID.Data2 = (unsigned short)InverseExtChar(anUUID.Data2);
  anUUID.Data3 = (unsigned short)InverseExtChar(anUUID.Data3);
#endif
  theValue = Standard_GUID(anUUID.Data1,
                           anUUID.Data2,
                           anUUID.Data3,
                           ((anUUID.Data4[0] << 8) | (anUUID.Data4[1])),
                           anUUID.Data4[2],
                           anUUID.Data4[3],
                           anUUID.Data4[4],
                           anUUID.Data4[5],
                           anUUID.Data4[6],
                           anUUID.Data4[7]);
  return *this;
}

//=======================================================================
// function : GetCharArray
// purpose  : Get C array of char, theLength is the number of elements;
//           theArray must point to a
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetCharArray(const BinObjMgt_PChar theArray,
                                                               const int theLength) const
{
  alignOffset(1);
  if (noMoreData(theLength))
  {
    return *this;
  }
  getArray(theArray, theLength);
  return *this;
}

//=======================================================================
// function : GetByteArray
// purpose  : Get C array of unsigned chars, theLength is the number of elements;
//           theArray must point to a
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetByteArray(const BinObjMgt_PByte theArray,
                                                               const int theLength) const
{
  alignOffset(1);
  if (noMoreData(theLength))
  {
    return *this;
  }
  getArray(theArray, theLength);
  return *this;
}

//=======================================================================
// function : GetExtCharArray
// purpose  : Get C array of ExtCharacter, theLength is the number of elements;
//           theArray must point to a
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetExtCharArray(const BinObjMgt_PExtChar theArray,
                                                                  const int theLength) const
{
  alignOffset(BP_EXTCHARSIZE, true);
  int aSize = theLength * BP_EXTCHARSIZE;
  if (noMoreData(aSize))
  {
    return *this;
  }
  getArray(theArray, aSize);
#ifdef DO_INVERSE
  for (int i = 0; i < theLength; i++)
    theArray[i] = InverseExtChar(theArray[i]);
#endif
  return *this;
}

//=======================================================================
// function : GetIntArray
// purpose  : Get C array of int, theLength is the number of elements;
//           theArray must point to a
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetIntArray(const BinObjMgt_PInteger theArray,
                                                              const int theLength) const
{
  alignOffset(BP_INTSIZE, true);
  int aSize = theLength * BP_INTSIZE;
  if (noMoreData(aSize))
  {
    return *this;
  }
  getArray(theArray, aSize);
#ifdef DO_INVERSE
  for (int i = 0; i < theLength; i++)
    theArray[i] = InverseInt(theArray[i]);
#endif
  return *this;
}

//=======================================================================
// function : GetRealArray
// purpose  : Get C array of double, theLength is the number of elements;
//           theArray must point to a
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetRealArray(const BinObjMgt_PReal theArray,
                                                               const int theLength) const
{
  alignOffset(BP_INTSIZE, true);
  int aSize = theLength * BP_REALSIZE;
  if (noMoreData(aSize))
  {
    return *this;
  }
  getArray(theArray, aSize);
#ifdef DO_INVERSE
  for (int i = 0; i < theLength; i++)
    theArray[i] = InverseReal(theArray[i]);
#endif
  return *this;
}

//=======================================================================
// function : GetShortRealArray
// purpose  : Get C array of float, theLength is the number of elements;
//           theArray must point to a
//           space enough to place theLength elements
//=======================================================================

const BinObjMgt_Persistent& BinObjMgt_Persistent::GetShortRealArray(
  const BinObjMgt_PShortReal theArray,
  const int                  theLength) const
{
  alignOffset(BP_INTSIZE, true);
  int aSize = theLength * BP_SHORTREALSIZE;
  if (noMoreData(aSize))
  {
    return *this;
  }
  getArray(theArray, aSize);
#ifdef DO_INVERSE
  for (int i = 0; i < theLength; i++)
    theArray[i] = InverseShortReal(theArray[i]);
#endif
  return *this;
}

//=======================================================================
// function : putArray
// purpose  : Puts theSize bytes from theArray
//=======================================================================

void BinObjMgt_Persistent::putArray(void* const theArray, const int theSize)
{
  char* aPtr = (char*)theArray;
  int   aLen = theSize;
  while (aLen > 0)
  {
    if (myOffset >= BP_PIECESIZE)
    {
      myIndex++;
      myOffset = 0;
    }
    int   aLenInPiece = std::min(aLen, BP_PIECESIZE - myOffset);
    char* aData       = (char*)myData(myIndex) + myOffset;
    memcpy(aData, aPtr, aLenInPiece);
    aLen -= aLenInPiece;
    aPtr += aLenInPiece;
    myOffset += aLenInPiece;
  }
}

//=======================================================================
// function : getArray
// purpose  : Gets theLength bytes into theArray
//=======================================================================

void BinObjMgt_Persistent::getArray(void* const theArray, const int theSize) const
{
  char*                 aPtr = (char*)theArray;
  int                   aLen = theSize;
  BinObjMgt_Persistent* me   = (BinObjMgt_Persistent*)this;
  while (aLen > 0)
  {
    if (myOffset >= BP_PIECESIZE)
    {
      me->myIndex++;
      me->myOffset = 0;
    }
    int   aLenInPiece = std::min(aLen, BP_PIECESIZE - myOffset);
    char* aData       = (char*)myData(myIndex) + myOffset;
    memcpy(aPtr, aData, aLenInPiece);
    aLen -= aLenInPiece;
    aPtr += aLenInPiece;
    me->myOffset += aLenInPiece;
  }
}

//=======================================================================
// function : inverseExtCharData
// purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseExtCharData(const int theIndex,
                                              const int theOffset,
                                              const int theSize)
{
  int anIndex  = theIndex;
  int anOffset = theOffset;
  int aLen     = theSize;
  while (aLen > 0)
  {
    int       aLenInPiece = std::min(aLen, BP_PIECESIZE - anOffset);
    char16_t* aData       = (char16_t*)((char*)myData(anIndex) + anOffset);
    for (int i = 0; i < aLenInPiece / BP_EXTCHARSIZE; i++)
    {
      aData[i] = FSD_BinaryFile::InverseExtChar(aData[i]);
    }
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE)
    {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
// function : inverseIntData
// purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseIntData(const int theIndex,
                                          const int theOffset,
                                          const int theSize)
{
  int anIndex  = theIndex;
  int anOffset = theOffset;
  int aLen     = theSize;
  while (aLen > 0)
  {
    int  aLenInPiece = std::min(aLen, BP_PIECESIZE - anOffset);
    int* aData       = (int*)((char*)myData(anIndex) + anOffset);
    for (int i = 0; i < aLenInPiece / BP_INTSIZE; i++)
    {
      aData[i] = FSD_BinaryFile::InverseInt(aData[i]);
    }
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE)
    {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
// function : inverseRealData
// purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseRealData(const int theIndex,
                                           const int theOffset,
                                           const int theSize)
{
  int anIndex  = theIndex;
  int anOffset = theOffset;
  int aLen     = theSize;

  union {
    double* aRealData;
    int*    aIntData;
  } aWrapUnion;

  void* aPrevPtr = nullptr;
  while (aLen > 0)
  {
    int aLenInPiece = std::min(aLen, BP_PIECESIZE - anOffset);

    aWrapUnion.aRealData = (double*)((char*)myData(anIndex) + anOffset);

    if (aPrevPtr)
    {
      int aTmp;
      aTmp                 = FSD_BinaryFile::InverseInt(*(int*)aPrevPtr);
      *(int*)aPrevPtr      = FSD_BinaryFile::InverseInt(*aWrapUnion.aIntData);
      *aWrapUnion.aIntData = aTmp;
      aWrapUnion.aIntData++;
      aPrevPtr = nullptr;
    }
    for (int i = 0; i < aLenInPiece / BP_REALSIZE; i++)
    {
      aWrapUnion.aRealData[i] = FSD_BinaryFile::InverseReal(aWrapUnion.aRealData[i]);
    }
    if (aLenInPiece % BP_REALSIZE)
    {
      aPrevPtr = &aWrapUnion.aRealData[aLenInPiece / BP_REALSIZE];
    }
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE)
    {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
// function : inverseShortRealData
// purpose  : Inverses bytes in the data addressed by the given values
//=======================================================================

void BinObjMgt_Persistent::inverseShortRealData(const int theIndex,
                                                const int theOffset,
                                                const int theSize)
{
  int anIndex  = theIndex;
  int anOffset = theOffset;
  int aLen     = theSize;
  while (aLen > 0)
  {
    int    aLenInPiece = std::min(aLen, BP_PIECESIZE - anOffset);
    float* aData       = (float*)((char*)myData(anIndex) + anOffset);
    for (int i = 0; i < aLenInPiece / BP_INTSIZE; i++)
    {
      aData[i] = FSD_BinaryFile::InverseShortReal(aData[i]);
    }
    aLen -= aLenInPiece;
    anOffset += aLenInPiece;
    if (anOffset >= BP_PIECESIZE)
    {
      anIndex++;
      anOffset = 0;
    }
  }
}

//=======================================================================
// function : GetOStream
// purpose  : Gets the stream for and enables direct writing
//=======================================================================

Standard_OStream* BinObjMgt_Persistent::GetOStream()
{
  Write(*myOStream, true); // finishes already stored data save
  myStreamStart = new BinObjMgt_Position(*myOStream);
  myStreamStart->WriteSize(*myOStream, true);
  myDirectWritingIsEnabled = true;
  return myOStream;
}

//=======================================================================
// function : GetOStream
// purpose  : Gets the stream for and enables direct writing
//=======================================================================
Standard_IStream* BinObjMgt_Persistent::GetIStream()
{
  // skip the stream size first
  myIStream->seekg(sizeof(uint64_t), std::ios_base::cur);
  return myIStream;
}
