From bb49e3711a92e414ef8ee592e2005c15c6fc82ed Mon Sep 17 00:00:00 2001 From: Przemyslaw Czerpak Date: Tue, 4 Nov 2008 21:39:10 +0000 Subject: [PATCH] 2008-11-04 22:39 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/include/hbapi.h * harbour/source/vm/hashfunc.c * harbour/source/vm/hashes.c + added support for binary key order % enable binary internal key order by default for new hash arrays + added .prg function to enable/disable/retrive binary order in hash items: hb_hSetBinary( , ) => hb_hBinary( [, ] ) => + added support for hash array resorting. It's activated automatically on 1-st key access/assign after changing binary or casematch hash array flag + added .prg function hb_hSort( ) => which marks hash array for sorting * harbour/source/rtl/itemseri.c % improved performance of hash array deserialization TODO: add support for hash array flags and hash default value serialization --- harbour/ChangeLog | 21 ++++++++ harbour/include/hbapi.h | 7 +++ harbour/source/rtl/itemseri.c | 21 ++++++-- harbour/source/vm/hashes.c | 99 +++++++++++++++++++++++++++-------- harbour/source/vm/hashfunc.c | 62 ++++++++++++++++++++-- 5 files changed, 182 insertions(+), 28 deletions(-) diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 6ccc5143f3..0974999578 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,27 @@ 2008-12-31 13:59 UTC+0100 Foo Bar (foo.bar foobar.org) */ +2008-11-04 22:39 UTC+0100 Przemyslaw Czerpak (druzus/at/priv.onet.pl) + * harbour/include/hbapi.h + * harbour/source/vm/hashfunc.c + * harbour/source/vm/hashes.c + + added support for binary key order + % enable binary internal key order by default for new hash arrays + + added .prg function to enable/disable/retrive binary order in hash + items: + hb_hSetBinary( , ) => + hb_hBinary( [, ] ) => + + added support for hash array resorting. It's activated automatically + on 1-st key access/assign after changing binary or casematch hash + array flag + + added .prg function hb_hSort( ) => which marks + hash array for sorting + + * harbour/source/rtl/itemseri.c + % improved performance of hash array deserialization + TODO: add support for hash array flags and hash default value + serialization + 2008-11-04 19:59 UTC+0200 Viktor Szakats (harbour.01 syenar hu) * contrib/hbnf/getenvrn.c - Removed some obsolete comments. diff --git a/harbour/include/hbapi.h b/harbour/include/hbapi.h index 2359cdaa92..f6ca7a79c7 100644 --- a/harbour/include/hbapi.h +++ b/harbour/include/hbapi.h @@ -756,6 +756,7 @@ extern HB_EXPORT BOOL hb_hashDel( PHB_ITEM pHash, PHB_ITEM pKey ); extern HB_EXPORT BOOL hb_hashAdd( PHB_ITEM pHash, PHB_ITEM pKey, PHB_ITEM pValue ); extern HB_EXPORT BOOL hb_hashAddNew( PHB_ITEM pHash, PHB_ITEM pKey, PHB_ITEM pValue ); extern HB_EXPORT BOOL hb_hashRemove( PHB_ITEM pHash, PHB_ITEM pItem ); +extern HB_EXPORT BOOL hb_hashAllocNewPair( PHB_ITEM pHash, PHB_ITEM * pKeyPtr, PHB_ITEM * pValPtr ); extern HB_EXPORT PHB_ITEM hb_hashClone( PHB_ITEM pSource ); extern HB_EXPORT void hb_hashJoin( PHB_ITEM pDest, PHB_ITEM pSource, int iType ); extern HB_EXPORT BOOL hb_hashScan( PHB_ITEM pHash, PHB_ITEM pKey, ULONG * pulPos ); @@ -785,7 +786,13 @@ extern HB_EXPORT BOOL hb_hashDelAt( PHB_ITEM pHash, ULONG ulPos ); #define HB_HASH_AUTOADD_REFERENCE HB_HASH_AUTOADD_ALWAYS #define HB_HASH_AUTOADD_MASK 0x03 +#define HB_HASH_RESORT 0x08 + #define HB_HASH_IGNORECASE 0x10 +#define HB_HASH_BINARY 0x20 + +#define HB_HASH_FLAG_MASK 0xFFFF +#define HB_HASH_FLAG_DEFAULT ( HB_HASH_AUTOADD_ASSIGN | HB_HASH_BINARY ) #ifdef _HB_API_INTERNAL_ /* internal hash API not exported */ diff --git a/harbour/source/rtl/itemseri.c b/harbour/source/rtl/itemseri.c index 8b63eeca10..cf54b62554 100644 --- a/harbour/source/rtl/itemseri.c +++ b/harbour/source/rtl/itemseri.c @@ -263,9 +263,9 @@ static void hb_itemSerialRefFree( PHB_CYCLIC_REF pRef ) { while( pRef ) { - PHB_CYCLIC_REF pNext = pRef->pNext; - hb_xfree( pRef ); - pRef = pNext; + PHB_CYCLIC_REF pFree = pRef; + pRef = pRef->pNext; + hb_xfree( pFree ); } } @@ -708,6 +708,7 @@ static ULONG hb_deserializeHash( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffset if( ulLen ) { +#if 0 PHB_ITEM pKey = hb_itemNew( NULL ); PHB_ITEM pVal = hb_itemNew( NULL ); @@ -720,6 +721,20 @@ static ULONG hb_deserializeHash( PHB_ITEM pItem, UCHAR * pBuffer, ULONG ulOffset } hb_itemRelease( pKey ); hb_itemRelease( pVal ); +#else + PHB_ITEM pKey, pVal; + + hb_hashSetFlags( pItem, HB_HASH_BINARY | HB_HASH_RESORT ); + hb_hashPreallocate( pItem, ulLen ); + while( ulLen-- ) + { + if( hb_hashAllocNewPair( pItem, &pKey, &pVal ) ) + { + ulOffset = hb_deserializeItem( pKey, pBuffer, ulOffset, pRef ); + ulOffset = hb_deserializeItem( pVal, pBuffer, ulOffset, pRef ); + } + } +#endif } return ulOffset; diff --git a/harbour/source/vm/hashes.c b/harbour/source/vm/hashes.c index 5707ab9788..b7aa314ce6 100644 --- a/harbour/source/vm/hashes.c +++ b/harbour/source/vm/hashes.c @@ -136,13 +136,19 @@ static HB_GARBAGE_FUNC( hb_hashReleaseGarbage ) } } -static int hb_hashItemCmp( PHB_ITEM pKey1, PHB_ITEM pKey2, BOOL fIgnoreCase ) +static int hb_hashItemCmp( PHB_ITEM pKey1, PHB_ITEM pKey2, int iFlags ) { if( HB_IS_STRING( pKey1 ) ) { if( HB_IS_STRING( pKey2 ) ) { - if( fIgnoreCase ) + if( iFlags & HB_HASH_BINARY ) + return pKey1->item.asString.length < pKey2->item.asString.length ? -1 : + ( pKey1->item.asString.length > pKey2->item.asString.length ? 1 : + memcmp( pKey1->item.asString.value, + pKey2->item.asString.value, + pKey1->item.asString.length ) ); + else if( iFlags & HB_HASH_IGNORECASE ) return hb_itemStrICmp( pKey1, pKey2, TRUE ); else return hb_itemStrCmp( pKey1, pKey2, TRUE ); @@ -184,16 +190,48 @@ static int hb_hashItemCmp( PHB_ITEM pKey1, PHB_ITEM pKey2, BOOL fIgnoreCase ) return -1; } +static void hb_hashResort( PHB_BASEHASH pBaseHash ) +{ + ULONG ulPos, ulFrom; + int iFlags = pBaseHash->iFlags; + + /* The hash array is probably quite well sorted so this trivial + * algorithm is the most efficient one [druzus] + */ + for( ulFrom = 1; ulFrom < pBaseHash->ulLen; ++ulFrom ) + { + ulPos = ulFrom; + while( ulPos > 0 && hb_hashItemCmp( &pBaseHash->pPairs[ ulPos - 1 ].key, + &pBaseHash->pPairs[ ulPos ].key, + iFlags ) > 0 ) + { + HB_HASHPAIR pair; + memcpy( &pair, pBaseHash->pPairs + ulPos - 1, sizeof( HB_HASHPAIR ) ); + memcpy( pBaseHash->pPairs + ulPos - 1, pBaseHash->pPairs + ulPos, sizeof( HB_HASHPAIR ) ); + memcpy( pBaseHash->pPairs + ulPos, &pair, sizeof( HB_HASHPAIR ) ); + --ulPos; + } + } + + pBaseHash->iFlags &= ~HB_HASH_RESORT; +} + static BOOL hb_hashFind( PHB_BASEHASH pBaseHash, PHB_ITEM pKey, ULONG * pulPos ) { - ULONG ulLeft = 0, ulRight = pBaseHash->ulLen, ulMiddle; - BOOL fIgnoreCase = ( pBaseHash->iFlags & HB_HASH_IGNORECASE ) != 0; + ULONG ulLeft, ulRight, ulMiddle; + int iFlags = pBaseHash->iFlags; int i; + if( iFlags & HB_HASH_RESORT ) + hb_hashResort( pBaseHash ); + + ulLeft = 0; + ulRight = pBaseHash->ulLen; + while( ulLeft < ulRight ) { ulMiddle = ( ulLeft + ulRight ) >> 1; - i = hb_hashItemCmp( &pBaseHash->pPairs[ ulMiddle ].key, pKey, fIgnoreCase ); + i = hb_hashItemCmp( &pBaseHash->pPairs[ ulMiddle ].key, pKey, iFlags ); if( i == 0 ) { if( pulPos ) @@ -301,6 +339,16 @@ static BOOL hb_hashNewValue( PHB_BASEHASH pBaseHash, PHB_ITEM pKey, PHB_ITEM pVa return FALSE; } +static void hb_hashNewPair( PHB_BASEHASH pBaseHash, PHB_ITEM * pKeyPtr, PHB_ITEM * pValPtr ) +{ + if( pBaseHash->ulSize == pBaseHash->ulLen ) + hb_hashResize( pBaseHash, pBaseHash->ulSize + HB_HASH_ITEM_ALLOC ); + + * pKeyPtr = &pBaseHash->pPairs[ pBaseHash->ulLen ].key; + * pValPtr = &pBaseHash->pPairs[ pBaseHash->ulLen ].value; + pBaseHash->ulLen++; +} + static void hb_hashDelPair( PHB_BASEHASH pBaseHash, ULONG ulPos ) { if( --pBaseHash->ulLen == 0 ) @@ -314,23 +362,19 @@ static void hb_hashDelPair( PHB_BASEHASH pBaseHash, ULONG ulPos ) hb_itemClear( &pPairs->value ); hb_xfree( pPairs ); } - else if( ulPos == pBaseHash->ulLen ) - { - hb_itemSetNil( &pBaseHash->pPairs[ ulPos ].key ); - hb_itemSetNil( &pBaseHash->pPairs[ ulPos ].value ); - } else { - HB_HASHPAIR pair; - memcpy( &pair, pBaseHash->pPairs + ulPos, sizeof( HB_HASHPAIR ) ); - memmove( pBaseHash->pPairs + ulPos, pBaseHash->pPairs + ulPos + 1, - ( pBaseHash->ulLen - ulPos ) * sizeof( HB_HASHPAIR ) ); - pBaseHash->pPairs[ pBaseHash->ulLen ].key.type = HB_IT_NIL; - pBaseHash->pPairs[ pBaseHash->ulLen ].value.type = HB_IT_NIL; - if( HB_IS_COMPLEX( &pair.key ) ) - hb_itemClear( &pair.key ); - if( HB_IS_COMPLEX( &pair.value ) ) - hb_itemClear( &pair.value ); + if( ulPos != pBaseHash->ulLen ) + { + HB_HASHPAIR pair; + memcpy( &pair, pBaseHash->pPairs + ulPos, sizeof( HB_HASHPAIR ) ); + memmove( pBaseHash->pPairs + ulPos, pBaseHash->pPairs + ulPos + 1, + ( pBaseHash->ulLen - ulPos ) * sizeof( HB_HASHPAIR ) ); + ulPos = pBaseHash->ulLen; + memcpy( pBaseHash->pPairs + ulPos, &pair, sizeof( HB_HASHPAIR ) ); + } + hb_itemSetNil( &pBaseHash->pPairs[ ulPos ].key ); + hb_itemSetNil( &pBaseHash->pPairs[ ulPos ].value ); if( pBaseHash->ulSize - pBaseHash->ulLen > ( HB_HASH_ITEM_ALLOC << 1 ) ) { pBaseHash->ulSize -= HB_HASH_ITEM_ALLOC; @@ -355,7 +399,7 @@ HB_EXPORT PHB_ITEM hb_hashNew( PHB_ITEM pItem ) pBaseHash->pPairs = NULL; pBaseHash->ulSize = 0; pBaseHash->ulLen = 0; - pBaseHash->iFlags = HB_HASH_AUTOADD_ASSIGN; + pBaseHash->iFlags = HB_HASH_FLAG_DEFAULT; pBaseHash->pDefault = NULL; pItem->type = HB_IT_HASH; @@ -380,6 +424,19 @@ HB_EXPORT void hb_hashPreallocate( PHB_ITEM pHash, ULONG ulNewSize ) hb_hashResize( pHash->item.asHash.value, ulNewSize ); } +HB_EXPORT BOOL hb_hashAllocNewPair( PHB_ITEM pHash, PHB_ITEM * pKeyPtr, PHB_ITEM * pValPtr ) +{ + HB_TRACE(HB_TR_DEBUG, ("hb_hashAllocNewPair(%p,%p,%p)", pHash, pKeyPtr, pValPtr)); + + if( HB_IS_HASH( pHash ) ) + { + hb_hashNewPair( pHash->item.asHash.value, pKeyPtr, pValPtr ); + return TRUE; + } + else + return FALSE; +} + HB_EXPORT PHB_ITEM hb_hashGetItemPtr( PHB_ITEM pHash, PHB_ITEM pKey, int iFlags ) { HB_TRACE(HB_TR_DEBUG, ("hb_hashGetItemPtr(%p,%p,%d)", pHash, pKey, iFlags)); diff --git a/harbour/source/vm/hashfunc.c b/harbour/source/vm/hashfunc.c index 6ec108a21c..5d48b21b31 100644 --- a/harbour/source/vm/hashfunc.c +++ b/harbour/source/vm/hashfunc.c @@ -604,6 +604,19 @@ HB_FUNC( HB_HSCAN ) hb_errRT_BASE( EG_ARG, 1123, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); } +HB_FUNC( HB_HSORT ) +{ + PHB_ITEM pHash = hb_param( 1, HB_IT_HASH ); + + if( pHash ) + { + hb_hashSetFlags( pHash, HB_HASH_RESORT ); + hb_itemReturn( pHash ); + } + else + hb_errRT_BASE( EG_ARG, 2017, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + HB_FUNC( HB_HCASEMATCH ) { PHB_ITEM pHash = hb_param( 1, HB_IT_HASH ); @@ -611,13 +624,53 @@ HB_FUNC( HB_HCASEMATCH ) if( pHash ) { - hb_retl( ( hb_hashGetFlags( pHash ) & HB_HASH_IGNORECASE ) == 0 ); + int iFlags = hb_hashGetFlags( pHash ); + hb_retl( ( iFlags & HB_HASH_IGNORECASE ) == 0 ); if( pValue ) { if( hb_itemGetL( pValue ) ) - hb_hashClearFlags( pHash, HB_HASH_IGNORECASE ); - else - hb_hashSetFlags( pHash, HB_HASH_IGNORECASE ); + { + if( ( iFlags & HB_HASH_IGNORECASE ) != 0 ) + { + hb_hashClearFlags( pHash, HB_HASH_IGNORECASE ); + hb_hashSetFlags( pHash, HB_HASH_RESORT ); + } + } + else if( ( iFlags & HB_HASH_IGNORECASE ) == 0 ) + { + hb_hashClearFlags( pHash, HB_HASH_BINARY ); + hb_hashSetFlags( pHash, HB_HASH_IGNORECASE | HB_HASH_RESORT ); + } + } + } + else + hb_errRT_BASE( EG_ARG, 2017, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS ); +} + +HB_FUNC( HB_HBINARY ) +{ + PHB_ITEM pHash = hb_param( 1, HB_IT_HASH ); + PHB_ITEM pValue = hb_param( 2, HB_IT_LOGICAL ); + + if( pHash ) + { + int iFlags = hb_hashGetFlags( pHash ); + hb_retl( ( iFlags & HB_HASH_BINARY ) != 0 ); + if( pValue ) + { + if( hb_itemGetL( pValue ) ) + { + if( ( iFlags & HB_HASH_BINARY ) == 0 ) + { + hb_hashClearFlags( pHash, HB_HASH_IGNORECASE ); + hb_hashSetFlags( pHash, HB_HASH_BINARY | HB_HASH_RESORT ); + } + } + else if( ( iFlags & HB_HASH_BINARY ) != 0 ) + { + hb_hashClearFlags( pHash, HB_HASH_BINARY ); + hb_hashSetFlags( pHash, HB_HASH_RESORT ); + } } } else @@ -688,3 +741,4 @@ HB_FUNC( HB_HDEFAULT ) HB_FUNC( HB_HSETAUTOADD ) { HB_FUNC_EXEC( HB_HAUTOADD ); hb_itemReturn( hb_param( 1, HB_IT_HASH ) ); } HB_FUNC( HB_HSETCASEMATCH ) { HB_FUNC_EXEC( HB_HCASEMATCH ); hb_itemReturn( hb_param( 1, HB_IT_HASH ) ); } +HB_FUNC( HB_HSETBINARY ) { HB_FUNC_EXEC( HB_HBINARY ); hb_itemReturn( hb_param( 1, HB_IT_HASH ) ); }