diff --git a/harbour/ChangeLog b/harbour/ChangeLog index f2da33c397..cc2ddacc95 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -16,6 +16,35 @@ The license applies to all entries newer than 2009-04-28. */ +2012-06-13 16:16 UTC+0200 Viktor Szakats (harbour syenar.net) + * contrib/hbplist + + contrib/hbamf + + contrib/hbamf/amf.h + + contrib/hbamf/amfdec.c + + contrib/hbamf/amfenc.c + + contrib/hbamf/amfstdio.c + + contrib/hbamf/hbamf.hbc + + contrib/hbamf/hbamf.hbp + + contrib/hbamf/hbamf.hbx + + contrib/hbamf/hbcls.c + + contrib/hbamf/hbref.c + + contrib/hbamf/issues.txt + + contrib/hbamf/readme.txt + + contrib/hbamf/tests + + contrib/hbamf/tests/hbmk.hbm + + contrib/hbamf/tests/tstendin.prg + + added AMF3 encoder/decoder + work of Aleksander Czajczynski and Ilina Stoilkovska + (with minor additions of mine: build file + cleanup, some C level formatting/comment cleanup, C++ + fixes) + ; NOTE: this is only the pure AMF3 encoder/decoder, + and doesn't include websocket layers + ; TODO: adding license + + * utils/hbmk2/hbmk2.prg + * minor in comments + 2012-06-13 15:44 UTC+0200 Viktor Szakats (harbour syenar.net) * contrib/hbrun/hbrun.prg * contrib/hbrun/plugins.prg diff --git a/harbour/contrib/hbamf/amf.h b/harbour/contrib/hbamf/amf.h new file mode 100644 index 0000000000..a00a292f1a --- /dev/null +++ b/harbour/contrib/hbamf/amf.h @@ -0,0 +1,67 @@ +/* + * $Id$ + */ + +/******* +* +* amf.h +* +* Aleksander Czajczynski 2011-2012 +* +* amf.h - AMF3 headers +* +* Based on a AmFast C library for Python by Dave Thompson +* +********/ + +#ifndef __HBAMF_H +#define __HBAMF_H + +/* Things used by both the encoder and the decoder. */ + +/* Use to test for endianness at run time. */ +/* #define is_bigendian() ((*(char*)&endian_test) == 0) */ + +/* ---- Harbour support */ + +#define OBJAMF_VER 0 + +#define OBJAMF_VAR_COUNT 5 +#define OBJAMF_VAR_VER 1 +#define OBJAMF_VAR_NAME 2 +#define OBJAMF_VAR_HASH 3 + +/* ---- AMF3 */ + +/* Valid AMF3 integer range */ +#define MIN_INT -268435457 +#define MAX_INT 268435456 + +/* Reference bit */ +#define REFERENCE_BIT 0x01 + +/* Empty string */ +#define EMPTY_STRING_TYPE 0x01 + +/* Object Headers */ +#define STATIC 0x03 +#define DYNAMIC 0x0B +#define EXTERNALIZABLE 0x07 + +/* Type markers */ +#define UNDEFINED_TYPE 0x00 +#define NULL_TYPE 0x01 +#define FALSE_TYPE 0x02 +#define TRUE_TYPE 0x03 +#define INT_TYPE 0x04 +#define DOUBLE_TYPE 0x05 +#define STRING_TYPE 0x06 +#define XML_DOC_TYPE 0x07 +#define DATE_TYPE 0x08 +#define ARRAY_TYPE 0x09 +#define OBJECT_TYPE 0x0A +#define XML_TYPE 0x0B +#define BYTE_ARRAY_TYPE 0x0C +#define AMF3_AMF0 0x11 + +#endif diff --git a/harbour/contrib/hbamf/amfdec.c b/harbour/contrib/hbamf/amfdec.c new file mode 100644 index 0000000000..87d93f976a --- /dev/null +++ b/harbour/contrib/hbamf/amfdec.c @@ -0,0 +1,1249 @@ +/* + * $Id$ + */ + +/******* + * + * amfdecode.c by Aleksander Czajczynski 2011-2012 + * + * amfdecode.c - Decoding AMF3 to Harbour items + * + * 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 "amf.h" + +#include "hbdate.h" +#include "hbmath.h" + +#include "hbvm.h" + +typedef struct +{ + const char * cBuf; + int position; + int length; + PHB_ITEM obj_ref; + PHB_ITEM str_ref; + PHB_ITEM class_ref; + PHB_ITEM conv_function; +} amfContext; + +static HB_BOOL amf3_getItem( amfContext * context, PHB_ITEM pItem ); +extern HB_BOOL is_cls_externalizable( HB_USHORT uiClass ); +extern PHB_ITEM cls_externalizable_instance( PHB_ITEM pClassFuncStr ); + +static char * readByte( amfContext * context ) +{ + int new_position = context->position + 1; + char * byte_ref; + + if( new_position < 0 || new_position > context->length ) + return NULL; + + byte_ref = ( char * ) ( context->cBuf + context->position ); + context->position = new_position; + return byte_ref; +} + +static char * readBytes( amfContext * context, int len ) +{ + int new_position = context->position + len; + char * result; + + if( new_position < 0 || new_position > context->length ) + return NULL; + + result = ( char * ) ( context->cBuf + context->position ); + context->position = new_position; + return result; +} + +static HB_BOOL amfX_decode_double( amfContext * context, double * val ) +{ + #ifndef HB_BIG_ENDIAN + char c_val[ 8 ]; + #endif + const char * bytes = readBytes( context, 8 ); + if( ! bytes ) + return HB_FALSE; + + /* + * Put bytes from byte array into double + * TOFIX: does this aligment work on any platform? + + union aligned { + double d_val; + char c_val[8]; + } d; + + */ + + /* AMF transmitted double is always in network byte order */ +#ifdef HB_BIG_ENDIAN + memcpy( val, bytes, 8 ); +#else + /* Flip endianness */ + c_val[ 0 ] = bytes[ 7 ]; + c_val[ 1 ] = bytes[ 6 ]; + c_val[ 2 ] = bytes[ 5 ]; + c_val[ 3 ] = bytes[ 4 ]; + c_val[ 4 ] = bytes[ 3 ]; + c_val[ 5 ] = bytes[ 2 ]; + c_val[ 6 ] = bytes[ 1 ]; + c_val[ 7 ] = bytes[ 0 ]; + memcpy( val, c_val, 8 ); +#endif + + return HB_TRUE; +} + +static HB_BOOL amf3_decode_int( amfContext * context, int * iVal ) +{ + int result = 0; + int byte_cnt = 0; + char * byte_ref; + char byte; + + byte_ref = readByte( context ); + if( ! byte_ref ) + return HB_FALSE; + + byte = byte_ref[ 0 ]; + + /* If 0x80 is set, int includes the next byte, up to 4 total bytes */ + while( ( byte & 0x80 ) && ( byte_cnt < 3 ) ) + { + result <<= 7; + result |= byte & 0x7F; + byte_ref = readByte( context ); + if( ! byte_ref ) + return HB_FALSE; + byte = byte_ref[ 0 ]; + byte_cnt++; + } + + /* shift bits in last byte */ + if( byte_cnt < 3 ) + { + result <<= 7; /* shift by 7, since the 1st bit is reserved for next byte flag */ + result |= byte & 0x7F; + } + else + { + result <<= 8; /* shift by 8, since no further bytes are possible and 1st bit is not used for flag. */ + result |= byte & 0xff; + } + + /* Move sign bit, since we're converting 29bit->32bit */ + if( result & 0x10000000 ) + result -= 0x20000000; + + *iVal = result; + return HB_TRUE; +} + +#if 0 + +static HB_BOOL amf3_decode_reference( PHB_ITEM pHash, int val, PHB_ITEM pRefItem ) +{ + /* Check for index reference */ + if( (val & REFERENCE_BIT) == 0 ) + { + PHB_ITEM pKey = hb_itemNew( NULL ); + hb_itemPutNI( pKey, val >> 1 ); + + pRefItem = hb_hashGetItemPtr( pHash, pKey, 0 ); + + hb_itemRelease( pKey ); + return HB_TRUE; + } + + return HB_FALSE; +} + +#endif + +static PHB_ITEM amf3_decode_reference( PHB_ITEM pHash, int val ) +{ + /* Check for index reference */ + if( ( val & REFERENCE_BIT ) == 0 ) + { + PHB_ITEM pKey = hb_itemNew( NULL ); + PHB_ITEM pRefItem; + hb_itemPutNI( pKey, val >> 1 ); + + pRefItem = hb_hashGetItemPtr( pHash, pKey, 0 ); + if( ! pRefItem ) + hb_itemPutL( pRefItem, HB_FALSE ); + + hb_itemRelease( pKey ); + return pRefItem; + } + + return NULL; +} + +static void amf3_add_reference( PHB_ITEM pHash, PHB_ITEM pItem ) +{ + HB_SIZE iRef = hb_hashLen( pHash ); + PHB_ITEM pKey = hb_itemNew( NULL ); + + /* the reference id in AMF starts from 0, and increase + sequentially - this means we can also use an array, + not hash for the purpose */ + + hb_itemPutNI( pKey, /* ++ first one was 0 */ iRef ); + hb_hashAdd( pHash, pKey, pItem ); + + hb_itemRelease( pKey ); +} + +static HB_BOOL amfX_decode_string( amfContext * context, PHB_ITEM pItem, unsigned int string_size ) +{ + const char * str = readBytes( context, string_size ); + + if( ! str ) + return HB_FALSE; + + hb_itemPutStrLenUTF8( pItem, str, string_size ); + return HB_TRUE; +} + +static HB_BOOL amf3_deserialize_string( amfContext * context, PHB_ITEM pItem ) +{ + int header; + int * header_p = &header; + PHB_ITEM pRefItem = NULL; + PHB_ITEM pHash = context->str_ref; + + if( ! amf3_decode_int( context, header_p ) ) + return HB_FALSE; + + /* Check for null string */ + if( header == EMPTY_STRING_TYPE ) + { + hb_itemPutC( pItem, NULL ); + return HB_TRUE; + } + + /* Check for reference */ + pRefItem = amf3_decode_reference( pHash, header ); + if( pRefItem ) + { + if( HB_IS_LOGICAL( pRefItem ) ) + { + /* Logical value means a problem getting reference from the hash */ + hb_itemRelease( pRefItem ); + return HB_FALSE; + } + /* Copies string from reference hash pRefItem -> pItem */ + hb_itemCopy( pItem, pRefItem ); + return HB_TRUE; + } + + /* No reference found */ + if( ! amfX_decode_string( context, pItem, header >> 1 ) ) + return HB_FALSE; + + /* Adds reference */ + amf3_add_reference( pHash, pItem ); + + return HB_TRUE; +} + +/* Add the dynamic attributes of an encoded obj to a dict. */ +static HB_BOOL amf3_decode_dynamic_dict( amfContext * context, PHB_ITEM pItem ) +{ + PHB_ITEM pKey; + PHB_ITEM pValue; + HB_BOOL result; + + while( 1 ) + { + pKey = hb_itemNew( NULL ); + if( ! amf3_deserialize_string( context, pKey ) ) + { + hb_itemRelease( pKey ); + return HB_FALSE; + } + + if( hb_itemGetCLen( pKey ) == 0 ) + { + /* Empty string marks end of name/value pairs */ + hb_itemRelease( pKey ); + return HB_TRUE; + } + + pValue = hb_itemNew( NULL ); + if( ! amf3_getItem( context, pValue ) ) + { + hb_itemRelease( pKey ); + hb_itemRelease( pValue ); + return HB_FALSE; + } + + result = hb_hashAdd( pItem, pKey, pValue ); + hb_itemRelease( pKey ); + hb_itemRelease( pValue ); + if( ! result ) + return HB_FALSE; + } +} + +/* Populate an array with vals from the buffer. */ +static HB_BOOL decode_dynamic_array_AMF3( amfContext * context, PHB_ITEM pItem, int array_len, HB_BOOL dict ) +{ + int i; + HB_BOOL lRet; + + if( dict ) + { + /* Object is a dict, set item index as key. */ + for( i = 0; i < array_len; i++ ) + { + PHB_ITEM pValue = hb_itemNew( NULL ); + lRet = HB_FALSE; + + if( amf3_getItem( context, pValue ) ) + { + PHB_ITEM pKey = hb_itemNew( NULL ); + hb_itemPutNI( pKey, i ); + + if( hb_hashAdd( pItem, pKey, pValue ) ) + lRet = HB_TRUE; + + hb_itemRelease( pKey ); + } + + hb_itemRelease( pValue ); + + if( ! lRet ) + return HB_FALSE; + } + } + else + { + /* Standard array. */ + for( i = 0; i < array_len; i++ ) + { + PHB_ITEM pValue = hb_itemNew( NULL ); + lRet = HB_FALSE; + + if( amf3_getItem( context, pValue ) ) + { + if( hb_arraySet( pItem, i + 1, pValue ) ) + lRet = HB_TRUE; + } + + hb_itemRelease( pValue ); + + if( ! lRet ) + return HB_FALSE; + } + } + + return HB_TRUE; +} + +static HB_BOOL amf3_deserialize_array( amfContext * context, PHB_ITEM pItem, HB_BOOL collection ) +{ + int header; + int * header_p = &header; + PHB_ITEM pRefItem = NULL; + PHB_ITEM pHash = context->obj_ref; + int array_len; + HB_BOOL mixed = HB_FALSE; /* if the result will be a Hash with both numbers and strings as keys */ + char * byte_ref; + + if( ! amf3_decode_int( context, header_p ) ) + return HB_FALSE; + + /* Check for reference */ + pRefItem = amf3_decode_reference( pHash, header ); + if( pRefItem ) + { + if( HB_IS_LOGICAL( pRefItem ) ) + { + /* Logical value means a problem getting reference from the hash */ + hb_itemRelease( pRefItem ); + return HB_FALSE; + } + + /* Reference found */ + if( collection ) + { + /* Map ArrayCollection idx to ref, since + it points to the same list. */ + amf3_add_reference( pHash, pRefItem ); + } + /* Copies string from reference hash pRefItem -> pItem */ + hb_itemCopy( pItem, pRefItem ); + return HB_TRUE; + } + + array_len = ( int ) ( header >> 1 ); + /* Original python comment was: + Can't use array_len to create a list of known + length, see ticket #46 + I think that this is not a problem for Harbour */ + + /* Determine if array is mixed (associative) or not */ + mixed = HB_FALSE; + byte_ref = readByte( context ); + if( byte_ref == NULL ) + return HB_FALSE; + + if( byte_ref[ 0 ] == EMPTY_STRING_TYPE ) + { + /* Dense array */ + hb_arrayNew( pItem, array_len ); + } + else + { + context->position--; + hb_hashNew( pItem ); + hb_hashSetFlags( pHash, HB_HASH_KEEPORDER ); + hb_hashPreallocate( pItem, array_len ); + + if( ! amf3_decode_dynamic_dict( context, pItem ) ) + return HB_FALSE; + + mixed = HB_TRUE; + } + + amf3_add_reference( pHash, pItem ); + + /* Originally a python comment. + I don't understand reasons for following, + but let it be like this. + ADD: oh, maybe it's because parsing of + ArrayCollection starts as Object + -- + If this is an ArrayCollection, + we need to add another reference, + so there is one that + points to the array and one that points + to the collection. + */ + + if( collection ) + amf3_add_reference( pHash, pItem ); + + return decode_dynamic_array_AMF3( context, pItem, array_len, mixed ); + + /* return HB_TRUE; */ +} + +/* Decode a date. */ +static HB_BOOL amf3_decode_epoch( amfContext * context, PHB_ITEM pItem ) +{ + double dJulian, dTime; + double epoch_millisecs; + double * epoch_p = &epoch_millisecs; + + if( ! amfX_decode_double( context, epoch_p ) ) + return HB_FALSE; + + /* 210866760000000 unix_epoch milliseconds base in JD */ + dTime = modf( ( 210866760000000 + epoch_millisecs + 0.5 ) / HB_MILLISECS_PER_DAY, &dJulian ); + + hb_itemPutTDT( pItem, ( long ) dJulian, ( long ) ( dTime * HB_MILLISECS_PER_DAY ) ); + + /* TOFIX: why following Harbour function doesn't work out of the box? + C compiler was MSVC 7.1 (Visual Studio 2003) + hb_itemPutTD( pItem, epoch_millisecs + 210866803200000 ); + */ + + return HB_TRUE; +} + +/* Deserialize date. */ +static HB_BOOL amf3_deserialize_date( amfContext * context, PHB_ITEM pItem ) +{ + int header; + int * header_p = &header; + PHB_ITEM pRefItem = NULL; + PHB_ITEM pHash = context->obj_ref; + + if( ! amf3_decode_int( context, header_p ) ) + return HB_FALSE; + + /* Check for reference */ + pRefItem = amf3_decode_reference( pHash, header ); + if( pRefItem ) + { + if( HB_IS_LOGICAL( pRefItem ) ) + { + /* Logical value means a problem getting reference from the hash */ + hb_itemRelease( pRefItem ); + return HB_FALSE; + } + /* Copies date from reference hash pRefItem -> pItem */ + hb_itemCopy( pItem, pRefItem ); + return HB_TRUE; + } + + if( ! amf3_decode_epoch( context, pItem ) ) + return HB_FALSE; + + /* Add reference */ + amf3_add_reference( pHash, pItem ); + + return HB_TRUE; +} + +/* Decode a byte array. */ +static HB_BOOL amf3_decode_byte_array( amfContext * context, PHB_ITEM pItem, int byte_len ) +{ + const char * str = readBytes( context, byte_len ); + + if( ! str ) + return HB_FALSE; + + hb_itemPutStrLen( pItem, hb_vmCDP(), str, byte_len ); + return HB_TRUE; +} + +/* Deserialize a byte array. */ +static HB_BOOL amf3_deserialize_byte_array( amfContext * context, PHB_ITEM pItem ) +{ + int header; + int * header_p = &header; + PHB_ITEM pRefItem = NULL; + PHB_ITEM pHash = context->obj_ref; + + if( ! amf3_decode_int( context, header_p ) ) + return HB_FALSE; + + /* Check for reference */ + pRefItem = amf3_decode_reference( pHash, header ); + if( pRefItem ) + { + if( HB_IS_LOGICAL( pRefItem ) ) + { + /* Logical value means a problem getting reference from the hash */ + hb_itemRelease( pRefItem ); + return HB_FALSE; + } + /* Copies ByteArray (string) from reference hash pRefItem -> pItem */ + hb_itemCopy( pItem, pRefItem ); + return HB_TRUE; + } + + if( ! amf3_decode_byte_array( context, pItem, header >> 1 ) ) + return HB_FALSE; + + /* Add reference */ + amf3_add_reference( pHash, pItem ); + + return HB_TRUE; +} + +/* Get an object's class def - nearly a copy/paste from decoder */ +static PHB_ITEM class_def_from_classname( /* amfContext * context, */ PHB_ITEM pClassName ) +{ + HB_USHORT uiClass; + PHB_ITEM pClass; + PHB_ITEM pKey; + PHB_ITEM pValue; + char * pszBuffer = hb_itemGetC( pClassName ); + HB_SIZE nLen = hb_itemGetCLen( pClassName ); + + hb_strUpper( pszBuffer, nLen ); + + /* get Harbour's class id/handle */ + uiClass = hb_clsFindClass( pszBuffer, NULL ); + + hb_strfree( pszBuffer ); + + /* uiClass = hb_objGetClass( pItem ); */ + if( ! uiClass ) + return NULL; + + 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( 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 ); + } + + return pClass; +} + +/* + * Decode a ClassDef. + * + * Header argument is the obj header. + */ +static HB_BOOL amf3_decode_class_def( amfContext * context, PHB_ITEM pClass, int header ) +{ + PHB_ITEM pStrAlias = hb_itemNew( NULL ); + PHB_ITEM pMappedClassDef = NULL; + PHB_ITEM pKey = NULL; + PHB_ITEM pValue = NULL; + PHB_ITEM pAttrs = NULL; + int static_attr_len; + int i; + + if( ! amf3_deserialize_string( context, pStrAlias ) ) + { + hb_itemRelease( pStrAlias ); + return HB_FALSE; + } + + /* If this alias string is empty then the object is anonymous */ + if( hb_itemGetCLen( pStrAlias ) > 0 ) + { + /* Retrieve a ClassDef from a class alias string. */ + pMappedClassDef = class_def_from_classname( pStrAlias ); + if( ! pMappedClassDef ) + { + pMappedClassDef = hb_itemNew( NULL ); + hb_hashNew( pMappedClassDef ); /* empty hash emulation for now */ + } + + /* PyObject_CallMethodObjArgs(context->class_mapper, + context->class_def_name, alias, NULL); */ + } + hb_itemRelease( pStrAlias ); + + /* Create a dict with class def information + specific to this decode context. */ + hb_hashNew( pClass ); + + if( pMappedClassDef ) + { + pKey = hb_itemPutC( NULL, "class_def" ); /* hb_itemNew( NULL ); */ + + if( ! hb_hashAdd( pClass, pKey, pMappedClassDef ) ) + { + hb_itemRelease( pKey ); + hb_itemRelease( pMappedClassDef ); + return HB_FALSE; + } + hb_itemRelease( pKey ); + + pKey = hb_itemPutC( NULL, "EXTERNALIZABLE_CLASS_DEF" ); + if( hb_hashScan( pMappedClassDef, pKey, NULL ) ) + { + /* There is nothing else we need to do + with externalizable ClassDefs */ + hb_itemRelease( pKey ); + hb_itemRelease( pMappedClassDef ); + return HB_TRUE; + } + hb_itemRelease( pKey ); + + /* this item should be now referenced in the hash */ + hb_itemRelease( pMappedClassDef ); + } + + if( ( header & 0x07FFFFFF ) == EXTERNALIZABLE ) + { + /* If the class is externalizable, but the ClassDef isn't, + we have a big problem, because we don't know how to read + the raw bytes. */ + + /* TODO: introduce similar RTE? + PyErr_SetString(amfast_DecodeError, "Encoded class is externalizable, but ClassDef is not."); */ + return HB_FALSE; + } + + + /* Set dynamic flag */ + pKey = hb_itemPutC( NULL, "dynamic" ); + pValue = hb_itemPutL( NULL, ( header & DYNAMIC ) == DYNAMIC ); + if( ! hb_hashAdd( pClass, pKey, pValue ) ) + { + hb_itemRelease( pKey ); + hb_itemRelease( pValue ); + return HB_FALSE; + } + hb_itemRelease( pKey ); + hb_itemRelease( pValue ); + + /* Decode static attr names */ + static_attr_len = ( int ) ( header >> 4 ); + pAttrs = hb_itemNew( NULL ); + hb_arrayNew( pAttrs, static_attr_len ); + + for( i = 0; i < static_attr_len; i++ ) + { + pValue = hb_itemNew( NULL ); + if( ! amf3_deserialize_string( context, pValue ) ) + { + hb_itemRelease( pAttrs ); + hb_itemRelease( pValue ); + return HB_FALSE; + } + + /* steals ref to attr_name */ + if( ! hb_arraySet( pAttrs, i + 1, pValue ) ) + { + hb_itemRelease( pAttrs ); + hb_itemRelease( pValue ); + return HB_FALSE; + } + + hb_itemRelease( pValue ); + } + + /* Set decoded attrs onto ClassDef */ + pKey = hb_itemPutC( NULL, "static_attrs" ); + if( ! hb_hashAdd( pClass, pKey, pAttrs ) ) + { + hb_itemRelease( pKey ); + hb_itemRelease( pAttrs ); + return HB_FALSE; + } + hb_itemRelease( pKey ); + hb_itemRelease( pAttrs ); + + return HB_TRUE; +} + +/* + * Deserialize a ClassDef. + * + * header argument is the parsed obj header. + */ +static HB_BOOL amf3_deserialize_class_def( amfContext * context, PHB_ITEM pClass, int header ) +{ + PHB_ITEM pHash = context->class_ref; + PHB_ITEM pRefItem = NULL; + + /* Check for reference */ + pRefItem = amf3_decode_reference( pHash, header ); + if( pRefItem ) + { + if( HB_IS_LOGICAL( pRefItem ) ) + { + /* Logical value means a problem getting reference from the hash */ + hb_itemRelease( pRefItem ); + return HB_FALSE; + } + /* Copies ClassDef from reference hash pRefItem -> pClass */ + hb_itemCopy( pClass, pRefItem ); + return HB_TRUE; + } + + if( ! amf3_decode_class_def( context, pClass, header ) ) + return HB_FALSE; + + /* Add reference to obj */ + amf3_add_reference( pHash, pClass ); + + return HB_TRUE; +} + +/* Returns a dict with vals from an obj. */ +static HB_BOOL amf3_decode_obj_attrs( amfContext * context, PHB_ITEM pHash, PHB_ITEM pClass ) +{ + PHB_ITEM pArray; + PHB_ITEM pKey; + PHB_ITEM pValue; + HB_BOOL result; + HB_SIZE static_attr_len; + HB_SIZE i; + + /* Put decoded attributes into pHash */ + + /* Decode static attrs */ + pArray = hb_hashGetCItemPtr( pClass, "static_attrs" ); + if( ! pArray ) + return HB_FALSE; + + /* maybe hb_arrayGetItemPtr(?) could be used */ + + static_attr_len = hb_arrayLen( pArray ); + + for( i = 0; i < static_attr_len; i++ ) + { + pValue = hb_itemNew( NULL ); + if( ! amf3_getItem( context, pValue ) ) + { + hb_itemRelease( pValue ); + return HB_FALSE; + } + + pKey = hb_itemNew( NULL ); + if( ! hb_arrayGet( pArray, i + 1, pKey ) ) + { + hb_itemRelease( pValue ); + hb_itemRelease( pKey ); + return HB_FALSE; + } + + result = hb_hashAdd( pHash, pKey, pValue ); + hb_itemRelease( pValue ); + hb_itemRelease( pKey ); + + if( ! result ) + return HB_FALSE; + } + + /* Decode dynamic attrs */ + pValue = hb_hashGetCItemPtr( pClass, "dynamic" ); + if( ! pValue ) + return HB_FALSE; + + if( HB_IS_LOGICAL( pValue ) && hb_itemGetL( pValue ) ) + { + if( ! amf3_decode_dynamic_dict( context, pHash ) ) + return HB_FALSE; + } + + return HB_TRUE; +} + +/* Decode an anonymous obj. */ +static HB_BOOL amf3_decode_anon_obj( amfContext * context, PHB_ITEM pItem, PHB_ITEM pClass ) +{ + PHB_ITEM pAnonHash = hb_itemNew( NULL ); + HB_BOOL result = HB_FALSE; + + /* Original python comment which I don't understand: + We're using merge instead of populating the dict + directly, because we have to setup a reference to the + object before decoding it. ?????? */ + + /* we (Harbourers) are supplying already initialized hash to next func */ + if( hb_arrayGet( pItem, OBJAMF_VAR_HASH, pAnonHash ) ) + result = amf3_decode_obj_attrs( context, pAnonHash, pClass ); + + hb_itemRelease( pAnonHash ); + + return result; +} + +static HB_BOOL amf3_decode_externalizable( amfContext * context, PHB_ITEM pItem ) +{ + const char * position; + PHB_ITEM pRetCopy = hb_itemNew( NULL ); + PHB_ITEM pStr, pPos; + HB_BOOL result = HB_TRUE; + PHB_ITEM pObject; + + if( pItem == hb_stackReturnItem() ) + pObject = pRetCopy; + else + pObject = pItem; + + hb_itemMove( pRetCopy, hb_stackReturnItem() ); + + position = context->cBuf + context->position; + pStr = hb_itemNew( NULL ); + pStr = hb_itemPutCLConst( pStr, position, context->length - context->position ); + pPos = hb_objSendMsg( pObject, "READEXTERNAL", 1, pStr ); + if( HB_IS_INTEGER( pPos ) ) + { + if( ! readBytes( context, hb_itemGetNI( pPos ) ) ) + { + result = HB_FALSE; + } + } + else + { + result = HB_FALSE; + } + + hb_itemMove( hb_stackReturnItem(), pRetCopy ); + + hb_itemRelease( pRetCopy ); + hb_itemRelease( pStr ); + return result; +} + +/* + * Deserialize an obj. + * + * proxy is flag indicating that the obj being deserialized is within an ObjectProxy + */ +static HB_BOOL amf3_deserialize_obj( amfContext * context, PHB_ITEM pItem, HB_BOOL proxy ) +{ + int header; + int * header_p = &header; + PHB_ITEM pRefItem = NULL; + PHB_ITEM pHash = context->obj_ref; + PHB_ITEM pClass = NULL; + PHB_ITEM pMappedClassDef = NULL; + PHB_ITEM pValue = NULL; + int obj_type; /* 0 = anonymous, 1 == externalizable, 2 == typed */ + HB_BOOL result; + + if( ! amf3_decode_int( context, header_p ) ) + return HB_FALSE; + + /* Check for reference */ + pRefItem = amf3_decode_reference( pHash, header ); + if( pRefItem ) + { + if( HB_IS_LOGICAL( pRefItem ) ) + { + /* Logical value means a problem getting reference from the hash */ + hb_itemRelease( pRefItem ); + return HB_FALSE; + } + + /* Reference found */ + if( proxy ) + { + /* Map ObjectProxy idx to ref, since + it points to the same obj. */ + amf3_add_reference( pHash, pRefItem ); + } + + /* Copies ByteArray (string) from reference hash pRefItem -> pItem */ + hb_itemCopy( pItem, pRefItem ); + return HB_TRUE; + } + + pClass = hb_itemNew( NULL ); + if( ! amf3_deserialize_class_def( context, pClass, header ) ) + { + hb_itemRelease( pClass ); + return HB_FALSE; + } + + pMappedClassDef = hb_hashGetCItemPtr( pClass, "class_def" ); + if( ! pMappedClassDef ) + { + /* Anonymous obj. */ + obj_type = 0; + } + else if( hb_hashGetCItemPos( pMappedClassDef, "EXTERNALIZABLE_CLASS_DEF" ) != 0 ) + { + if( hb_hashGetCItemPos( pMappedClassDef, "ARRAY_COLLECTION_CLASS_DEF" ) != 0 ) + { + hb_itemRelease( pClass ); + + if( ! readByte( context ) ) /* Skip array type marker */ + { + return HB_FALSE; + } + + return amf3_deserialize_array( context, pItem, HB_TRUE ); + } + + if( hb_hashGetCItemPos( pMappedClassDef, "OBJECT_PROXY_CLASS_DEF" ) != 0 ) + { + hb_itemRelease( pClass ); + + if( ! readByte( context ) ) /* Skip array type marker */ + { + return HB_FALSE; + } + + return amf3_deserialize_obj( context, pItem, HB_TRUE ); + } + + obj_type = 1; + } + else + obj_type = 2; + + /* Instantiate new obj */ + if( obj_type == 0 ) + { + /* Anonymous obj == OBJAMF */ + hb_arrayNew( pItem, OBJAMF_VAR_COUNT ); + /* performance TOFIX, cache class id (in context maybe) + to not scan all classes by name everytime */ + hb_objSetClass( pItem, "OBJAMF", "OBJAMF" ); + pValue = hb_itemPutNI( NULL, OBJAMF_VER ); + hb_arraySet( pItem, OBJAMF_VAR_VER, pValue ); + hb_itemRelease( pValue ); + pValue = hb_itemPutC( NULL, "ANONYMOUS" ); + hb_arraySet( pItem, OBJAMF_VAR_NAME, pValue ); + hb_itemRelease( pValue ); + pValue = hb_hashNew( NULL ); + hb_arraySet( pItem, OBJAMF_VAR_HASH, pValue ); + hb_itemRelease( pValue ); + } + else if( obj_type == 1 ) + { + /* externalizable object should allow a constructor without parameters, + and the object should not do anything dangerous, because it could + be instantiated on clients request */ + + pValue = hb_hashGetCItemPtr( pMappedClassDef, "alias" ); + + if( ! pValue ) + { + hb_itemRelease( pClass ); + return HB_FALSE; + } + + pValue = cls_externalizable_instance( pValue ); + if( ! pValue ) + { + hb_itemRelease( pClass ); + return HB_FALSE; + } + + hb_itemMove( pItem, pValue ); + hb_itemRelease( pValue ); + } + else + { + /* Create obj_val for all typed objs. */ + /* obj_val = PyObject_CallMethod(class_def, "getInstance", NULL); */ + } + + if( ! HB_IS_OBJECT( pItem ) ) + { + hb_itemRelease( pClass ); + return HB_FALSE; + } + + /* Reference must be added before children (to allow for recursion). */ + amf3_add_reference( pHash, pItem ); + + if( proxy ) + { + /* If this is an ObjectProxy, + we need to add another reference, + so there is one that + points to the obj and one that points + to the proxy. */ + + amf3_add_reference( pHash, pItem ); + } + + result = HB_FALSE; + if( obj_type == 0 ) + { + result = amf3_decode_anon_obj( context, pItem, pClass ); + } + else if( obj_type == 1 ) + { + /* result = HB_TRUE; */ + result = amf3_decode_externalizable( context, pItem /*, pMappedClassDef */ ); + } + else if( obj_type == 2 ) + { + /* result = decode_typed_obj_AMF3(context, obj_val, class_def_dict); */ + } + + hb_itemRelease( pClass ); + + return result; +} + +static void amf3_conversion_in( amfContext * context, PHB_ITEM pItem ) +{ + PHB_ITEM pRetCopy = hb_itemNew( NULL ); + PHB_SYMB pSym = hb_itemGetSymbol( context->conv_function ); + + if( pItem == hb_stackReturnItem() ) + { + hb_vmPushSymbol( pSym ); + hb_vmPushNil(); + hb_vmPush( pItem ); + hb_vmDo( 1 ); + } + else + { + hb_itemMove( pRetCopy, hb_stackReturnItem() ); + hb_vmPushSymbol( pSym ); + hb_vmPushNil(); + hb_vmPush( pItem ); + hb_vmDo( 1 ); + hb_itemMove( pItem, hb_stackReturnItem() ); + hb_itemMove( hb_stackReturnItem(), pRetCopy ); + } + hb_itemRelease( pRetCopy ); +} + +/* much of deserialize_* functions are so much similar that we may + generalize them in one, f.e. adding another parameter specifying + pointer of a final decoding function... in case reference checking + returns nothing */ + +static HB_BOOL amf3_getItem( amfContext * context, PHB_ITEM pItem ) +{ + char byte; + const char * byte_ref; + HB_BOOL lRet = HB_TRUE; + + byte_ref = readByte( context ); + + if( ! byte_ref ) + return HB_FALSE; + byte = byte_ref[ 0 ]; + + switch( byte ) + { + case UNDEFINED_TYPE: + case NULL_TYPE: + lRet = HB_TRUE; + break; + + case FALSE_TYPE: + hb_itemPutL( pItem, HB_FALSE ); + lRet = HB_TRUE; + break; + + case TRUE_TYPE: + hb_itemPutL( pItem, HB_TRUE ); + lRet = HB_TRUE; + break; + + case INT_TYPE: + { + int iVal; + if( amf3_decode_int( context, &iVal ) ) + hb_itemPutNI( pItem, iVal ); + else + lRet = HB_FALSE; + } + break; + + case DOUBLE_TYPE: + { + double dVal; + if( amfX_decode_double( context, &dVal ) ) + hb_itemPutND( pItem, dVal ); + else + lRet = HB_FALSE; + } + break; + + case STRING_TYPE: + lRet = amf3_deserialize_string( context, pItem ); + break; + + case XML_DOC_TYPE: + /* don't touch xml encoding right now */ + lRet = amf3_deserialize_byte_array( context, pItem ); + break; + + case DATE_TYPE: + lRet = amf3_deserialize_date( context, pItem ); + break; + + case ARRAY_TYPE: + lRet = amf3_deserialize_array( context, pItem, HB_FALSE ); + break; + + case OBJECT_TYPE: + lRet = amf3_deserialize_obj( context, pItem, HB_FALSE ); + break; + + case XML_TYPE: + /* don't touch xml encoding right now */ + lRet = amf3_deserialize_byte_array( context, pItem ); + break; + + case BYTE_ARRAY_TYPE: + lRet = amf3_deserialize_byte_array( context, pItem ); + break; + + case AMF3_AMF0: + lRet = amf3_getItem( context, pItem ); + break; + + default: + lRet = HB_FALSE; + break; + } + + if( context->conv_function ) + amf3_conversion_in( context, pItem ); + + return lRet; + +} + +HB_FUNC( AMF3_DECODE ) +{ + PHB_ITEM pItem = hb_stackReturnItem(); +#if defined( _DEBUG ) + PHB_ITEM pDebugBlock = hb_param( 2, HB_IT_BLOCK ); +#endif + PHB_ITEM pFuncSym = hb_param( 2, HB_IT_SYMBOL ); + + amfContext * context; + + const char * szBuffer = hb_parc( 1 ); + + if( ! szBuffer ) + return; + + context = ( amfContext * ) hb_xgrab( sizeof( amfContext ) ); + memset( context, 0, sizeof( amfContext ) ); + + context->cBuf = szBuffer; + context->position = 0; + context->length = hb_parclen( 1 ); + context->obj_ref = hb_hashNew( NULL ); + context->str_ref = hb_hashNew( NULL ); + context->class_ref = hb_hashNew( NULL ); + context->conv_function = pFuncSym; + + amf3_getItem( context, pItem ); + +#if defined( _DEBUG ) + if( pDebugBlock ) + { + hb_vmPushEvalSym(); + hb_vmPush( pDebugBlock ); + hb_vmPush( context->obj_ref ); + hb_vmPush( context->str_ref ); + hb_vmPush( context->class_ref ); + hb_vmSend( ( HB_USHORT ) 3 ); + } +#endif + + hb_itemRelease( context->obj_ref ); + hb_itemRelease( context->str_ref ); + hb_itemRelease( context->class_ref ); + + /*if(context->conv_function) + hb_itemRelease(context->conv_function);*/ + + hb_xfree( context ); +} diff --git a/harbour/contrib/hbamf/amfenc.c b/harbour/contrib/hbamf/amfenc.c new file mode 100644 index 0000000000..15ceaa015b --- /dev/null +++ b/harbour/contrib/hbamf/amfenc.c @@ -0,0 +1,1464 @@ +/* + * $Id$ + */ + +/******* + * + * amfencode.c by + * Ilina Stoilkovska 2011 + * Aleksander Czajczynski 2011-2012 + * + * amfencode.c - 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; + int position; + int length; + HB_BOOL use_refs; + HB_BOOL use_strstr; + HB_BOOL str_rtrim; + HB_SIZE strstr_count; /* used only when str_ref is disabled */ + 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 void _ref_realItemPtr( PHB_ITEM pKey, PHB_ITEM pItem ); +extern HB_BOOL is_cls_externalizable( HB_USHORT uiClass ); + +static int bufferGrow( amfContext * context, int len ) +{ + int new_len = context->position + len; + int 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 int writeBuffer( amfContext * context, const char * str, int 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 ); + } +} + +/* + static HB_BOOL amf3_encode_float(amfContext * context, PHB_ITEM pItem) + { + float n = (float)hb_itemGetND(pItem); + return amfX_encode_double(context, (double)n); + } + */ + +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 UTF8 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 pKey; + PHB_ITEM pVal; + int result = 0; + HB_SIZE str_len; + + if( context->use_refs ) + { + pKey = hb_itemNew( NULL ); + _ref_realItemPtr( pKey, pItem ); + if( ! HB_IS_POINTER( pKey ) && ! HB_IS_DATETIME( pKey ) ) + { + hb_itemRelease( pKey ); + return -1; + } + + pVal = hb_itemNew( NULL ); + + if( ! hb_hashAdd( pHash, pKey, pVal ) ) + { + hb_itemRelease( pKey ); + hb_itemRelease( pVal ); + return -1; + } + hb_itemRelease( pVal ); + + hb_itemRelease( pKey ); + result = ( int ) hb_hashLen( pHash ) - 1 + context->strstr_count; + /* used only when some inner context inside + * conversion function uses only strstr mode + * like AMF3_FROMWA function f.e. */ + } + + if( ( HB_IS_STRING( pItem ) || HB_IS_MEMO( pItem ) ) && context->use_strstr ) + { + str_len = hb_itemGetCLen( pItem ); + if( str_len > 3 && str_len < 32 ) /* do this only for mid-sized strings */ + { + if( ! context->use_refs ) + result = 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 ) +{ + PHB_ITEM pKey; + PHB_ITEM pStrIdx; + HB_SIZE nPos; + HB_SIZE str_len; + + if( context->use_refs ) + { + pKey = hb_itemNew( NULL ); + _ref_realItemPtr( pKey, pItem ); + if( ! HB_IS_POINTER( pKey ) && ! HB_IS_DOUBLE( pKey ) ) + { + hb_itemRelease( pKey ); + return -1; + } + if( hb_hashScan( pHash, pKey, &nPos ) ) + { + hb_itemRelease( pKey ); + return nPos - 1; + } + else + hb_itemRelease( pKey ); + } + + if( ( HB_IS_STRING( pItem ) || HB_IS_MEMO( pItem ) ) && context->use_strstr ) + { + str_len = hb_itemGetCLen( pItem ); + if( str_len > 3 && str_len < 32 ) /* do this only for mid-sized strings */ + { + pStrIdx = hb_hashGetItemPtr( context->strstr_ref, pItem, 0 ); + if( pStrIdx ) + return hb_itemGetNS( pStrIdx ); + else + return -1; + } + else + return -1; + } + + return -1; +} + +static int amf3_encode_reference( amfContext * context, PHB_ITEM pHash, PHB_ITEM pItem, int bit ) +{ + int idx = -1; + + 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 ); +} + +/* + 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; + } + */ + +static HB_BOOL amf3_encode_hash( amfContext * context, PHB_ITEM pItem ) +{ + PHB_ITEM pKey; + PHB_ITEM pVal; + int i = 0; + int len = hb_hashLen( pItem ); + int 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, 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_encode_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 ) +{ + PHB_ITEM pKey; + PHB_ITEM pVal; + int i = 0; + int len = hb_hashLen( pItem ); + + 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; + + return HB_TRUE; +} + +static HB_BOOL amf3_serialize_hash( amfContext * context, PHB_ITEM pItem ) +{ + HB_BOOL result = amf3_encode_reference( context, context->obj_ref, pItem, 0 ); + + if( result > -1 ) + { + return result; + } + + 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 int amf3_encode_byte_array( amfContext * context, PHB_ITEM pItem ) +{ + int 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 int 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 ); + PHB_ITEM pArrayItem; + int i; + int result; + + 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++ ) + { + pArrayItem = hb_itemNew( NULL ); + hb_arrayGet( pItem, i, pArrayItem ); + if( ! pArrayItem ) + return HB_FALSE; + + result = amf3_encode( context, pArrayItem ); + hb_itemRelease( pArrayItem ); + if( ! result ) + return HB_FALSE; + } + + return HB_TRUE; +} + +static HB_BOOL amf3_serialize_array( amfContext * context, PHB_ITEM pItem ) +{ + int result = amf3_encode_reference( context, context->obj_ref, pItem, 0 ); + + if( result > -1 ) + return result; + return amf3_encode_array( context, pItem ); +} + +static int amf3_encode_class_def( amfContext * context, PHB_ITEM pClass ) +{ + int header; + int result; + int static_attr_len; + int i; + PHB_ITEM class_alias = NULL; + PHB_ITEM static_attrs = NULL; + /* PHB_ITEM attr_len = NULL; */ + PHB_ITEM attr_name = NULL; + + 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++ ) + { + 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( 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 + 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; + } + */ + + 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 ) ), "OBJAMF" ) == 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 ); + 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 ) ), "RAWAMF" ) == 0 ) + { + PHB_ITEM pStr = hb_itemNew( NULL ); + hb_arrayGet( pItem, 1, pStr ); + context->position--; + result = amf3_encode_byte_array( context, pStr ); + hb_itemRelease( pStr ); + return result; + } + + result = amf3_encode_reference( context, context->obj_ref, pItem, 0 ); + + if( result > -1 ) + return result; + + 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 ); + } + 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; + 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 ); + /* memset( context->cBuf, 0, 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; + } + else + { + context->obj_ref = hb_hashNew( NULL ); + context->str_ref = hb_hashNew( NULL ); + context->class_ref = hb_hashNew( NULL ); + hb_hashSetFlags( context->obj_ref, HB_HASH_KEEPORDER ); + hb_hashSetFlags( context->str_ref, HB_HASH_KEEPORDER ); + hb_hashSetFlags( context->class_ref, HB_HASH_KEEPORDER ); + } + } + 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( context->use_refs && ! ( outer_context && outer_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 ); + /* amf3_encode_int(context, ((int) 1) << 1 | REFERENCE_BIT); */ + 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 ); + /* memset( context->cBuf, 0, 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; + hb_hashSetFlags( context->obj_ref, HB_HASH_KEEPORDER ); + hb_hashSetFlags( context->str_ref, HB_HASH_KEEPORDER ); + hb_hashSetFlags( context->class_ref, HB_HASH_KEEPORDER ); + + /* "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(context->conv_function) + hb_itemRelease(context->conv_function);*/ + + 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 ); +} diff --git a/harbour/contrib/hbamf/amfstdio.c b/harbour/contrib/hbamf/amfstdio.c new file mode 100644 index 0000000000..43ff3ab60f --- /dev/null +++ b/harbour/contrib/hbamf/amfstdio.c @@ -0,0 +1,100 @@ +/* + * $Id$ + */ + +/******* + * + * amfstdio.c by Ilina Stoilkovska 2011 + * Aleksander Czajczynski 2012 + * + * amfstdio.c - Reading AMFIO data from standard input pipe + * + * + ********/ + +#include "hbapi.h" +#include "hbapiitm.h" +#include "hbapigt.h" +#include "hbapifs.h" + +#define SINGLEBUF 32768 +#define MAXLEN ( 16 * 1024 * 1024 ) + +static int s_nCount = 0; + +static void countCheck( int n ) +{ + /* yes, this is flow-control */ + + s_nCount += n; + + while( s_nCount >= SINGLEBUF ) + { + hb_conOutStd( "\0\0\0\0", 4 ); + s_nCount -= SINGLEBUF; + } +} + +HB_FUNC( AMFSTDIO_READ ) +{ + char * pszStrIn = ( char * ) hb_xgrab( SINGLEBUF ); + char * pszLenPrefix = ( char * ) hb_xgrab( 5 ); + char * pszBuf; /* = ( char * ) hb_xgrab( SINGLEBUF ); */ + char * pszTmp = pszLenPrefix; + HB_USHORT nBytes = 0; + int nTotal = 0; + int nLen = 0; + int nToRead; + HB_FHANDLE hStdIn = hb_fsGetOsHandle( HB_STDIN_HANDLE ); + + while( nTotal < 4 ) + { + nToRead = ( s_nCount + 4 - nTotal > SINGLEBUF ? SINGLEBUF - s_nCount : 4 - nTotal ); + nBytes = hb_fsRead( hStdIn, pszStrIn, ( HB_USHORT ) nToRead ); + + countCheck( nBytes ); + + memcpy( pszTmp, pszStrIn, nBytes ); + nTotal += nBytes; + pszTmp = pszLenPrefix + nTotal; + } + + pszLenPrefix[ 4 ] = '\0'; + nLen = HB_GET_LE_UINT32( pszLenPrefix ); + + if( nLen >= MAXLEN ) + { + hb_ret(); + return; + } + + nTotal = 0; + pszBuf = ( char * ) hb_xgrab( nLen + 1 ); + pszTmp = pszBuf; + + while( nTotal < nLen ) + { + /* + * here it's being decided that nToRead is never over 32768 bytes long, + * so hb_fsRead() is fine, no hb_fsReadLarge() needed + */ + + if( nLen - nTotal > SINGLEBUF ) + nToRead = SINGLEBUF - s_nCount; + else + nToRead = ( s_nCount + nLen - nTotal > SINGLEBUF ? SINGLEBUF - s_nCount : nLen - nTotal ); + + nBytes = hb_fsRead( hStdIn, pszStrIn, ( HB_USHORT ) nToRead ); + + countCheck( nBytes ); + + memcpy( pszTmp, pszStrIn, nBytes ); + nTotal += nBytes; + pszTmp = pszBuf + nTotal; + } + + hb_xfree( pszStrIn ); + hb_xfree( pszLenPrefix ); + hb_retclen_buffer( pszBuf, nLen ); + +} diff --git a/harbour/contrib/hbamf/hbamf.hbc b/harbour/contrib/hbamf/hbamf.hbc new file mode 100644 index 0000000000..1e7989e430 --- /dev/null +++ b/harbour/contrib/hbamf/hbamf.hbc @@ -0,0 +1,7 @@ +# +# $Id$ +# + +incpaths=. + +libs=${_HB_DYNPREF}${hb_name}${_HB_DYNSUFF} diff --git a/harbour/contrib/hbamf/hbamf.hbp b/harbour/contrib/hbamf/hbamf.hbp new file mode 100644 index 0000000000..fc4f1a2055 --- /dev/null +++ b/harbour/contrib/hbamf/hbamf.hbp @@ -0,0 +1,20 @@ +# +# $Id$ +# + +-hblib +-inc + +-o${hb_name} + +-w3 -es2 + +-instfile=inc:hbamf.hbx + +hbamf.hbx + +amfenc.c +amfdec.c +amfstdio.c +hbref.c +hbcls.c diff --git a/harbour/contrib/hbamf/hbamf.hbx b/harbour/contrib/hbamf/hbamf.hbx new file mode 100644 index 0000000000..4006288b7b --- /dev/null +++ b/harbour/contrib/hbamf/hbamf.hbx @@ -0,0 +1,37 @@ +/* + * $Id$ + */ + +/* -------------------------------------------------------------------- */ +/* NOTE: You can add manual override which functions to include or */ +/* exclude from automatically generated EXTERNAL/DYNAMIC list. */ +/* Syntax: // HB_FUNC_INCLUDE */ +/* // HB_FUNC_EXCLUDE */ +/* -------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------- */ +/* WARNING: Automatically generated code below. DO NOT EDIT! */ +/* Regenerate using hbmk2 '-hbx=' option. */ +/* -------------------------------------------------------------------- */ + +#ifndef __HBEXTERN_CH__HBAMF__ +#define __HBEXTERN_CH__HBAMF__ + +#if defined( __HBEXTREQ__ ) .OR. defined( __HBEXTERN__HBAMF__ANNOUNCE ) + ANNOUNCE __HBEXTERN__HBAMF__ +#endif + +#if defined( __HBEXTREQ__ ) .OR. defined( __HBEXTERN__HBAMF__REQUEST ) + #command DYNAMIC => EXTERNAL +#endif + +DYNAMIC AMF3_DECODE +DYNAMIC AMF3_ENCODE +DYNAMIC AMF3_FROMWA +DYNAMIC AMFSTDIO_READ + +#if defined( __HBEXTREQ__ ) .OR. defined( __HBEXTERN__HBAMF__REQUEST ) + #uncommand DYNAMIC => EXTERNAL +#endif + +#endif diff --git a/harbour/contrib/hbamf/hbcls.c b/harbour/contrib/hbamf/hbcls.c new file mode 100644 index 0000000000..44722d2bca --- /dev/null +++ b/harbour/contrib/hbamf/hbcls.c @@ -0,0 +1,80 @@ +/******* + * + * hbref.c by Aleksander Czajczynski 2011 + ** + * hbref.c - Some class support functions for AMF3 (de)serialization + * + ********/ + +#include "hbapi.h" +#include "hbapicls.h" +#include "hbapiitm.h" +#include "hbapistr.h" +#include "hbstack.h" +#include "hbvm.h" +#include "hboo.ch" + +HB_BOOL is_cls_externalizable( HB_USHORT uiClass ) +{ + PHB_DYNS pSymbol = hb_dynsymGet( "__CLSMSGTYPE" ); + HB_BOOL result = HB_FALSE; + + /* as far as i know, there is no exported Harbour C level api for this */ + + if( uiClass && pSymbol ) + { + PHB_ITEM pRetCopy = hb_itemNew( NULL ); + + hb_itemMove( pRetCopy, hb_stackReturnItem() ); + + hb_vmPushDynSym( pSymbol ); + hb_vmPushNil(); + hb_vmPushInteger( uiClass ); + hb_vmPushString( "EXTERNALIZABLE", 14 ); + hb_vmDo( 2 ); + + if( hb_itemGetNI( hb_stackReturnItem() ) == HB_OO_MSG_CLASSDATA ) + result = HB_TRUE; + + hb_itemMove( hb_stackReturnItem(), pRetCopy ); + hb_itemRelease( pRetCopy ); + } + + return result; +} + +PHB_ITEM cls_externalizable_instance( PHB_ITEM pClassFuncStr ) +{ + PHB_DYNS pSymbol = hb_dynsymGet( hb_itemGetCPtr( pClassFuncStr ) ); + + if( pSymbol ) + { + PHB_ITEM pRetCopy = hb_itemNew( NULL ); + PHB_ITEM pNewItem = hb_itemNew( NULL ); + hb_itemMove( pRetCopy, hb_stackReturnItem() ); + + hb_vmPushDynSym( pSymbol ); + hb_vmPushNil(); + hb_vmDo( 0 ); + + hb_objSendMsg( hb_stackReturnItem(), "NEW", 0 ); + + hb_itemMove( pNewItem, hb_stackReturnItem() ); + hb_itemMove( hb_stackReturnItem(), pRetCopy ); + + hb_itemRelease( pRetCopy ); + + if( pNewItem ) + { + if( ! HB_IS_OBJECT( pNewItem ) ) + { + hb_itemRelease( pNewItem ); + pNewItem = NULL; + } + } + + return pNewItem; + } + + return NULL; +} diff --git a/harbour/contrib/hbamf/hbref.c b/harbour/contrib/hbamf/hbref.c new file mode 100644 index 0000000000..440402dfb8 --- /dev/null +++ b/harbour/contrib/hbamf/hbref.c @@ -0,0 +1,29 @@ +/******* + * + * hbref.c by Aleksander Czajczynski 2011-2012 + * + * hbref.c - Using Harbour C-pointers to various items as hashkeys + * + ********/ + +#include "hbapiitm.h" + +void _ref_realItemPtr( PHB_ITEM pKey, PHB_ITEM pItem ) +{ + if( HB_IS_STRING( pItem ) ) + { + hb_itemPutPtr( pKey, ( void * ) 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 ); + } +} diff --git a/harbour/contrib/hbamf/issues.txt b/harbour/contrib/hbamf/issues.txt new file mode 100644 index 0000000000..4fde6f6634 --- /dev/null +++ b/harbour/contrib/hbamf/issues.txt @@ -0,0 +1,34 @@ +/* + * $Id$ + */ + +* amf3enc.c +* amf3dec.c + ; AMF3_ENCODE(), AMF3_DECODE() + - there is no real serialization class-mapping included at the moment, + due to lack of time to make a concept of it complete. + Only anonymous (emulated on Harbour side using ObjAMF class) + and externalizable objects are supported. + + - context->positon-- decrements should be removed from the .c code, + before we make the functions able to work on real streams without + buffering. There is no rewind in such situations. + + ; AMF3_FROMWA() - generates AMF3 array from current workarea, + but the function doesn't have an idea of SET DELETED switch! + It should have, because it tries to predict the number of records. + As a workaround parameter could be used, because it switches + off the prediction. Another option could be creating temporary INDEX FOR, + it should have correct OrdKeyCount(). + + ; AMF3_DECODE() - really doesn't need a hash for references, because + reference id in AMF increase sequentially. It could be okay and faster + to use some array with decent resize schema. Other than that benchmarks (in + ST mode) showed that decoding speed is a little bit faster in this + implementation than with Flash's bulit-in. + + ; .c function amf3_encode_string() does string hb_strRTrimLen() + on a utf8 values. so far i haven't found a string that was broken + by this, but i have a feeling that it is possible... + +Note your issues too! diff --git a/harbour/contrib/hbamf/readme.txt b/harbour/contrib/hbamf/readme.txt new file mode 100644 index 0000000000..4d3025f380 --- /dev/null +++ b/harbour/contrib/hbamf/readme.txt @@ -0,0 +1,68 @@ +/* + * $Id$ + */ + +-------------------------------------- +Short description of Harbour functions +-------------------------------------- + + cAMF := AMF3_ENCODE( xVal, symConvOut, lBinaryStrings ) + xVal - any supported datatype: + Character (String/MEMO), Numeric (Integer/Double), NIL, Logical, Date (encoded as DateTime), + DateTime, Array, Hash (String and Integer keys only), + Object (anonymous hash-like, externalizable, class-mapped) + + AMF supports references, so this example a1 value will be serialized correctly. + a1 := { NIL } + a2 := { a1 } + a1[1] := a2 + + symConvOut - function symbol for outbound conversion + Acts recursively, so if xVal is array, + all of it's values will be passed to this + function. + + lBinaryStrings - treat strings as binary, resulting AMF + datatype will be ByteArray. Normally + a string is encoded to UTF-8. + + xVal := AMF3_DECODE( cAMF, symConvIn ) + cAMF - AMF3 serialized binary string + symConvIn - function symbol for inbound conversion + + cAMF := AMF3_FROMWA( [ ], [ ], [ ], [ ], [ ], [ ], [ pContext ] ) + + Function to convert current workarea to AMF3 Array. + + bWhile - COPY TO like WHILE codeblock + bFor - COPY TO like FOR codeblock + aFields - array of fieldnames (codeblocks are going to be supported here too) + nCount - NEXT like, process only specified count of records + lStrTrim - RTrim() strings, default is .T. + + nPackage - determine the exact output AMF format + 0 - Array of records contains Arrays of fields (default) + 1 - Array of records contains Anonymous objects + 2 - ArrayCollection object + Lower number means lesser packet size on the network. + + pContext - when this function is used inside AMF3_ENCODE specified + outbound conversion function, you must pass pointer to + encoding context, otherwise AMF3 references will encoded + incorrectly. Example code of such case: + STATIC FUNCTION ConvOut( xVal, pOuterContext ) + LOCAL lClose + + IF ValType( xVal ) = "O" + IF xVal:className == "WORKAREAEXPORT" + lClose := xVal:lCloseWA + SELECT ( xVal:nWorkArea ) + + xVal := RawAMF():New( AMF3_FROMWA( xVal:bWhile, xVal:bFor, xVal:aFields, xVal:nCount, xVal:lStrTrim, 1, pOuterContext ) ) + IF lClose + CLOSE + ENDIF + ENDIF + ENDIF + + RETURN xVal diff --git a/harbour/contrib/hbamf/tests/hbmk.hbm b/harbour/contrib/hbamf/tests/hbmk.hbm new file mode 100644 index 0000000000..f0dfa66e74 --- /dev/null +++ b/harbour/contrib/hbamf/tests/hbmk.hbm @@ -0,0 +1,7 @@ +# +# $Id$ +# + +hbamf.hbc + +-w3 -es2 diff --git a/harbour/contrib/hbamf/tests/tstendin.prg b/harbour/contrib/hbamf/tests/tstendin.prg new file mode 100644 index 0000000000..20207c218b --- /dev/null +++ b/harbour/contrib/hbamf/tests/tstendin.prg @@ -0,0 +1,45 @@ +/* + * $Id$ + */ + +REQUEST HB_CODEPAGE_UTF8EX + +#uncommand ? [] => +#command ? [] => A( ) + +PROCEDURE MAIN() + + hb_cdpSelect( "UTF8EX" ) + + ? { }, "8352" + ? "a", "F248" + ? "¥", "96F0" + ? 1, "AE79" + ? 1000, "278B" + ? 1000000, "A752" + ? 268435455, "4907" + ? 268435456, "E677" + ? 268435456000, "4271" + ? - 1, "FE11" + ? 9007199254740990, "0009" + ? AMF3_DECODE( AMF3_ENCODE( 9007199254740990 ) ), "0009" + ? 9007199254740991, "8918" + ? AMF3_DECODE( AMF3_ENCODE( 9007199254740991 ) ), "8918" + ? 9007199254740991.00, "8918" + ? 6969.69, "10AF" + ? NIL, "F1E1" + ? .T. , "E3C2" + ? .F. , "6AD3" + ? { 1, - 1 }, "0560" + ? { "ONE" => 0xcafe, "TWO" => 0xbabe }, "CE93" + + RETURN + +PROCEDURE A( a, cChkOK ) + + LOCAL x := AMF3_ENCODE( a ) + LOCAL cChk := hb_StrToHex( I2Bin( hb_CRC( x ) ) ) + + QOut( PadL( hb_ValToExp( a ), 18 ), hb_StrToHex( x, " " ), "CHECKSUM", cChk, IIF( !Empty( cChkOK ) .AND. !( cChk == cChkOK ), "!TEST FAILED!, should be " + cChkOK, " " ) ) + + RETURN diff --git a/harbour/contrib/hbplist b/harbour/contrib/hbplist index ca25ffbaa7..132a9cadc8 100644 --- a/harbour/contrib/hbplist +++ b/harbour/contrib/hbplist @@ -4,6 +4,7 @@ gtalleg/gtalleg.hbp gtwvg/gtwvg.hbp +hbamf/hbamf.hbp hbblat/hbblat.hbp hbblink/hbblink.hbp hbbz2/hbbz2.hbp # uses: bz2 (locally hosted) diff --git a/harbour/utils/hbmk2/hbmk2.prg b/harbour/utils/hbmk2/hbmk2.prg index 803ca70f80..01f95d127e 100644 --- a/harbour/utils/hbmk2/hbmk2.prg +++ b/harbour/utils/hbmk2/hbmk2.prg @@ -518,13 +518,8 @@ REQUEST hbmk_KEYW #define PathMakeAbsolute( cPathR, cPathA ) hb_PathJoin( cPathA, cPathR ) -/* NOTE: use hbextern library instead of #include "hbextern.ch" - * in dynamic builds it will greatly reduce the size because - * all function symbols will be registered by harbour shared - * library (.dll, .so, .sl, .dyn, ...) not by this code - */ - -REQUEST __HB_EXTERN__ /* for runner and interactive shell */ +/* Request for runner and interactive shell */ +REQUEST __HB_EXTERN__ /* Request some functions for plugins */ REQUEST HBCLASS