/*
 * $Id$
 */

/*
 * Harbour Project source code:
 *    item serialization code
 *
 * Copyright 2007 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
 * www - http://www.harbour-project.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#include "hbapi.h"
#include "hbapiitm.h"
#include "hbapicls.h"


/*
TODO: 1. extended number format with size and decimals
      2. extended hash format with default value and hash flags
*/

/*
UCHAR [ 1 ] - item type
   0. NIL               0
   1. TRUE              0
   2. FALSE             0
   3. ZERO              0
   4. INT8              1
   5. INT16             2
   6. INT24             3
   7. INT32             4
   8. INT64             8
   9. DOUBLE IEE754 LE  8
  10. DATE              3
  11. STRING8           1+n
  12. STRING16          2+n
  13. STRING32          4+n
  14. ARRAY8            1+n
  15. ARRAY16           2+n
  16. ARRAY32           4+n
  17. ARRAYREF8         1+n
  18. ARRAYREF16        2+n
  19. ARRAYREF32        4+n
  20. HASH8             1+n
  21. HASH16            2+n
  22. HASH32            4+n
  23. HASHREF8          1+n
  24. HASHREF16         2+n
  25. HASHREF32         4+n
  26. SYMBOL            1+n
  27. CYCLIC REFERENCE  4
  28. OBJECT MARKER     n+1+m+1
  29. STRNUL            0
  30. STRPAD8           1+1+n
  31. STRPAD16          2+2+n
  32. STRPAD32          4+4+n
*/

#define HB_SERIAL_NIL         0
#define HB_SERIAL_TRUE        1
#define HB_SERIAL_FALSE       2
#define HB_SERIAL_ZERO        3
#define HB_SERIAL_INT8        4
#define HB_SERIAL_INT16       5
#define HB_SERIAL_INT24       6
#define HB_SERIAL_INT32       7
#define HB_SERIAL_INT64       8
#define HB_SERIAL_DOUBLE      9
#define HB_SERIAL_DATE       10
#define HB_SERIAL_STRING8    11
#define HB_SERIAL_STRING16   12
#define HB_SERIAL_STRING32   13
#define HB_SERIAL_ARRAY8     14
#define HB_SERIAL_ARRAY16    15
#define HB_SERIAL_ARRAY32    16
#define HB_SERIAL_ARRAYREF8  17
#define HB_SERIAL_ARRAYREF16 18
#define HB_SERIAL_ARRAYREF32 19
#define HB_SERIAL_HASH8      20
#define HB_SERIAL_HASH16     21
#define HB_SERIAL_HASH32     22
#define HB_SERIAL_HASHREF8   23
#define HB_SERIAL_HASHREF16  24
#define HB_SERIAL_HASHREF32  25
#define HB_SERIAL_SYMBOL     26
#define HB_SERIAL_REF        27
#define HB_SERIAL_OBJ        28
#define HB_SERIAL_STRNUL     29
#define HB_SERIAL_STRPAD8    30
#define HB_SERIAL_STRPAD16   31
#define HB_SERIAL_STRPAD32   32

#define HB_SERIAL_DUMMYOFFSET ( ( ULONG ) -1 )

typedef struct _HB_CYCLIC_REF
{
   void *   value;
   ULONG    ulOffset;
   BOOL     fRef;
   struct _HB_CYCLIC_REF * pNext;
} HB_CYCLIC_REF, * PHB_CYCLIC_REF;

static ULONG hb_deserializeItem( PHB_ITEM pItem, UCHAR * pBuffer,
                                 ULONG ulOffset, PHB_CYCLIC_REF pRef );

static BOOL hb_itemSerialValueRef( PHB_CYCLIC_REF * pRefPtr, void * value,
                                   ULONG ulOffset )
{
   while( * pRefPtr )
   {
      if( ( * pRefPtr )->value == value )
      {
         ( * pRefPtr )->fRef = TRUE;
         return TRUE;
      }
      pRefPtr = &( * pRefPtr )->pNext;
   }

   * pRefPtr = ( PHB_CYCLIC_REF ) hb_xgrab( sizeof( HB_CYCLIC_REF ) );
   ( * pRefPtr )->value = value;
   ( * pRefPtr )->ulOffset = ulOffset;
   ( * pRefPtr )->fRef = FALSE;
   ( * pRefPtr )->pNext = NULL;

   return FALSE;
}

static void hb_itemSerialUnRefFree( PHB_CYCLIC_REF * pRefPtr )
{
   PHB_CYCLIC_REF pRef;

   while( * pRefPtr )
   {
      pRef = * pRefPtr;
      if( ! pRef->fRef )
      {
         * pRefPtr =pRef->pNext;
         hb_xfree( pRef );
      }
      else
         pRefPtr = &pRef->pNext;
   }
}

static BOOL hb_itemSerialValueOffset( PHB_CYCLIC_REF pRef, void * value,
                                      ULONG ulOffset, ULONG * pulRef )
{
   while( pRef )
   {
      if( pRef->value == value )
      {
         * pulRef = pRef->ulOffset;
         return pRef->ulOffset < ulOffset;
      }
      pRef = pRef->pNext;
   }

   * pulRef = HB_SERIAL_DUMMYOFFSET;
   return FALSE;
}

static BOOL hb_itemSerialOffsetRef( PHB_CYCLIC_REF * pRefPtr, void * value,
                                    ULONG ulOffset )
{
   while( * pRefPtr )
   {
      if( ( * pRefPtr )->ulOffset == ulOffset )
         return TRUE;
      pRefPtr = &( * pRefPtr )->pNext;
   }

   * pRefPtr = ( PHB_CYCLIC_REF ) hb_xgrab( sizeof( HB_CYCLIC_REF ) );
   ( * pRefPtr )->value = value;
   ( * pRefPtr )->ulOffset = ulOffset;
   ( * pRefPtr )->fRef = FALSE;
   ( * pRefPtr )->pNext = NULL;

   return FALSE;
}

static void hb_itemSerialOffsetSet( PHB_CYCLIC_REF pRef, PHB_ITEM pItem,
                                    ULONG ulOffset )
{
   while( pRef )
   {
      if( pRef->ulOffset == ulOffset )
      {
         pRef->value = ( void * ) pItem;
         break;
      }
      pRef = pRef->pNext;
   }
}

static void hb_itemSerialOffsetGet( PHB_CYCLIC_REF pRef, PHB_ITEM pItem,
                                    ULONG ulOffset )
{
   while( pRef )
   {
      if( pRef->ulOffset == ulOffset )
      {
         hb_itemCopy( pItem, ( PHB_ITEM ) pRef->value );
         break;
      }
      pRef = pRef->pNext;
   }
}

static void hb_itemSerialRefFree( PHB_CYCLIC_REF pRef )
{
   while( pRef )
   {
      PHB_CYCLIC_REF pNext = pRef->pNext;
      hb_xfree( pRef );
      pRef = pNext;
   }
}

static ULONG hb_itemSerialSize( PHB_ITEM pItem, PHB_CYCLIC_REF * pRefPtr, ULONG ulOffset )
{
   ULONG ulSize, ulLen, u;
   HB_LONG lVal;
   double d;
   USHORT uiClass;
   const char * szVal;

   switch( hb_itemType( pItem ) )
   {
      case HB_IT_NIL:
      case HB_IT_LOGICAL:
         ulSize = 1;
         break;

      case HB_IT_DATE:
         ulSize = 4;
         break;

      case HB_IT_INTEGER:
      case HB_IT_LONG:
         lVal = hb_itemGetNInt( pItem );
         if( lVal == 0 )
            ulSize = 1;
         else if( HB_LIM_INT8( lVal ) )
            ulSize = 2;
         else if( HB_LIM_INT16( lVal ) )
            ulSize = 3;
         else if( HB_LIM_INT24( lVal ) )
            ulSize = 4;
         else if( HB_LIM_INT32( lVal ) )
            ulSize = 5;
         else
            ulSize = 9;
         break;

      case HB_IT_DOUBLE:
         d = hb_itemGetND( pItem );
         if( d == 0.0 )
            ulSize = 1;
         else
            ulSize = 9;
         break;

      case HB_IT_SYMBOL:
         ulSize = 2 + strlen( hb_itemGetSymbol( pItem )->szName );
         break;

      case HB_IT_STRING:
      case HB_IT_MEMO:
         szVal = hb_itemGetCPtr( pItem );
         ulLen = hb_itemGetCLen( pItem );
         if( ulLen == 0 )
            ulSize = 1;
         else
         {
            u = ulLen;
            while( u && szVal[ u - 1 ] == ' ' )
               --u;
            u = ulLen - u;
            if( ulLen <= 255 )
               ulSize = u > 1 ? ulLen - u + 3: ulLen + 2;
            else if( ulLen <= UINT16_MAX )
               ulSize = u > 2 ? ulLen - u + 5: ulLen + 3;
            else
               ulSize = u > 4 ? ulLen - u + 9: ulLen + 5;
         }
         break;

      case HB_IT_ARRAY:
         ulSize = 0;
         uiClass = hb_objGetClass( pItem );
         if( uiClass )
         {
            const char * szClass = hb_clsName( uiClass ),
                       * szFunc = hb_clsFuncName( uiClass );
            if( szClass && szFunc )
               ulSize += strlen( szClass ) + strlen( szFunc ) + 3;
         }
         if( hb_itemSerialValueRef( pRefPtr, hb_arrayId( pItem ), ulOffset + ulSize ) )
         {
            ulSize = 5;
         }
         else
         {
            ulLen = hb_arrayLen( pItem );
            if( ulLen <= 255 )
               ulSize += 2;
            else if( ulLen <= UINT16_MAX )
               ulSize += 3;
            else
               ulSize += 5;
            for( u = 1; u <= ulLen; u++ )
               ulSize += hb_itemSerialSize( hb_arrayGetItemPtr( pItem, u ),
                                            pRefPtr, ulOffset + ulSize );
         }
         break;

      case HB_IT_HASH:
         if( hb_itemSerialValueRef( pRefPtr, hb_hashId( pItem ), ulOffset ) )
         {
            ulSize = 5;
         }
         else
         {
            ulLen = hb_hashLen( pItem );
            if( ulLen <= 255 )
               ulSize = 2;
            else if( ulLen <= UINT16_MAX )
               ulSize = 3;
            else
               ulSize = 5;
            for( u = 1; u <= ulLen; u++ )
            {
               ulSize += hb_itemSerialSize( hb_hashGetKeyAt( pItem, u ),
                                            pRefPtr, ulOffset + ulSize );
               ulSize += hb_itemSerialSize( hb_hashGetValueAt( pItem, u ),
                                            pRefPtr, ulOffset + ulSize );
            }
         }
         break;

      default:
         /* map to NIL */
         ulSize = 1;
   }

   return ulSize;
}

static ULONG hb_serializeItem( PHB_ITEM pItem, UCHAR * pBuffer,
                               ULONG ulOffset, PHB_CYCLIC_REF pRef )
{
   HB_LONG lVal;
   ULONG ulRef, ulLen, u;
   LONG l;
   double d;
   const char * szVal;

   switch( hb_itemType( pItem ) )
   {
      case HB_IT_NIL:
         pBuffer[ ulOffset++ ] = HB_SERIAL_NIL;
         break;

      case HB_IT_LOGICAL:
         pBuffer[ ulOffset++ ] = hb_itemGetL( pItem ) ? HB_SERIAL_TRUE : HB_SERIAL_FALSE;
         break;

      case HB_IT_DATE:
         pBuffer[ ulOffset++ ] = HB_SERIAL_DATE;
         l = hb_itemGetDL( pItem );
         HB_PUT_LE_UINT24( &pBuffer[ ulOffset ], l );
         ulOffset += 3;
         break;

      case HB_IT_INTEGER:
      case HB_IT_LONG:
         lVal = hb_itemGetNInt( pItem );
         if( lVal == 0 )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_ZERO;
         }
         else if( HB_LIM_INT8( lVal ) )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_INT8;
            pBuffer[ ulOffset++ ] = ( UCHAR ) lVal;
         }
         else if( HB_LIM_INT16( lVal ) )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_INT16;
            HB_PUT_LE_UINT16( &pBuffer[ ulOffset ], lVal );
            ulOffset += 2;
         }
         else if( HB_LIM_INT24( lVal ) )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_INT24;
            HB_PUT_LE_UINT24( &pBuffer[ ulOffset ], lVal );
            ulOffset += 3;
         }
         else if( HB_LIM_INT32( lVal ) )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_INT32;
            HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], lVal );
            ulOffset += 4;
         }
         else
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_INT64;
            HB_PUT_LE_UINT64( &pBuffer[ ulOffset ], lVal );
            ulOffset += 8;
         }
         break;

      case HB_IT_DOUBLE:
         d = hb_itemGetND( pItem );
         if( d == 0.0 )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_ZERO;
         }
         else
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_DOUBLE;
            HB_PUT_LE_DOUBLE( &pBuffer[ ulOffset ], d );
            ulOffset += 8;
         }
         break;

      case HB_IT_SYMBOL:
         szVal = hb_itemGetSymbol( pItem )->szName;
         ulLen = strlen( szVal );
         if( ulLen > 0xFF )
            ulLen = 0xFF;
         pBuffer[ ulOffset++ ] = HB_SERIAL_SYMBOL;
         pBuffer[ ulOffset++ ] = ( UCHAR ) ulLen;
         memcpy( &pBuffer[ ulOffset ], szVal, ulLen );
         ulOffset += ulLen;
         break;

      case HB_IT_STRING:
      case HB_IT_MEMO:
         szVal = hb_itemGetCPtr( pItem );
         ulLen = hb_itemGetCLen( pItem );
         if( ulLen == 0 )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_STRNUL;
         }
         else
         {
            u = ulLen;
            while( u && szVal[ u - 1 ] == ' ' )
               --u;
            u = ulLen - u;
            if( ulLen <= 255 )
            {
               if( u > 1 )
               {
                  ulLen -= u;
                  pBuffer[ ulOffset++ ] = HB_SERIAL_STRPAD8;
                  pBuffer[ ulOffset++ ] = ( UCHAR ) ulLen;
                  pBuffer[ ulOffset++ ] = ( UCHAR ) u;
               }
               else
               {
                  pBuffer[ ulOffset++ ] = HB_SERIAL_STRING8;
                  pBuffer[ ulOffset++ ] = ( UCHAR ) ulLen;
               }
            }
            else if( ulLen <= UINT16_MAX )
            {
               if( u > 2 )
               {
                  ulLen -= u;
                  pBuffer[ ulOffset++ ] = HB_SERIAL_STRPAD16;
                  HB_PUT_LE_UINT16( &pBuffer[ ulOffset ], ulLen );
                  ulOffset += 2;
                  HB_PUT_LE_UINT16( &pBuffer[ ulOffset ], u );
                  ulOffset += 2;
               }
               else
               {
                  pBuffer[ ulOffset++ ] = HB_SERIAL_STRING16;
                  HB_PUT_LE_UINT16( &pBuffer[ ulOffset ], ulLen );
                  ulOffset += 2;
               }
            }
            else
            {
               if( u > 4 )
               {
                  ulLen -= u;
                  pBuffer[ ulOffset++ ] = HB_SERIAL_STRPAD32;
                  HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], ulLen );
                  ulOffset += 4;
                  HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], u );
                  ulOffset += 4;
               }
               else
               {
                  pBuffer[ ulOffset++ ] = HB_SERIAL_STRING32;
                  HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], ulLen );
                  ulOffset += 4;
               }
            }
            memcpy( &pBuffer[ ulOffset ], szVal, ulLen );
            ulOffset += ulLen;
         }
         break;

      case HB_IT_ARRAY:
         if( hb_itemSerialValueOffset( pRef, hb_arrayId( pItem ), ulOffset, &ulRef ) )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_REF;
            HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], ulRef );
            ulOffset += 4;
         }
         else
         {
            USHORT uiClass = hb_objGetClass( pItem );
            if( uiClass )
            {
               const char * szClass = hb_clsName( uiClass ),
                          * szFunc = hb_clsFuncName( uiClass );
               if( szClass && szFunc )
               {
                  pBuffer[ ulOffset++ ] = HB_SERIAL_OBJ;
                  ulLen = strlen( szClass ) + 1;
                  memcpy( &pBuffer[ ulOffset ], szClass, ulLen );
                  ulOffset += ulLen;
                  ulLen = strlen( szFunc ) + 1;
                  memcpy( &pBuffer[ ulOffset ], szFunc, ulLen );
                  ulOffset += ulLen;
               }
            }
            ulLen = hb_arrayLen( pItem );
            if( ulLen <= 255 )
            {
               pBuffer[ ulOffset++ ] = ulRef == HB_SERIAL_DUMMYOFFSET ?
                                       HB_SERIAL_ARRAY8 : HB_SERIAL_ARRAYREF8;
               pBuffer[ ulOffset++ ] = ( UCHAR ) ulLen;
            }
            else if( ulLen <= UINT16_MAX )
            {
               pBuffer[ ulOffset++ ] = ulRef == HB_SERIAL_DUMMYOFFSET ?
                                       HB_SERIAL_ARRAY16 : HB_SERIAL_ARRAYREF16;
               HB_PUT_LE_UINT16( &pBuffer[ ulOffset ], ulLen );
               ulOffset += 2;
            }
            else
            {
               pBuffer[ ulOffset++ ] = ulRef == HB_SERIAL_DUMMYOFFSET ?
                                       HB_SERIAL_ARRAY32 : HB_SERIAL_ARRAYREF32;
               HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], ulLen );
               ulOffset += 4;
            }
            for( u = 1; u <= ulLen; u++ )
               ulOffset = hb_serializeItem( hb_arrayGetItemPtr( pItem, u ), pBuffer, ulOffset, pRef );
         }
         break;

      case HB_IT_HASH:
         if( hb_itemSerialValueOffset( pRef, hb_hashId( pItem ), ulOffset, &ulRef ) )
         {
            pBuffer[ ulOffset++ ] = HB_SERIAL_REF;
            HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], ulRef );
            ulOffset += 4;
         }
         else
         {
            ulLen = hb_hashLen( pItem );
            if( ulLen <= 255 )
            {
               pBuffer[ ulOffset++ ] = ulRef == HB_SERIAL_DUMMYOFFSET ?
                                       HB_SERIAL_HASH8 : HB_SERIAL_HASHREF8;
               pBuffer[ ulOffset++ ] = ( UCHAR ) ulLen;
            }
            else if( ulLen <= UINT16_MAX )
            {
               pBuffer[ ulOffset++ ] = ulRef == HB_SERIAL_DUMMYOFFSET ?
                                       HB_SERIAL_HASH16 : HB_SERIAL_HASHREF16;
               HB_PUT_LE_UINT16( &pBuffer[ ulOffset ], ulLen );
               ulOffset += 2;
            }
            else
            {
               pBuffer[ ulOffset++ ] = ulRef == HB_SERIAL_DUMMYOFFSET ?
                                       HB_SERIAL_HASH32 : HB_SERIAL_HASHREF32;
               HB_PUT_LE_UINT32( &pBuffer[ ulOffset ], ulLen );
               ulOffset += 4;
            }
            for( u = 1; u <= ulLen; u++ )
            {
               ulOffset = hb_serializeItem( hb_hashGetKeyAt( pItem, u ), pBuffer, ulOffset, pRef );
               ulOffset = hb_serializeItem( hb_hashGetValueAt( pItem, u ), pBuffer, ulOffset, pRef );
            }
         }
         break;

      default:
         /* map to NIL */
         pBuffer[ ulOffset++ ] = HB_SERIAL_NIL;
         break;
   }

   return ulOffset;
}

static ULONG hb_deserializeHash( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffset,
                                 ULONG ulLen, PHB_CYCLIC_REF pRef )
{
   hb_hashNew( pItem );

   if( ulLen )
   {
      PHB_ITEM pKey = hb_itemNew( NULL );
      PHB_ITEM pVal = hb_itemNew( NULL );

      hb_hashPreallocate( pItem, ulLen );
      while( ulLen-- )
      {
         ulOffset = hb_deserializeItem( pKey, pBuffer, ulOffset, pRef );
         ulOffset = hb_deserializeItem( pVal, pBuffer, ulOffset, pRef );
         hb_hashAdd( pItem, pKey, pVal );
      }
      hb_itemRelease( pKey );
      hb_itemRelease( pVal );
   }

   return ulOffset;
}

static ULONG hb_deserializeArray( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffset,
                                  ULONG ulLen, PHB_CYCLIC_REF pRef )
{
   ULONG u;

   hb_arrayNew( pItem, ulLen );
   for( u = 1; u <= ulLen; u++ )
      ulOffset = hb_deserializeItem( hb_arrayGetItemPtr( pItem, u ),
                                     pBuffer, ulOffset, pRef );

   return ulOffset;
}

static ULONG hb_deserializeItem( PHB_ITEM pItem, UCHAR * pBuffer,
                                 ULONG ulOffset, PHB_CYCLIC_REF pRef )
{
   ULONG ulLen, ulPad;
   char * szVal;

   switch( pBuffer[ ulOffset++ ] )
   {
      case HB_SERIAL_NIL:
         hb_itemClear( pItem );
         break;

      case HB_SERIAL_TRUE:
         hb_itemPutL( pItem, TRUE );
         break;

      case HB_SERIAL_FALSE:
         hb_itemPutL( pItem, FALSE );
         break;

      case HB_SERIAL_ZERO:
         hb_itemPutNI( pItem, 0 );
         break;

      case HB_SERIAL_INT8:
         hb_itemPutNI( pItem, pBuffer[ ulOffset++ ] );
         break;

      case HB_SERIAL_INT16:
         hb_itemPutNI( pItem, HB_GET_LE_INT16( &pBuffer[ ulOffset ] ) );
         ulOffset += 2;
         break;

      case HB_SERIAL_INT24:
         hb_itemPutNInt( pItem, HB_GET_LE_INT24( &pBuffer[ ulOffset ] ) );
         ulOffset += 3;
         break;

      case HB_SERIAL_INT32:
         hb_itemPutNInt( pItem, HB_GET_LE_INT32( &pBuffer[ ulOffset ] ) );
         ulOffset += 4;
         break;

      case HB_SERIAL_INT64:
         hb_itemPutNInt( pItem, HB_GET_LE_INT64( &pBuffer[ ulOffset ] ) );
         ulOffset += 8;
         break;

      case HB_SERIAL_DOUBLE:
         hb_itemPutND( pItem, HB_GET_LE_DOUBLE( &pBuffer[ ulOffset ] ) );
         ulOffset += 8;
         break;

      case HB_SERIAL_DATE:
         hb_itemPutDL( pItem, HB_GET_LE_UINT24( &pBuffer[ ulOffset ] ) );
         ulOffset += 3;
         break;

      case HB_SERIAL_SYMBOL:
         ulLen = pBuffer[ ulOffset++ ];
         szVal = hb_strndup( ( char * ) &pBuffer[ ulOffset ], ulLen );
         hb_itemPutSymbol( pItem, hb_dynsymGetSymbol( szVal ) );
         hb_xfree( szVal );
         ulOffset += ulLen;
         break;

      case HB_SERIAL_STRNUL:
         hb_itemPutCL( pItem, NULL, 0 );
         break;
      case HB_SERIAL_STRING8:
         ulLen = pBuffer[ ulOffset++ ];
         hb_itemPutCL( pItem, ( char * ) &pBuffer[ ulOffset ], ulLen );
         ulOffset += ulLen;
         break;
      case HB_SERIAL_STRING16:
         ulLen = HB_GET_LE_UINT16( &pBuffer[ ulOffset ] );
         ulOffset += 2;
         hb_itemPutCL( pItem, ( char * ) &pBuffer[ ulOffset ], ulLen );
         ulOffset += ulLen;
         break;
      case HB_SERIAL_STRING32:
         ulLen = HB_GET_LE_UINT32( &pBuffer[ ulOffset ] );
         ulOffset += 4;
         hb_itemPutCL( pItem, ( char * ) &pBuffer[ ulOffset ], ulLen );
         ulOffset += ulLen;
         break;
      case HB_SERIAL_STRPAD8:
         ulLen = pBuffer[ ulOffset++ ];
         ulPad = pBuffer[ ulOffset++ ];
         szVal = ( char * ) hb_xgrab( ulLen + ulPad + 1 );
         memcpy( szVal, &pBuffer[ ulOffset ], ulLen );
         memset( szVal + ulLen, ' ', ulPad + 1 );
         hb_itemPutCLPtr( pItem, szVal, ulLen + ulPad );
         ulOffset += ulLen;
         break;
      case HB_SERIAL_STRPAD16:
         ulLen = HB_GET_LE_UINT16( &pBuffer[ ulOffset ] );
         ulOffset += 2;
         ulPad = HB_GET_LE_UINT16( &pBuffer[ ulOffset ] );
         ulOffset += 2;
         szVal = ( char * ) hb_xgrab( ulLen + ulPad + 1 );
         memcpy( szVal, &pBuffer[ ulOffset ], ulLen );
         memset( szVal + ulLen, ' ', ulPad + 1 );
         hb_itemPutCLPtr( pItem, szVal, ulLen + ulPad );
         ulOffset += ulLen;
         break;
      case HB_SERIAL_STRPAD32:
         ulLen = HB_GET_LE_UINT32( &pBuffer[ ulOffset ] );
         ulOffset += 4;
         ulPad = HB_GET_LE_UINT32( &pBuffer[ ulOffset ] );
         ulOffset += 4;
         szVal = ( char * ) hb_xgrab( ulLen + ulPad + 1 );
         memcpy( szVal, &pBuffer[ ulOffset ], ulLen );
         hb_xmemset( szVal + ulLen, ' ', ulPad + 1 );
         hb_itemPutCLPtr( pItem, szVal, ulLen + ulPad );
         ulOffset += ulLen;
         break;

      case HB_SERIAL_ARRAYREF8:
         hb_itemSerialOffsetSet( pRef, pItem, ulOffset - 1 );
      case HB_SERIAL_ARRAY8:
         ulLen = pBuffer[ ulOffset++ ];
         ulOffset = hb_deserializeArray( pItem, pBuffer, ulOffset, ulLen, pRef );
         break;
      case HB_SERIAL_ARRAYREF16:
         hb_itemSerialOffsetSet( pRef, pItem, ulOffset - 1 );
      case HB_SERIAL_ARRAY16:
         ulLen = HB_GET_LE_UINT16( &pBuffer[ ulOffset ] );
         ulOffset = hb_deserializeArray( pItem, pBuffer, ulOffset + 2, ulLen, pRef );
         break;
      case HB_SERIAL_ARRAYREF32:
         hb_itemSerialOffsetSet( pRef, pItem, ulOffset - 1 );
      case HB_SERIAL_ARRAY32:
         ulLen = HB_GET_LE_UINT32( &pBuffer[ ulOffset ] );
         ulOffset = hb_deserializeArray( pItem, pBuffer, ulOffset + 4, ulLen, pRef );
         break;

      case HB_SERIAL_HASHREF8:
         hb_itemSerialOffsetSet( pRef, pItem, ulOffset - 1 );
      case HB_SERIAL_HASH8:
         ulLen = pBuffer[ ulOffset++ ];
         ulOffset = hb_deserializeHash( pItem, pBuffer, ulOffset, ulLen, pRef );
         break;
      case HB_SERIAL_HASHREF16:
         hb_itemSerialOffsetSet( pRef, pItem, ulOffset - 1 );
      case HB_SERIAL_HASH16:
         ulLen = HB_GET_LE_UINT16( &pBuffer[ ulOffset ] );
         ulOffset = hb_deserializeHash( pItem, pBuffer, ulOffset + 2, ulLen, pRef );
         break;
      case HB_SERIAL_HASHREF32:
         hb_itemSerialOffsetSet( pRef, pItem, ulOffset - 1 );
      case HB_SERIAL_HASH32:
         ulLen = HB_GET_LE_UINT32( &pBuffer[ ulOffset ] );
         ulOffset = hb_deserializeHash( pItem, pBuffer, ulOffset + 4, ulLen, pRef );
         break;

      case HB_SERIAL_REF:
         hb_itemSerialOffsetGet( pRef, pItem,
                                 HB_GET_LE_UINT32( &pBuffer[ ulOffset ] ) );
         ulOffset += 4;
         break;

      case HB_SERIAL_OBJ:
      {
         char * szClass, * szFunc;
         szClass = ( char * ) &pBuffer[ ulOffset ];
         ulLen = strlen( szClass );
         szFunc = szClass + ulLen + 1;
         ulOffset = hb_deserializeItem( pItem, pBuffer,
                              ulOffset + ulLen + strlen( szFunc ) + 2, pRef );
         hb_objSetClass( pItem, szClass, szFunc );
         break;
      }

      default:
         hb_itemClear( pItem );
         break;
   }

   return ulOffset;
}

static BOOL hb_deserializeTest( UCHAR ** pBufferPtr, ULONG * pulSize,
                                ULONG ulOffset, PHB_CYCLIC_REF * pRefPtr )
{
   UCHAR * pBuffer = * pBufferPtr;
   ULONG ulSize = * pulSize, ulLen = 0;

   if( ulSize == 0 )
      return FALSE;

   switch( *pBuffer++ )
   {
      case HB_SERIAL_NIL:
      case HB_SERIAL_TRUE:
      case HB_SERIAL_FALSE:
      case HB_SERIAL_ZERO:
      case HB_SERIAL_STRNUL:
         ulSize = 1;
         break;
      case HB_SERIAL_INT8:
         ulSize = 2;
         break;
      case HB_SERIAL_INT16:
         ulSize = 3;
         break;
      case HB_SERIAL_INT24:
      case HB_SERIAL_DATE:
         ulSize = 4;
         break;
      case HB_SERIAL_INT32:
         ulSize = 5;
         break;
      case HB_SERIAL_INT64:
      case HB_SERIAL_DOUBLE:
         ulSize = 9;
         break;
      case HB_SERIAL_SYMBOL:
      case HB_SERIAL_STRING8:
         ulSize = 2 + ( ulSize >= 2 ? *pBuffer : ulSize );
         break;
      case HB_SERIAL_STRING16:
         ulSize = 3 + ( ulSize >= 3 ? HB_GET_LE_UINT16( pBuffer ) : ulSize );
         break;
      case HB_SERIAL_STRING32:
         ulSize = 5 + ( ulSize >= 5 ? HB_GET_LE_UINT32( pBuffer ) : ulSize );
         break;
      case HB_SERIAL_STRPAD8:
         ulSize = 3 + ( ulSize >= 3 ? *pBuffer : ulSize );
         break;
      case HB_SERIAL_STRPAD16:
         ulSize = 5 + ( ulSize >= 5 ? HB_GET_LE_UINT16( pBuffer ) : ulSize );
         break;
      case HB_SERIAL_STRPAD32:
         ulSize = 9 + ( ulSize >= 9 ? HB_GET_LE_UINT32( pBuffer ) : ulSize );
         break;
      case HB_SERIAL_ARRAYREF8:
         if( hb_itemSerialOffsetRef( pRefPtr, NULL, ulOffset ) )
            return FALSE;
      case HB_SERIAL_ARRAY8:
         if( ulSize >= 2 )
         {
            ulSize = 2;
            ulLen = *pBuffer;
         }
         else
            ulSize++;
         break;
      case HB_SERIAL_ARRAYREF16:
         if( hb_itemSerialOffsetRef( pRefPtr, NULL, ulOffset ) )
            return FALSE;
      case HB_SERIAL_ARRAY16:
         if( ulSize >= 3 )
         {
            ulSize = 3;
            ulLen = HB_GET_LE_UINT16( pBuffer );
         }
         else
            ulSize++;
         break;
      case HB_SERIAL_ARRAYREF32:
         if( hb_itemSerialOffsetRef( pRefPtr, NULL, ulOffset ) )
            return FALSE;
      case HB_SERIAL_ARRAY32:
         if( ulSize >= 5 )
         {
            ulSize = 5;
            ulLen = HB_GET_LE_UINT32( pBuffer );
         }
         else
            ulSize++;
         break;
      case HB_SERIAL_HASHREF8:
         if( hb_itemSerialOffsetRef( pRefPtr, NULL, ulOffset ) )
            return FALSE;
      case HB_SERIAL_HASH8:
         if( ulSize >= 2 )
         {
            ulSize = 2;
            ulLen = *pBuffer << 1;
         }
         else
            ulSize++;
         break;
      case HB_SERIAL_HASHREF16:
         if( hb_itemSerialOffsetRef( pRefPtr, NULL, ulOffset ) )
            return FALSE;
      case HB_SERIAL_HASH16:
         if( ulSize >= 3 )
         {
            ulSize = 3;
            ulLen = HB_GET_LE_UINT16( pBuffer ) << 1;
         }
         else
            ulSize++;
         break;
      case HB_SERIAL_HASHREF32:
         if( hb_itemSerialOffsetRef( pRefPtr, NULL, ulOffset ) )
            return FALSE;
      case HB_SERIAL_HASH32:
         if( ulSize >= 5 )
         {
            ulSize = 5;
            ulLen = HB_GET_LE_UINT32( pBuffer ) << 1;
         }
         else
            ulSize++;
         break;
      case HB_SERIAL_REF:
         if( !hb_itemSerialOffsetRef( pRefPtr, NULL, HB_GET_LE_UINT32( pBuffer ) ) )
            return FALSE;
         ulSize = 5;
         break;
      case HB_SERIAL_OBJ:
         ulLen = hb_strnlen( ( char * ) pBuffer, ulSize - 1 ) + 1;
         if( ulLen >= ulSize )
            ulSize++;
         else
         {
            ulLen += hb_strnlen( ( char * ) pBuffer + ulLen, ulSize - ulLen - 1 ) + 2;
            if( ulLen >= ulSize )
               ulSize++;
            else
               ulSize = ulLen;
         }
         ulLen = 1;
         break;
      default:
         ulSize = 1;
         break;
   }

   if( ulSize > * pulSize )
      return FALSE;

   * pulSize -= ulSize;
   * pBufferPtr += ulSize;

   while( ulLen )
   {
      ulOffset += ulSize;
      ulSize = * pulSize;
      if( !hb_deserializeTest( pBufferPtr, pulSize, ulOffset, pRefPtr ) )
         return FALSE;
      ulSize -= * pulSize;
      --ulLen;
   }

   return TRUE;
}

/*
 * These function will be public in the future [druzus]
 */
static char * hb_itemSerial( PHB_ITEM pItem, ULONG *pulSize )
{
   PHB_CYCLIC_REF pRef = NULL;
   ULONG ulSize = hb_itemSerialSize( pItem, &pRef, 0 );
   UCHAR * pBuffer = ( UCHAR * ) hb_xgrab( ulSize + 1 );

   hb_itemSerialUnRefFree( &pRef );
   hb_serializeItem( pItem, pBuffer, 0, pRef );
   pBuffer[ ulSize ] = '\0';
   if( pulSize )
      *pulSize = ulSize;

   hb_itemSerialRefFree( pRef );

   return ( char * ) pBuffer;
}

/*
 * These function will be public in the future [druzus]
 */
static PHB_ITEM hb_itemDeSerial( char ** pBufferPtr, ULONG * pulSize )
{
   PHB_CYCLIC_REF pRef = NULL;
   UCHAR * pBuffer = ( UCHAR * ) *pBufferPtr;
   PHB_ITEM pItem = NULL;

   if( !pulSize || hb_deserializeTest( ( UCHAR ** ) pBufferPtr, pulSize, 0, &pRef ) )
   {
      pItem = hb_itemNew( NULL );
      hb_deserializeItem( pItem, pBuffer, 0, pRef );
   }
   hb_itemSerialRefFree( pRef );

   return pItem;
}

HB_FUNC( HB_SERIALIZE )
{
   PHB_ITEM pItem = hb_param( 1, HB_IT_ANY );

   if( pItem )
   {
      ULONG ulSize;
      char * pBuffer = hb_itemSerial( pItem, &ulSize );
      hb_retclen_buffer( pBuffer, ulSize );
   }
}

HB_FUNC( HB_DESERIALIZE )
{
   PHB_ITEM pItem, pParam = hb_param( 1, HB_IT_BYREF );
   ULONG ulSize = hb_parclen( 1 );

   if( ulSize )
   {
      char * pBuffer = hb_parc( 1 );

      pItem = hb_itemDeSerial( &pBuffer, &ulSize );
      if( pItem )
      {
         hb_itemReturn( pItem );
         if( pParam )
         {
            hb_itemPutCL( pItem, pBuffer, ulSize );
            hb_itemMove( pParam, pItem );
         }
         hb_itemRelease( pItem );
      }
      else if( pParam )
         hb_itemClear( pParam );
   }
   else if( pParam )
      hb_itemClear( pParam );
}
