Files
harbour-core/contrib/hbamf/amfenc.c
Aleksander Czajczynski 6e3fe511f0 2017-12-14 14:20 UTC+0100 Aleksander Czajczynski (hb fki.pl)
* contrib/hbamf/amfenc.c
    * update amf3_Encode() serialize function with great idea of Przemek
      implemented recenly in hb_Serialize(). Checking garbage collector
      reference count can save time here too, unique arrays and hashes won't
      be indexed as references.

  * contrib/hbexpat/3rd/expat/*
    ! updated to 2.2.5 (from 2.2.1) using 3rdpatch.hb, expat.diff from
      Viktor's 3.4 fork was used - but (again) adapted for DOS 8.3 naming
      scheme. Also i've kept local patches for WinCE, OpenWatcom DOS/OS2,
      please test. Compilation in CPP mode is explicitly disabled for libexpat
      now, as upstream decided to ignore such use cases completly.

   * contrib/hbexpat/*
     * synced with Viktor's 3.4 fork

   + include/hbarc4.h
   * src/harbour.def
     + export ARC4 core routines like in 3.4 fork, updated expat lib reuses it.

   * contrib/hbtip/hbtip.hbp
     ! adapt *.hbx file specifier to hbmk2 3.2 syntax
2017-12-14 14:21:03 +01:00

1463 lines
39 KiB
C

/* Ilina Stoilkovska <anili100/at/gmail.com> 2011
* Aleksander Czajczynski <hb/at/fki.pl> 2011-2017
*
* Encoding Harbour items to AMF3
*
* Contains portions from
* Dave Thompson's MIT licensed
* AmFast C library for Python
*/
#include "hbapi.h"
#include "hbapiitm.h"
#include "hbapistr.h"
#include "hbapicls.h" /* for hb_objSetClass() */
#include "hbstack.h"
#include "hbapirdd.h" /* for amf3_FromWA() */
#include "hbapierr.h" /* as above */
#include "amf.h"
#include "hbdate.h"
#include "hbmath.h"
#include "hbvm.h"
typedef struct
{
char * cBuf;
HB_ISIZ position;
HB_ISIZ length;
HB_BOOL use_refs;
HB_BOOL use_strstr;
HB_BOOL str_rtrim;
HB_SIZE strstr_count; /* used only when str_ref is disabled */
HB_SIZE objnref_count; /* items that should normally appear in obj_ref, but GC says that they are not referenced */
PHB_ITEM obj_ref;
PHB_ITEM str_ref;
PHB_ITEM strstr_ref;
PHB_ITEM class_ref;
PHB_ITEM conv_function;
HB_BOOL encode_ba;
} amfContext;
static HB_BOOL amf3_encode( amfContext * context, PHB_ITEM pItem );
extern HB_BOOL hbamf_is_cls_externalizable( HB_USHORT uiClass );
static void _ref_realItemPtr( PHB_ITEM pKey, PHB_ITEM pItem )
{
if( HB_IS_STRING( pItem ) )
hb_itemPutPtr( pKey, HB_UNCONST( hb_itemGetCPtr( pItem ) ) );
else if( HB_IS_ARRAY( pItem ) )
hb_itemPutPtr( pKey, hb_arrayId( pItem ) );
else if( HB_IS_HASH( pItem ) )
hb_itemPutPtr( pKey, hb_hashId( pItem ) );
else if( HB_IS_DATETIME( pItem ) )
hb_itemCopy( pKey, pItem );
}
static HB_ISIZ bufferGrow( amfContext * context, HB_ISIZ len )
{
HB_ISIZ new_len = context->position + len;
HB_ISIZ current_len = context->length;
while( new_len > current_len )
current_len *= 2;
if( current_len != context->length )
{
context->cBuf = ( char * ) hb_xrealloc( context->cBuf, sizeof( char ) * current_len );
if( ! context->cBuf )
return -1;
context->length = current_len;
}
return current_len;
}
static int writeByte( amfContext * context, char byte )
{
if( bufferGrow( context, 1 ) == -1 )
return 0;
memcpy( context->cBuf + context->position, &byte, 1 );
context->position += 1;
return 1;
}
static HB_ISIZ writeBuffer( amfContext * context, const char * str, HB_ISIZ len )
{
if( bufferGrow( context, len ) == -1 )
return 0;
memcpy( context->cBuf + context->position, str, len );
context->position += len;
return len;
}
static HB_BOOL amfX_encode_double( amfContext * context, double value )
{
char c_value[ 8 ];
char * c_value_ref = &c_value[ 0 ];
#ifndef HB_BIG_ENDIAN
char flipped[ 8 ];
#endif
memcpy( c_value_ref, &value, 8 );
#ifdef HB_BIG_ENDIAN
if( writeBuffer( context, c_value_ref, 8 ) == 0 )
return HB_FALSE;
#else
flipped[ 0 ] = c_value[ 7 ];
flipped[ 1 ] = c_value[ 6 ];
flipped[ 2 ] = c_value[ 5 ];
flipped[ 3 ] = c_value[ 4 ];
flipped[ 4 ] = c_value[ 3 ];
flipped[ 5 ] = c_value[ 2 ];
flipped[ 6 ] = c_value[ 1 ];
flipped[ 7 ] = c_value[ 0 ];
if( writeBuffer( context, flipped, 8 ) == 0 )
return HB_FALSE;
#endif
return HB_TRUE;
}
static HB_BOOL amfX_write_double( amfContext * context, PHB_ITEM pItem )
{
double d = hb_itemGetND( pItem );
if( ! writeByte( context, DOUBLE_TYPE ) )
return HB_FALSE;
return amfX_encode_double( context, d );
}
static HB_BOOL amf3_encode_int( amfContext * context, int value )
{
char tmp[ 4 ];
HB_SIZE tmp_size;
/*
* Int can be up to 4 bytes long.
*
* The first bit of the first 3 bytes
* is set if another byte follows.
*
* The integer value is the last 7 bits from
* the first 3 bytes and the 8 bits of the last byte
* (29 bits).
*
* The int is negative if the 1st bit of the 29 int is set.
*/
value &= 0x1fffffff; /* Ignore 1st 3 bits of 32-bit int, since we're encoding to 29 bit. */
if( value < 0x80 )
{
tmp_size = 1;
tmp[ 0 ] = ( char ) value; /* TODO: some explicit casts in here to keep the compiler silent */
}
else if( value < 0x4000 )
{
tmp_size = 2;
tmp[ 0 ] = ( char ) ( ( value >> 7 ) & 0x7f ) | 0x80; /* Shift bits by 7 to fill 1st byte and set next byte flag */
tmp[ 1 ] = ( char ) value & 0x7f; /* Shift bits by 7 to fill 2nd byte, leave next byte flag unset */
}
else if( value < 0x200000 )
{
tmp_size = 3;
tmp[ 0 ] = ( char ) ( ( value >> 14 ) & 0x7f ) | 0x80;
tmp[ 1 ] = ( char ) ( ( value >> 7 ) & 0x7f ) | 0x80;
tmp[ 2 ] = ( char ) value & 0x7f;
}
else if( value < 0x40000000 )
{
tmp_size = 4;
tmp[ 0 ] = ( char ) ( ( value >> 22 ) & 0x7f ) | 0x80;
tmp[ 1 ] = ( char ) ( ( value >> 15 ) & 0x7f ) | 0x80;
tmp[ 2 ] = ( char ) ( ( value >> 8 ) & 0x7f ) | 0x80; /* Shift bits by 8, since we can use all bits in the 4th byte */
tmp[ 3 ] = ( char ) ( value & 0xff );
}
else
return HB_FALSE;
if( ( HB_SIZE ) writeBuffer( context, tmp, tmp_size ) != tmp_size )
return HB_FALSE;
return HB_TRUE;
}
static HB_BOOL amf3_write_int( amfContext * context, PHB_ITEM pItem )
{
#ifndef HB_LONG_LONG_OFF
HB_LONGLONG n = hb_itemGetNLL( pItem );
#else
long n = hb_itemGetNL( pItem );
#endif
if( n > MIN_INT && n < MAX_INT )
{
if( ! writeByte( context, INT_TYPE ) )
return HB_FALSE;
return amf3_encode_int( context, ( int ) n );
}
else
{
if( ! writeByte( context, DOUBLE_TYPE ) )
return HB_FALSE;
return amfX_encode_double( context, ( double ) n );
}
}
#if 0
static HB_BOOL amf3_encode_float( amfContext * context, PHB_ITEM pItem )
{
float n = ( float ) hb_itemGetND( pItem );
return amfX_encode_double( context, ( double ) n );
}
#endif
static HB_BOOL amf3_encode_nil( amfContext * context )
{
return writeByte( context, NULL_TYPE );
}
static HB_BOOL amf3_encode_bool( amfContext * context, PHB_ITEM pItem )
{
if( hb_itemGetL( pItem ) )
return writeByte( context, TRUE_TYPE );
else
return writeByte( context, FALSE_TYPE );
}
static HB_BOOL amf3_encode_string( amfContext * context, PHB_ITEM pItem )
{
void * hStr = NULL; /* = hb_itemGetCPtr( pItem ); not needed with UTF-8 conversion */
HB_SIZE len;
const char * utf8str = hb_itemGetStrUTF8( pItem, &hStr, &len );
HB_BOOL result;
if( ! hStr )
return HB_FALSE;
if( context->str_rtrim )
len = hb_strRTrimLen( utf8str, len, HB_FALSE );
if( ! amf3_encode_int( context, ( ( int ) len << 1 ) | REFERENCE_BIT ) )
return HB_FALSE;
result = ( writeBuffer( context, utf8str, len ) != 0 );
hb_strfree( hStr );
return result;
}
static int amf3_add_index( amfContext * context, PHB_ITEM pHash, PHB_ITEM pItem )
{
PHB_ITEM pVal;
int result = 0;
if( context->use_refs )
{
PHB_ITEM pKey = hb_itemNew( NULL );
_ref_realItemPtr( pKey, pItem );
if( ! HB_IS_POINTER( pKey ) && ! HB_IS_DATETIME( pKey ) )
{
hb_itemRelease( pKey );
return -1;
}
if( pHash == context->str_ref )
result = ( int ) ( hb_hashLen( pHash ) + context->strstr_count );
/* ->strstr_count > 0 only when some inner context inside
* user-defined conversion function uses only strstr mode
* like amf3_FromWA() function f.e. */
else if( pHash == context->obj_ref )
result = ( int ) ( hb_hashLen( pHash ) + context->objnref_count );
else
result = ( int ) ( hb_hashLen( pHash ) );
pVal = hb_itemPutNS( NULL, result );
if( ! hb_hashAdd( pHash, pKey, pVal ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pVal );
return -1;
}
hb_itemRelease( pVal );
hb_itemRelease( pKey );
}
if( ( HB_IS_STRING( pItem ) || HB_IS_MEMO( pItem ) ) && context->use_strstr )
{
HB_SIZE str_len = hb_itemGetCLen( pItem );
if( str_len > 3 && str_len < 32 ) /* do this only for mid-sized strings */
{
if( ! context->use_refs )
result = ( int ) context->strstr_count;
pVal = hb_itemPutNS( NULL, result ); /* put the AMF reference id as value */
hb_hashAdd( context->strstr_ref, pItem, pVal );
hb_itemRelease( pVal );
}
if( ! context->use_refs )
context->strstr_count++;
}
return result;
}
static int amf3_get_index( amfContext * context, PHB_ITEM pHash, PHB_ITEM pItem )
{
if( context->use_refs )
{
PHB_ITEM pKey = hb_itemNew( NULL );
HB_SIZE nPos;
PHB_ITEM pVal;
_ref_realItemPtr( pKey, pItem );
if( ! HB_IS_POINTER( pKey ) && ! HB_IS_DOUBLE( pKey ) )
{
hb_itemRelease( pKey );
return -1;
}
if( context->objnref_count )
{
pVal = hb_hashGetItemPtr( pHash, pKey, 0 );
hb_itemRelease( pKey );
if( pVal )
return ( int ) hb_itemGetNS( pVal );
}
else if( hb_hashScan( pHash, pKey, &nPos ) )
{
hb_itemRelease( pKey );
return ( int ) ( nPos - 1 );
}
else
hb_itemRelease( pKey );
}
if( ( HB_IS_STRING( pItem ) || HB_IS_MEMO( pItem ) ) && context->use_strstr )
{
HB_SIZE str_len = hb_itemGetCLen( pItem );
if( str_len > 3 && str_len < 32 ) /* do this only for mid-sized strings */
{
PHB_ITEM pStrIdx = hb_hashGetItemPtr( context->strstr_ref, pItem, 0 );
if( pStrIdx )
return ( int ) hb_itemGetNS( pStrIdx );
}
}
return -1;
}
static int amf3_encode_reference( amfContext * context, PHB_ITEM pHash, PHB_ITEM pItem, int bit )
{
int idx;
if( pItem == NULL )
return -1;
idx = amf3_get_index( context, pHash, pItem );
if( idx > -1 )
{
if( idx < MAX_INT )
{
if( ! amf3_encode_int( context, ( idx << ( bit + 1 ) ) | ( 0x00 + bit ) ) )
return 0;
return 1;
}
}
if( amf3_add_index( context, pHash, pItem ) == -1 )
return 0;
return -1;
}
static HB_BOOL amf3_serialize_string( amfContext * context, PHB_ITEM pItem )
{
int result;
HB_SIZE len = hb_itemGetCLen( pItem );
if( len == 0 )
return writeByte( context, EMPTY_STRING_TYPE );
else if( context->str_rtrim && hb_strRTrimLen( hb_itemGetCPtr( pItem ), len, HB_FALSE ) == 0 )
return writeByte( context, EMPTY_STRING_TYPE );
result = amf3_encode_reference( context, context->str_ref, pItem, 0 );
if( result > -1 )
return result;
return amf3_encode_string( context, pItem );
}
#if 0
static HB_BOOL amf3_serialize_object_as_string( amfContext * context, PHB_ITEM pItem )
{
PHB_ITEM pStr;
HB_BOOL result;
if( HB_IS_STRING( pItem ) || HB_IS_MEMO( pItem ) )
return amf3_serialize_string( context, pItem );
if( ! hb_itemPutC( pStr, hb_itemGetCPtr( pItem ) ) )
return HB_FALSE;
result = amf3_serialize_string( context, pStr );
hb_itemRelease( pStr );
return result;
}
#endif
static HB_BOOL amf3_encode_hash( amfContext * context, PHB_ITEM pItem )
{
PHB_ITEM pKey;
PHB_ITEM pVal;
HB_ISIZ i;
HB_ISIZ len = hb_hashLen( pItem );
HB_ISIZ nIntKeys = 0;
if( ( ( ( hb_hashGetFlags( pItem ) & HB_HASH_KEEPORDER ) != 0
&& HB_IS_INTEGER( hb_hashGetKeyAt( pItem, 1 ) ) )
|| hb_hashGetFlags( pItem ) & HB_HASH_KEEPORDER ) == 0 )
{
for( i = 1; i <= len; i++ )
{
pKey = hb_hashGetKeyAt( pItem, i );
if( HB_IS_INTEGER( pKey ) )
nIntKeys++;
}
}
if( ! amf3_encode_int( context, ( int ) ( ( nIntKeys << 1 ) | REFERENCE_BIT ) ) )
return HB_FALSE;
for( i = 1; i <= len; i++ )
{
pKey = hb_hashGetKeyAt( pItem, i );
pVal = hb_hashGetValueAt( pItem, i );
if( HB_IS_STRING( pKey ) )
{
if( ! amf3_serialize_string( context, pKey ) )
return HB_FALSE;
if( ! amf3_encode( context, pVal ) )
return HB_FALSE;
}
}
if( ! writeByte( context, EMPTY_STRING_TYPE ) )
return HB_FALSE;
if( nIntKeys > 0 )
{
for( i = 1; i <= len; i++ )
{
pKey = hb_hashGetKeyAt( pItem, i );
pVal = hb_hashGetValueAt( pItem, i );
if( HB_IS_INTEGER( pKey ) )
{
if( ! amf3_encode( context, pVal ) )
return HB_FALSE;
}
}
}
return HB_TRUE;
}
static HB_BOOL amf3_encode_dynamic_dict( amfContext * context, PHB_ITEM pItem )
{
HB_ISIZ i;
HB_ISIZ len = hb_hashLen( pItem );
for( i = 1; i <= len; i++ )
{
PHB_ITEM pKey = hb_hashGetKeyAt( pItem, i );
PHB_ITEM pVal = hb_hashGetValueAt( pItem, i );
if( HB_IS_STRING( pKey ) )
{
if( ! amf3_serialize_string( context, pKey ) )
return HB_FALSE;
if( ! amf3_encode( context, pVal ) )
return HB_FALSE;
}
}
if( ! writeByte( context, EMPTY_STRING_TYPE ) )
return HB_FALSE;
return HB_TRUE;
}
static HB_BOOL amf3_serialize_hash( amfContext * context, PHB_ITEM pItem )
{
if( context->use_refs )
{
if( hb_hashRefs( pItem ) > 1 )
{
HB_BOOL result = amf3_encode_reference( context, context->obj_ref, pItem, 0 );
if( result > -1 )
return result;
}
else
context->objnref_count++;
}
return amf3_encode_hash( context, pItem );
}
static HB_BOOL amf3_write_hash( amfContext * context, PHB_ITEM pItem )
{
if( ! writeByte( context, ARRAY_TYPE ) )
return 0;
return amf3_serialize_hash( context, pItem );
}
static HB_ISIZ amf3_encode_byte_array( amfContext * context, PHB_ITEM pItem )
{
HB_ISIZ item_len;
const char * bytes;
if( HB_IS_STRING( pItem ) || HB_IS_MEMO( pItem ) )
{
item_len = hb_itemGetCLen( pItem );
bytes = hb_itemGetCPtr( pItem );
}
else
return HB_FALSE;
if( ! bytes )
return HB_FALSE;
return writeBuffer( context, bytes, item_len );
}
static HB_ISIZ amf3_serialize_byte_array( amfContext * context, PHB_ITEM pItem )
{
int result;
if( hb_itemGetCLen( pItem ) == 0 )
return writeByte( context, EMPTY_STRING_TYPE );
result = amf3_encode_reference( context, context->obj_ref, pItem, 0 );
if( result > -1 )
return result;
if( ! amf3_encode_int( context, ( ( int ) hb_itemGetCLen( pItem ) << 1 ) | REFERENCE_BIT ) )
return HB_FALSE;
return amf3_encode_byte_array( context, pItem );
}
static int amf3_encode_date( amfContext * context, PHB_ITEM pItem )
{
double timestamp;
if( ! amf3_encode_int( context, REFERENCE_BIT ) )
return 0;
timestamp = ( ( hb_itemGetTD( pItem ) - 2440587.5 ) * 86400000 );
return amfX_encode_double( context, timestamp );
}
static int amf3_serialize_date( amfContext * context, PHB_ITEM pItem )
{
int result = amf3_encode_reference( context, context->obj_ref, pItem, 0 );
if( result > -1 )
return result;
return amf3_encode_date( context, pItem );
}
static HB_BOOL amf3_encode_array( amfContext * context, PHB_ITEM pItem )
{
HB_SIZE item_len = hb_arrayLen( pItem );
int i;
if( ! amf3_encode_int( context, ( ( int ) item_len << 1 ) | REFERENCE_BIT ) )
return HB_FALSE;
if( ! writeByte( context, NULL_TYPE ) )
return HB_FALSE;
for( i = 1; i <= ( int ) item_len; i++ )
{
PHB_ITEM pArrayItem;
int result;
pArrayItem = hb_arrayGetItemPtr( pItem, i );
if( ! pArrayItem )
return HB_FALSE;
result = amf3_encode( context, pArrayItem );
if( ! result )
return HB_FALSE;
}
return HB_TRUE;
}
static HB_BOOL amf3_serialize_array( amfContext * context, PHB_ITEM pItem )
{
if( context->use_refs )
{
if( hb_arrayRefs( pItem ) > 1 )
{
int result = amf3_encode_reference( context, context->obj_ref, pItem, 0 );
if( result > -1 )
return result;
}
else
context->objnref_count++;
}
return amf3_encode_array( context, pItem );
}
static int amf3_encode_class_def( amfContext * context, PHB_ITEM pClass )
{
int header;
int result;
HB_ISIZ static_attr_len;
HB_ISIZ i;
PHB_ITEM class_alias;
PHB_ITEM static_attrs;
#if 0
PHB_ITEM attr_len = NULL;
#endif
if( ! pClass )
{
if( ! writeByte( context, DYNAMIC ) )
return 0;
if( ! writeByte( context, EMPTY_STRING_TYPE ) )
return 0;
return 1;
}
if( hb_hashGetCItemPos( pClass, "CLASS_DEF" ) == 0 )
return 0;
if( hb_hashGetCItemPos( pClass, "EXTERNALIZABLE_CLASS_DEF" ) != 0 )
header = EXTERNALIZABLE;
else if( hb_hashGetCItemPos( pClass, "DYNAMIC_CLASS_DEF" ) != 0 )
header = DYNAMIC;
else
header = STATIC;
class_alias = hb_hashGetCItemPtr( pClass, "alias" );
if( ! class_alias )
return 0;
if( header == EXTERNALIZABLE )
{
if( ! amf3_encode_int( context, header ) )
return 0;
result = amf3_serialize_string( context, class_alias );
return result;
}
static_attrs = hb_hashGetCItemPtr( pClass, "static_attrs" );
if( ! static_attrs )
return 0;
static_attr_len = hb_arrayLen( static_attrs ); /* array this is -- hb_itemGetCLen( static_attrs ); */
if( static_attr_len == -1 || static_attr_len > ( MAX_INT >> 4 ) )
return 0;
header |= ( ( int ) static_attr_len ) << 4;
if( ! amf3_encode_int( context, header ) )
return 0;
result = amf3_serialize_string( context, class_alias );
/* not needed hb_itemRelease( class_alias ); */
if( ! result )
return 0;
for( i = 0; i < static_attr_len; i++ )
{
PHB_ITEM attr_name = hb_itemArrayGet( static_attrs, i );
if( ! attr_name )
{
/* not needed hb_itemRelease( static_attrs ); */
return 0;
}
result = amf3_serialize_string( context, attr_name );
hb_itemRelease( attr_name );
if( ! result )
return 0;
}
/* not needed hb_itemRelease( static_attrs ); */
return 1;
}
static int amf3_serialize_class_def( amfContext * context, PHB_ITEM pClass )
{
int result = amf3_encode_reference( context, context->class_ref, pClass, 0 );
if( result > -1 )
return result;
return amf3_encode_class_def( context, pClass );
}
/* Get an object's class def. */
static PHB_ITEM class_def_from_class( /* amfContext * context, */ PHB_ITEM pItem )
{
HB_USHORT uiClass;
PHB_ITEM pClass;
PHB_ITEM pKey;
PHB_ITEM pValue;
/* get Harbour's class id/handle */
uiClass = hb_objGetClass( pItem );
if( ! uiClass )
return NULL;
/* we left this (python-originated) indirect method of storing
class "properties" in a hash, this it may be easier in the
future to implement some additional class mapper instead of
"tags" which are put into the Harbour class definitions
right now */
pClass = hb_hashNew( NULL );
pKey = hb_itemPutC( NULL, "CLASS_DEF" );
pValue = hb_itemNew( NULL );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
hb_itemRelease( pClass );
return NULL;
}
hb_itemRelease( pKey );
hb_itemRelease( pValue );
pKey = hb_itemPutC( NULL, "alias" );
pValue = hb_itemPutC( NULL, hb_clsName( uiClass ) );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
hb_itemRelease( pClass );
return NULL;
}
hb_itemRelease( pKey );
hb_itemRelease( pValue );
if( hbamf_is_cls_externalizable( uiClass ) )
{
pKey = hb_itemPutC( NULL, "EXTERNALIZABLE_CLASS_DEF" );
pValue = hb_itemNew( NULL );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
hb_itemRelease( pClass );
return NULL;
}
hb_itemRelease( pKey );
hb_itemRelease( pValue );
}
/* if we ever want to store the objects dynamically */
#if 0
hb_itemPutC( pKey, "DYNAMIC_CLASS_DEF" );
hb_itemNew( pValue );
if( ! hb_hashAdd( pClass, pKey, pValue ) )
{
hb_itemRelease( pKey );
hb_itemRelease( pValue );
hb_itemRelease( pClass );
return NULL;
}
#endif
return pClass;
}
static HB_BOOL amf3_encode_object( amfContext * context, PHB_ITEM pItem )
{
HB_BOOL result;
PHB_ITEM pClass;
/* serialize emulated ActionScript dynamic object */
if( strcmp( hb_clsName( hb_objGetClass( pItem ) ), "AMF_OBJ" ) == 0 )
{
PHB_ITEM pAnonHash = hb_itemNew( NULL );
if( amf3_serialize_class_def( context, NULL ) == 0 )
{
hb_itemRelease( pAnonHash );
return HB_FALSE;
}
hb_arrayGet( pItem, OBJAMF_VAR_HASH, pAnonHash );
result = amf3_encode_dynamic_dict( context, pAnonHash );
hb_itemRelease( pAnonHash );
return result;
}
pClass = class_def_from_class( /* context, */ pItem );
if( ! pClass )
return HB_FALSE;
if( ! amf3_serialize_class_def( context, pClass ) )
{
hb_itemRelease( pClass );
return HB_FALSE;
}
if( hb_hashGetCItemPos( pClass, "EXTERNALIZABLE_CLASS_DEF" ) != 0 )
{
PHB_ITEM pStr;
PHB_ITEM pRetCopy = hb_itemNew( NULL );
PHB_ITEM pObject;
if( pItem == hb_stackReturnItem() )
pObject = pRetCopy;
else
pObject = pItem;
hb_itemMove( pRetCopy, hb_stackReturnItem() );
pStr = hb_objSendMsg( pObject, "WRITEEXTERNAL", 0 );
if( ! pStr )
{
hb_itemMove( hb_stackReturnItem(), pRetCopy );
hb_itemRelease( pRetCopy );
hb_itemRelease( pClass );
return HB_FALSE;
}
result = amf3_encode_byte_array( context, pStr ) != 0;
hb_itemRelease( pClass );
hb_itemMove( hb_stackReturnItem(), pRetCopy );
hb_itemRelease( pRetCopy );
return result;
}
hb_itemRelease( pClass );
return HB_FALSE;
#if 0
/* Encoding attributes of class mapped objects is still a TODO, so the Python code is left for reference. */
/* Encode static attrs */
PyObject * static_attrs = static_attr_vals_from_class_def( context, class_def, value );
if( ! static_attrs )
{
Py_DECREF( class_def );
return 0;
}
Py_ssize_t static_attr_len = PySequence_Size( static_attrs );
if( static_attr_len == -1 )
{
Py_DECREF( class_def );
Py_DECREF( static_attrs );
return 0;
}
int i;
for( i = 0; i < static_attr_len; i++ )
{
PyObject * static_attr = PySequence_GetItem( static_attrs, i );
if( ! static_attr )
{
Py_DECREF( static_attrs );
Py_DECREF( class_def );
return 0;
}
int result = encode_AMF3( context, static_attr );
Py_DECREF( static_attr );
if( ! result )
{
Py_DECREF( static_attrs );
Py_DECREF( class_def );
return 0;
}
}
Py_DECREF( static_attrs );
/* Encode dynamic attrs */
if( PyObject_HasAttrString( class_def, "DYNAMIC_CLASS_DEF" ) )
{
PyObject * dynamic_attrs = dynamic_attrs_from_class_def( context, class_def, value );
if( ! dynamic_attrs )
{
Py_DECREF( class_def );
return 0;
}
int result = encode_dynamic_dict_AMF3( context, dynamic_attrs );
Py_DECREF( dynamic_attrs );
if( ! result )
{
Py_DECREF( class_def );
return 0;
}
}
Py_DECREF( class_def );
return 1;
#endif
}
static HB_BOOL amf3_serialize_object( amfContext * context, PHB_ITEM pItem )
{
int result;
if( strcmp( hb_clsName( hb_objGetClass( pItem ) ), "AMF_RAW" ) == 0 )
{
PHB_ITEM pStr = hb_itemNew( NULL );
hb_arrayGet( pItem, 1, pStr );
context->position--;
result = amf3_encode_byte_array( context, pStr ) != 0;
hb_itemRelease( pStr );
return result;
}
if( context->use_refs )
{
if( hb_arrayRefs( pItem ) > 1 )
{
result = amf3_encode_reference( context, context->obj_ref, pItem, 0 );
if( result > -1 )
return result;
}
else
context->objnref_count++;
}
return amf3_encode_object( context, pItem );
}
static void amf3_conversion_out( amfContext * context, PHB_ITEM pItem )
{
PHB_ITEM pRetCopy = hb_itemNew( NULL );
PHB_ITEM pOuterContext = hb_itemPutPtr( NULL, context );
PHB_SYMB pSym = hb_itemGetSymbol( context->conv_function );
if( pItem == hb_stackReturnItem() )
{
hb_vmPushSymbol( pSym );
hb_vmPushNil();
hb_vmPush( pItem );
hb_vmPush( pOuterContext );
hb_vmDo( 2 );
}
else
{
hb_itemMove( pRetCopy, hb_stackReturnItem() );
hb_vmPushSymbol( pSym );
hb_vmPushNil();
hb_vmPush( pItem );
hb_vmPush( pOuterContext );
hb_vmDo( 2 );
hb_itemMove( pItem, hb_stackReturnItem() );
hb_itemMove( hb_stackReturnItem(), pRetCopy );
}
hb_itemRelease( pOuterContext );
hb_itemRelease( pRetCopy );
}
static HB_BOOL amf3_encode( amfContext * context, PHB_ITEM pItem )
{
HB_BOOL result = HB_FALSE;
if( context->conv_function )
amf3_conversion_out( context, pItem );
if( HB_IS_NIL( pItem ) )
result = amf3_encode_nil( context );
else if( HB_IS_LOGICAL( pItem ) )
result = amf3_encode_bool( context, pItem );
else if( HB_IS_INTEGER( pItem ) || HB_IS_LONG( pItem ) )
result = amf3_write_int( context, pItem );
else if( HB_IS_DOUBLE( pItem ) )
result = amfX_write_double( context, pItem );
else if( HB_IS_STRING( pItem ) || HB_IS_MEMO( pItem ) )
{
if( context->encode_ba )
{
if( ! writeByte( context, BYTE_ARRAY_TYPE ) )
result = HB_FALSE;
else
result = amf3_serialize_byte_array( context, pItem ) != 0;
}
else
{
if( ! writeByte( context, STRING_TYPE ) )
result = HB_FALSE;
else
result = amf3_serialize_string( context, pItem );
}
}
else if( HB_IS_DATETIME( pItem ) )
{
if( ! writeByte( context, DATE_TYPE ) )
result = HB_FALSE;
else
result = amf3_serialize_date( context, pItem );
}
else if( HB_IS_OBJECT( pItem ) )
{
if( ! writeByte( context, OBJECT_TYPE ) )
result = HB_FALSE;
else
result = amf3_serialize_object( context, pItem );
}
else if( HB_IS_ARRAY( pItem ) )
{
if( ! writeByte( context, ARRAY_TYPE ) )
result = HB_FALSE;
else
result = amf3_serialize_array( context, pItem );
}
else if( HB_IS_HASH( pItem ) )
result = amf3_write_hash( context, pItem );
return result;
}
static amfContext * context_setup( PHB_ITEM pFuncSym, HB_BOOL use_refs, HB_BOOL str_rtrim, amfContext * outer_context )
{
amfContext * context;
context = ( amfContext * ) hb_xgrab( sizeof( amfContext ) );
memset( context, 0, sizeof( amfContext ) );
context->cBuf = ( char * ) hb_xgrab( sizeof( char ) * 8 );
context->position = 0;
context->length = sizeof( char ) * 8;
context->str_rtrim = str_rtrim;
context->use_refs = use_refs;
if( use_refs )
{
if( outer_context && outer_context->use_refs )
{
context->obj_ref = outer_context->obj_ref;
context->str_ref = outer_context->str_ref;
context->class_ref = outer_context->class_ref;
context->objnref_count = outer_context->objnref_count;
}
else
{
context->obj_ref = hb_hashNew( NULL );
context->str_ref = hb_hashNew( NULL );
context->class_ref = hb_hashNew( NULL );
context->objnref_count = 0;
}
}
else
{
context->obj_ref = NULL;
context->str_ref = NULL;
context->class_ref = NULL;
}
context->conv_function = pFuncSym;
/* "strstr" is another optional idea of catching similar strings,
key in this hash is not the pointer to C char, but the string
itself and the value is id of the reference */
context->use_strstr = HB_TRUE;
if( outer_context )
{
context->strstr_count = outer_context->strstr_count;
if( outer_context->use_strstr )
context->strstr_ref = outer_context->strstr_ref;
else
context->strstr_ref = hb_hashNew( NULL );
if( ! context->use_refs && outer_context->use_refs )
context->strstr_count += hb_hashLen( outer_context->str_ref );
}
else
{
context->strstr_count = 0;
context->strstr_ref = hb_hashNew( NULL );
}
return context;
}
static void context_release( amfContext * context, amfContext * outer_context )
{
if( outer_context && outer_context->use_refs )
outer_context->objnref_count = context->objnref_count;
else if( context->use_refs )
{
hb_itemRelease( context->obj_ref );
hb_itemRelease( context->str_ref );
hb_itemRelease( context->class_ref );
}
if( context->use_strstr )
{
if( outer_context )
{
if( ! context->use_refs && outer_context->use_refs )
context->strstr_count -= hb_hashLen( outer_context->str_ref );
outer_context->strstr_count = context->strstr_count;
if( ! outer_context->strstr_ref )
hb_itemRelease( context->strstr_ref );
}
else
hb_itemRelease( context->strstr_ref );
}
}
HB_FUNC( AMF3_FROMWA )
{
PHB_ITEM pWhile = hb_param( 1, HB_IT_BLOCK );
PHB_ITEM pFor = hb_param( 2, HB_IT_BLOCK );
PHB_ITEM pFields = hb_param( 3, HB_IT_ARRAY );
HB_ULONG nCount = hb_parnldef( 4, 0 );
HB_BOOL str_rtrim = hb_parldef( 5, HB_TRUE );
HB_USHORT nPkg = ( HB_USHORT ) hb_parnidef( 6, 0 );
amfContext * outer_context = ( amfContext * ) hb_parptr( 7 );
DBORDERINFO pInfo;
int iOrd;
HB_USHORT uiFields;
HB_ULONG uiRecCount = 0;
HB_ULONG uiRecNo = 0;
HB_BOOL bNoFieldPassed = ( pFields == NULL || hb_arrayLen( pFields ) == 0 );
HB_BOOL bEof = HB_FALSE;
AREAP pArea = ( AREAP ) hb_rddGetCurrentWorkAreaPointer();
PHB_ITEM pItem;
HB_USHORT uiFieldCopy = 0; /* GCC knows better (warns) */
HB_USHORT uiIter;
amfContext * context;
HB_BOOL bPredictLen = ( ! pWhile && ! pFor );
HB_BOOL bAsArray = ! nPkg;
PHB_ITEM pFieldNames = NULL; /* again GCC knows better */
PHB_ITEM pField;
if( pArea )
{
memset( &pInfo, 0, sizeof( pInfo ) );
pInfo.itmResult = hb_itemPutNI( NULL, 0 );
SELF_ORDINFO( pArea, DBOI_NUMBER, &pInfo );
iOrd = hb_itemGetNI( pInfo.itmResult );
if( iOrd > 0 )
{
SELF_ORDINFO( pArea, DBOI_KEYCOUNT, &pInfo );
uiRecCount = hb_itemGetNL( pInfo.itmResult );
SELF_ORDINFO( pArea, DBOI_POSITION, &pInfo );
uiRecNo = hb_itemGetNL( pInfo.itmResult );
hb_itemRelease( pInfo.itmResult );
}
else
{
hb_itemRelease( pInfo.itmResult );
if( SELF_RECCOUNT( pArea, &uiRecCount ) != HB_SUCCESS )
return;
if( SELF_RECNO( pArea, &uiRecNo ) != HB_SUCCESS )
return;
}
pItem = hb_itemNew( NULL );
context = context_setup( NULL, HB_FALSE, str_rtrim, outer_context );
if( bPredictLen )
{
if( nCount == 0 || uiRecNo + nCount > uiRecCount )
nCount = uiRecCount - uiRecNo + 1;
}
else
{
uiRecCount = 0;
while( ( nCount == 0 || uiRecCount < nCount ) &&
( ! pWhile || hb_itemGetL( hb_vmEvalBlock( pWhile ) ) ) )
{
if( SELF_EOF( pArea, &bEof ) != HB_SUCCESS )
break;
if( bEof )
break;
if( ! pFor || hb_itemGetL( hb_vmEvalBlock( pFor ) ) )
uiRecCount++;
if( SELF_SKIP( pArea, 1 ) != HB_SUCCESS )
break;
}
nCount = uiRecCount;
if( iOrd > 0 )
{
memset( &pInfo, 0, sizeof( pInfo ) );
pInfo.itmNewVal = hb_itemPutNL( NULL, uiRecNo );
pInfo.itmResult = hb_itemPutL( NULL, HB_FALSE );
SELF_ORDINFO( pArea, DBOI_POSITION, &pInfo );
hb_itemRelease( pInfo.itmNewVal );
hb_itemRelease( pInfo.itmResult );
}
else
SELF_GOTO( pArea, uiRecNo );
}
/* TODO: should be if( writeByte() ), before we make a variant that operates on streams directly */
writeByte( context, ARRAY_TYPE );
amf3_encode_int( context, ( ( int ) nCount << 1 ) | REFERENCE_BIT );
writeByte( context, NULL_TYPE );
SELF_FIELDCOUNT( pArea, &uiFields );
if( ! bNoFieldPassed )
{
uiFieldCopy = ( HB_USHORT ) hb_arrayLen( pFields );
for( uiIter = 1; uiIter <= uiFieldCopy; uiIter++ )
{
const char * szFieldName = hb_arrayGetCPtr( pFields, uiIter );
if( szFieldName )
{
int iPos = hb_rddFieldIndex( pArea, szFieldName );
if( iPos )
{
PHB_ITEM pFieldNum = hb_itemPutNI( NULL, iPos );
hb_itemArrayPut( pFields, uiIter, pFieldNum );
hb_itemRelease( pFieldNum );
continue;
}
}
if( hb_arrayDel( pFields, uiIter ) )
{
hb_arraySize( pFields, hb_arrayLen( pFields ) - 1 );
uiIter--;
uiFieldCopy--;
}
}
}
if( ! bAsArray )
{
pFieldNames = hb_itemNew( NULL );
if( bNoFieldPassed )
{
hb_arrayNew( pFieldNames, uiFields );
for( uiIter = 1; uiIter <= uiFields; uiIter++ )
{
char * szName = ( char * ) hb_xgrab( pArea->uiMaxFieldNameLength + 1 );
pField = hb_itemNew( NULL );
szName[ 0 ] = '\0';
SELF_FIELDNAME( pArea, uiIter, szName );
hb_itemPutCPtr( pField, szName );
hb_arraySet( pFieldNames, uiIter, pField );
hb_itemRelease( pField );
}
}
else
{
hb_arrayNew( pFieldNames, uiFieldCopy );
for( uiIter = 1; uiIter <= uiFieldCopy; uiIter++ )
{
char * szName = ( char * ) hb_xgrab( pArea->uiMaxFieldNameLength + 1 );
pField = hb_itemNew( NULL );
szName[ 0 ] = '\0';
SELF_FIELDNAME( pArea, ( HB_USHORT ) hb_itemGetNI( hb_arrayGetItemPtr( pFields, uiIter ) ), szName );
hb_itemPutCPtr( pField, szName );
hb_arraySet( pFieldNames, uiIter, pField );
hb_itemRelease( pField );
}
}
}
uiRecCount = 0;
while( ( nCount == 0 || uiRecCount <= nCount ) &&
( ! pWhile || hb_itemGetL( hb_vmEvalBlock( pWhile ) ) ) )
{
if( SELF_EOF( pArea, &bEof ) != HB_SUCCESS )
break;
if( bEof )
break;
if( ! pFor || hb_itemGetL( hb_vmEvalBlock( pFor ) ) )
{
if( bAsArray )
{
writeByte( context, ARRAY_TYPE );
if( bNoFieldPassed )
{
amf3_encode_int( context, ( ( int ) uiFields << 1 ) | REFERENCE_BIT );
writeByte( context, NULL_TYPE );
for( uiIter = 1; uiIter <= uiFields; uiIter++ )
{
SELF_GETVALUE( pArea, uiIter, pItem );
amf3_encode( context, pItem );
}
}
else
{
amf3_encode_int( context, ( ( int ) uiFieldCopy << 1 ) | REFERENCE_BIT );
writeByte( context, NULL_TYPE );
for( uiIter = 1; uiIter <= uiFieldCopy; uiIter++ )
{
SELF_GETVALUE( pArea, ( HB_USHORT ) hb_itemGetNI( hb_arrayGetItemPtr( pFields, uiIter ) ) /* hb_arrayGetNI( pFields, uiIter ) */, pItem );
amf3_encode( context, pItem );
}
}
}
else
{
PHB_ITEM pValue = hb_itemNew( NULL );
writeByte( context, OBJECT_TYPE );
#if 0
amf3_encode_int( context, ( ( int ) 1 ) << 1 | REFERENCE_BIT );
#endif
writeByte( context, DYNAMIC );
writeByte( context, EMPTY_STRING_TYPE );
if( bNoFieldPassed )
{
for( uiIter = 1; uiIter <= uiFields; uiIter++ )
{
SELF_GETVALUE( pArea, uiIter, pValue );
amf3_serialize_string( context, hb_arrayGetItemPtr( pFieldNames, uiIter ) );
amf3_encode( context, pValue );
}
}
else
{
for( uiIter = 1; uiIter <= uiFieldCopy; uiIter++ )
{
SELF_GETVALUE( pArea, ( HB_USHORT ) hb_itemGetNI( hb_arrayGetItemPtr( pFields, uiIter ) ), pValue );
amf3_serialize_string( context, hb_arrayGetItemPtr( pFieldNames, uiIter ) );
amf3_encode( context, pValue );
}
}
hb_itemRelease( pValue );
writeByte( context, EMPTY_STRING_TYPE );
}
uiRecCount++;
}
if( SELF_SKIP( pArea, 1 ) != HB_SUCCESS )
break;
}
hb_itemRelease( pItem );
if( ! bAsArray )
hb_itemRelease( pFieldNames );
context->cBuf = ( char * ) hb_xrealloc( context->cBuf, sizeof( char ) * context->position + 1 );
hb_retclen_buffer( context->cBuf, context->position );
context_release( context, outer_context );
hb_xfree( context );
}
else
hb_errRT_DBCMD( EG_NOTABLE, EDBCMD_NOTABLE, NULL, HB_ERR_FUNCNAME );
}
HB_FUNC( AMF3_ENCODE )
{
PHB_ITEM pItem = hb_param( 1, HB_IT_ANY );
PHB_ITEM pFuncSym = hb_param( 2, HB_IT_SYMBOL );
HB_BOOL lBA = hb_parldef( 3, HB_FALSE );
HB_BOOL lRetval;
amfContext * context;
if( ! pItem )
return;
context = ( amfContext * ) hb_xgrab( sizeof( amfContext ) );
memset( context, 0, sizeof( amfContext ) );
context->cBuf = ( char * ) hb_xgrab( sizeof( char ) * 8 );
context->position = 0;
context->length = sizeof( char ) * 8;
context->str_rtrim = HB_FALSE;
context->obj_ref = hb_hashNew( NULL );
context->str_ref = hb_hashNew( NULL );
context->class_ref = hb_hashNew( NULL );
context->use_refs = HB_TRUE;
context->conv_function = pFuncSym;
context->encode_ba = lBA;
context->objnref_count = 0;
/* "strstr" is another optional idea of catching similar strings,
key in this hash is not the pointer to C char, but the string
itself and the value is id of the reference */
context->use_strstr = HB_TRUE;
context->strstr_count = 0;
context->strstr_ref = hb_hashNew( NULL );
lRetval = amf3_encode( context, pItem );
if( context->use_refs )
{
hb_itemRelease( context->obj_ref );
hb_itemRelease( context->str_ref );
hb_itemRelease( context->class_ref );
}
if( context->use_strstr )
hb_itemRelease( context->strstr_ref );
#if 0
if( context->conv_function )
hb_itemRelease( context->conv_function );
#endif
if( ! lRetval )
{
hb_xfree( context->cBuf );
hb_xfree( context );
return;
}
context->cBuf = ( char * ) hb_xrealloc( context->cBuf, sizeof( char ) * context->position + 1 );
hb_retclen_buffer( context->cBuf, context->position );
hb_xfree( context );
}