Files
harbour-core/harbour/source/vm/hashes.c
Przemyslaw Czerpak 3d4a97040b 2008-07-17 18:19 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl)
* harbour/contrib/hbw32/dllcall.c
    * minor cleanup in return values

  * harbour/contrib/hbole/ole2.c
    * cleaned casting in UNICODE conversions

  * harbour/source/common/expropt2.c
    * replicate CA-Cl*pper compile time optimization bugs:
         "" $ <literString>         => .T.
         AT( "", <literString> )    => 1
         CHR( 256 )                 => ""
      only when Harbour extensions (-kh) are not enabled, f.e.
      in strict Clipper compatibility mode (-kc)

  * harbour/utils/hbtest/rt_hvma.prg
  * harbour/utils/hbtest/rt_str.prg
    * updated to test Clipper and Harbour compile time modes in
      the above situations

  * harbour/common.mak
  * harbour/source/rtl/Makefile
  - harbour/source/rtl/strings.c
  * harbour/source/common/hbstr.c
    * moved hb_strEmpty() from RTL to COMMON library

  * harbour/include/hbexprb.c
  * harbour/include/hbexprop.h
  * harbour/source/common/expropt2.c
    + added compile time optimization for EMPTY() function
    ; removed 'TODO: empty optimization' note

  * harbour/source/rtl/itemseri.c
    % compress trailing spaces during string item serialization

  * harbour/include/hbapi.h
  * harbour/source/vm/hashes.c
    + added hb_hashAddNew() - works like hb_hashAdd() but it adds item
      only if new key is used

  * harbour/source/vm/hvm.c
    * use hb_hashAddNew() instead of hb_hashAdd() for HB_P_HASHGEN
      I cannot revert the order of hash item during compilation because
      it will also change the order of user expression evaluation.
2008-07-17 16:28:11 +00:00

849 lines
26 KiB
C

/*
* $Id$
*/
/*
* Harbour Project source code:
* The Hash tables API (C level)
*
* Copyright 2007 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
* www - http://www.harbour-project.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/).
*
* As a special exception, the Harbour Project gives permission for
* additional uses of the text contained in its release of Harbour.
*
* The exception is that, if you link the Harbour libraries with other
* files to produce an executable, this does not by itself cause the
* resulting executable to be covered by the GNU General Public License.
* Your use of that executable is in no way restricted on account of
* linking the Harbour library code into it.
*
* This exception does not however invalidate any other reasons why
* the executable file might be covered by the GNU General Public License.
*
* This exception applies only to the code released by the Harbour
* Project under the name Harbour. If you copy code from other
* Harbour Project or Free Software Foundation releases into a copy of
* Harbour, as the General Public License permits, the exception does
* not apply to the code that you add in this way. To avoid misleading
* anyone as to the status of such modified files, you must delete
* this exception notice from them.
*
* If you write modifications of your own for Harbour, it is your choice
* whether to permit this exception to apply to your modifications.
* If you do not wish that, delete this exception notice.
*
*/
#define _HB_HASH_INTERNAL_
#include "hbvmopt.h"
#include "hbapi.h"
#include "hbapiitm.h"
#include "hbapierr.h"
#include "hbapilng.h"
#include "hbvm.h"
#include "hbxvm.h"
#include "hbstack.h"
#define HB_HASH_ITEM_ALLOC 16
/* internal structures for hashes */
typedef struct _HB_HASHPAIR
{
HB_ITEM key;
HB_ITEM value;
} HB_HASHPAIR, * PHB_HASHPAIR;
typedef struct _HB_BASEHASH
{
PHB_HASHPAIR pPairs; /* pointer to the array of key/value pairs */
ULONG ulSize; /* size of allocated pair array */
ULONG ulLen; /* number of used items in pair array */
int iFlags; /* hash item flags */
PHB_ITEM pDefault; /* default autoadd value */
} HB_BASEHASH, * PHB_BASEHASH, * HB_BASEHASH_PTR;
void hb_hashRefGrabage( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashRefGrabage(%p)", pHash));
if( HB_IS_HASH( pHash ) && pHash->item.asHash.value->ulLen > 0 )
{
PHB_HASHPAIR pPairs = pHash->item.asHash.value->pPairs;
ULONG ulLen = pHash->item.asHash.value->ulLen;
while( ulLen-- )
{
if( HB_IS_GCITEM( &pPairs[ ulLen ].key ) )
hb_gcItemRef( &pPairs[ ulLen ].key );
if( HB_IS_GCITEM( &pPairs[ ulLen ].value ) )
hb_gcItemRef( &pPairs[ ulLen ].value );
}
}
}
/* This releases hash when called from the garbage collector */
static HB_GARBAGE_FUNC( hb_hashReleaseGarbage )
{
PHB_BASEHASH pBaseHash = ( PHB_BASEHASH ) Cargo;
HB_TRACE(HB_TR_INFO, ("hb_hashReleaseGarbage(%p)", pBaseHash ));
if( pBaseHash->ulSize > 0 )
{
PHB_HASHPAIR pPairs = pBaseHash->pPairs;
ULONG ulLen = pBaseHash->ulLen;
/*
* clear the pBaseHash->pPairs to avoid infinite loop in cross
* referenced items when pBaseArray is not freed due to buggy
* object destructor [druzus]
*/
pBaseHash->pPairs = NULL;
pBaseHash->ulLen = 0;
while( ulLen-- )
{
if( HB_IS_COMPLEX( &pPairs[ ulLen ].key ) )
hb_itemClear( &pPairs[ ulLen ].key );
if( HB_IS_COMPLEX( &pPairs[ ulLen ].value ) )
hb_itemClear( &pPairs[ ulLen ].value );
}
hb_xfree( pPairs );
}
if( pBaseHash->pDefault )
{
PHB_ITEM pDefault = pBaseHash->pDefault;
pBaseHash->pDefault = NULL;
hb_itemRelease( pDefault );
}
}
static int hb_hashItemCmp( PHB_ITEM pKey1, PHB_ITEM pKey2, BOOL fIgnoreCase )
{
if( HB_IS_STRING( pKey1 ) )
{
if( HB_IS_STRING( pKey2 ) )
{
if( fIgnoreCase )
return hb_itemStrICmp( pKey1, pKey2, TRUE );
else
return hb_itemStrCmp( pKey1, pKey2, TRUE );
}
else
return 1;
}
else if( HB_IS_DATE( pKey1 ) )
{
if( HB_IS_DATE( pKey2 ) )
return pKey1->item.asDate.value < pKey2->item.asDate.value ? -1 :
( pKey1->item.asDate.value > pKey2->item.asDate.value ? 1 : 0 );
else if( HB_IS_STRING( pKey2 ) )
return -1;
else
return 1;
}
else if( HB_IS_POINTER( pKey1 ) )
{
if( HB_IS_POINTER( pKey2 ) )
return pKey1->item.asPointer.value < pKey2->item.asPointer.value ? -1 :
( pKey1->item.asPointer.value > pKey2->item.asPointer.value ? 1 : 0 );
else if( HB_IS_STRING( pKey2 ) || HB_IS_DATE( pKey2 ) )
return -1;
else
return 1;
}
else if( HB_IS_NUMINT( pKey1 ) && HB_IS_NUMINT( pKey2 ) )
{
HB_LONG l1 = HB_ITEM_GET_NUMINTRAW( pKey1 ),
l2 = HB_ITEM_GET_NUMINTRAW( pKey2 );
return l1 < l2 ? -1 : ( l1 > l2 ? 1 : 0 );
}
else if( HB_IS_NUMERIC( pKey2 ) )
{
double d1 = hb_itemGetND( pKey1 ), d2 = hb_itemGetND( pKey2 );
return d1 < d2 ? -1 : ( d1 > d2 ? 1 : 0 );
}
return -1;
}
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;
int i;
while( ulLeft < ulRight )
{
ulMiddle = ( ulLeft + ulRight ) >> 1;
i = hb_hashItemCmp( &pBaseHash->pPairs[ ulMiddle ].key, pKey, fIgnoreCase );
if( i == 0 )
{
if( pulPos )
*pulPos = ulMiddle;
return TRUE;
}
else if( i < 0 )
ulLeft = ulMiddle + 1;
else
ulRight = ulMiddle;
}
if( pulPos )
*pulPos = ulLeft;
return FALSE;
}
static void hb_hashResize( PHB_BASEHASH pBaseHash, ULONG ulNewSize )
{
if( pBaseHash->ulSize < ulNewSize )
{
if( pBaseHash->ulSize )
pBaseHash->pPairs = ( PHB_HASHPAIR ) hb_xrealloc( pBaseHash->pPairs,
ulNewSize * sizeof( HB_HASHPAIR ) );
else
pBaseHash->pPairs = ( PHB_HASHPAIR ) hb_xgrab( ulNewSize * sizeof( HB_HASHPAIR ) );
do
{
pBaseHash->pPairs[ pBaseHash->ulSize ].key.type = HB_IT_NIL;
pBaseHash->pPairs[ pBaseHash->ulSize ].value.type = HB_IT_NIL;
}
while( ++pBaseHash->ulSize < ulNewSize );
}
else if( pBaseHash->ulSize > ulNewSize && pBaseHash->ulLen <= ulNewSize )
{
pBaseHash->ulSize = ulNewSize;
if( ulNewSize )
pBaseHash->pPairs = ( PHB_HASHPAIR ) hb_xrealloc( pBaseHash->pPairs,
ulNewSize * sizeof( HB_HASHPAIR ) );
else
{
hb_xfree( pBaseHash->pPairs );
pBaseHash->pPairs = NULL;
}
}
}
static PHB_ITEM hb_hashValuePtr( PHB_BASEHASH pBaseHash, PHB_ITEM pKey, BOOL fAdd )
{
ULONG ulPos;
if( !hb_hashFind( pBaseHash, pKey, &ulPos ) )
{
if( !fAdd )
return NULL;
if( pBaseHash->ulSize == pBaseHash->ulLen )
hb_hashResize( pBaseHash, pBaseHash->ulSize + HB_HASH_ITEM_ALLOC );
if( ulPos < pBaseHash->ulLen )
{
memmove( pBaseHash->pPairs + ulPos + 1, pBaseHash->pPairs + ulPos,
( pBaseHash->ulLen - ulPos ) * sizeof( HB_HASHPAIR ) );
pBaseHash->pPairs[ ulPos ].key.type = HB_IT_NIL;
pBaseHash->pPairs[ ulPos ].value.type = HB_IT_NIL;
}
hb_itemCopy( &pBaseHash->pPairs[ ulPos ].key, pKey );
pBaseHash->ulLen++;
if( pBaseHash->pDefault )
{
PHB_ITEM pDefault = hb_itemClone( pBaseHash->pDefault );
hb_itemMove( &pBaseHash->pPairs[ ulPos ].value, pDefault );
hb_itemRelease( pDefault );
}
}
return &pBaseHash->pPairs[ ulPos ].value;
}
static BOOL hb_hashNewValue( PHB_BASEHASH pBaseHash, PHB_ITEM pKey, PHB_ITEM pValue )
{
ULONG ulPos;
if( !hb_hashFind( pBaseHash, pKey, &ulPos ) )
{
if( pBaseHash->ulSize == pBaseHash->ulLen )
hb_hashResize( pBaseHash, pBaseHash->ulSize + HB_HASH_ITEM_ALLOC );
if( ulPos < pBaseHash->ulLen )
{
memmove( pBaseHash->pPairs + ulPos + 1, pBaseHash->pPairs + ulPos,
( pBaseHash->ulLen - ulPos ) * sizeof( HB_HASHPAIR ) );
pBaseHash->pPairs[ ulPos ].key.type = HB_IT_NIL;
pBaseHash->pPairs[ ulPos ].value.type = HB_IT_NIL;
}
hb_itemCopy( &pBaseHash->pPairs[ ulPos ].key, pKey );
hb_itemCopyFromRef( &pBaseHash->pPairs[ ulPos ].value, pValue );
pBaseHash->ulLen++;
return TRUE;
}
return FALSE;
}
static void hb_hashDelPair( PHB_BASEHASH pBaseHash, ULONG ulPos )
{
if( --pBaseHash->ulLen == 0 )
{
PHB_HASHPAIR pPairs = pBaseHash->pPairs;
pBaseHash->pPairs = NULL;
pBaseHash->ulSize = 0;
if( HB_IS_COMPLEX( &pPairs->key ) )
hb_itemClear( &pPairs->key );
if( HB_IS_COMPLEX( &pPairs->value ) )
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( pBaseHash->ulSize - pBaseHash->ulLen > ( HB_HASH_ITEM_ALLOC << 1 ) )
{
pBaseHash->ulSize -= HB_HASH_ITEM_ALLOC;
pBaseHash->pPairs = ( PHB_HASHPAIR ) hb_xrealloc( pBaseHash->pPairs,
pBaseHash->ulSize * sizeof( HB_HASHPAIR ) );
}
}
}
HB_EXPORT PHB_ITEM hb_hashNew( PHB_ITEM pItem )
{
PHB_BASEHASH pBaseHash;
HB_TRACE(HB_TR_DEBUG, ("hb_hashNew(%p)", pItem));
if( pItem == NULL )
pItem = hb_itemNew( NULL );
else if( HB_IS_COMPLEX( pItem ) )
hb_itemClear( pItem );
pBaseHash = ( PHB_BASEHASH ) hb_gcAlloc( sizeof( HB_BASEHASH ), hb_hashReleaseGarbage );
pBaseHash->pPairs = NULL;
pBaseHash->ulSize = 0;
pBaseHash->ulLen = 0;
pBaseHash->iFlags = HB_HASH_AUTOADD_ASSIGN;
pBaseHash->pDefault = NULL;
pItem->type = HB_IT_HASH;
pItem->item.asHash.value = pBaseHash;
return pItem;
}
HB_EXPORT ULONG hb_hashLen( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashLen(%p)", pHash));
if( HB_IS_HASH( pHash ) )
return pHash->item.asHash.value->ulLen;
else
return 0;
}
HB_EXPORT void hb_hashPreallocate( PHB_ITEM pHash, ULONG ulNewSize )
{
if( HB_IS_HASH( pHash ) )
hb_hashResize( pHash->item.asHash.value, ulNewSize );
}
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));
if( HB_IS_HASH( pHash ) && HB_IS_HASHKEY( pKey ) )
{
PHB_ITEM pDest = hb_hashValuePtr( pHash->item.asHash.value, pKey,
iFlags && ( pHash->item.asHash.value->iFlags & iFlags ) == iFlags );
if( pDest )
return HB_IS_BYREF( pDest ) ? hb_itemUnRef( pDest ) : pDest;
}
return NULL;
}
HB_EXPORT PHB_ITEM hb_hashGetItemRefPtr( PHB_ITEM pHash, PHB_ITEM pKey )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashGetItemRefPtr(%p,%p)", pHash, pKey));
if( HB_IS_HASH( pHash ) && HB_IS_HASHKEY( pKey ) )
{
PHB_ITEM pDest = hb_hashValuePtr( pHash->item.asHash.value, pKey,
( pHash->item.asHash.value->iFlags & HB_HASH_AUTOADD_REFERENCE ) ==
HB_HASH_AUTOADD_REFERENCE );
if( pDest )
{
if( !HB_IS_BYREF( pDest ) )
pDest = hb_memvarDetachLocal( pDest );
return pDest;
}
}
return NULL;
}
HB_EXPORT BOOL hb_hashScan( PHB_ITEM pHash, PHB_ITEM pKey, ULONG * pulPos )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashScan(%p,%p,%p)", pHash, pKey, pulPos));
if( HB_IS_HASH( pHash ) )
{
ULONG ulPos;
if( HB_IS_HASHKEY( pKey ) )
{
if( hb_hashFind( pHash->item.asHash.value, pKey, &ulPos ) )
{
if( pulPos )
*pulPos = ulPos + 1;
return TRUE;
}
}
else if( HB_IS_HASH( pKey ) && pKey->item.asHash.value->ulLen == 1 )
{
if( hb_hashFind( pHash->item.asHash.value, &pKey->item.asHash.value->pPairs[ 0 ].key, &ulPos ) )
{
PHB_ITEM pVal1 = &pHash->item.asHash.value->pPairs[ ulPos ].value;
PHB_ITEM pVal2 = &pKey->item.asHash.value->pPairs[ 0 ].value;
BOOL fResult = FALSE;
if( HB_IS_STRING( pVal1 ) && HB_IS_STRING( pVal2 ) )
fResult = hb_itemStrCmp( pVal1, pVal2, TRUE ) == 0;
else if( HB_IS_NUMINT( pVal1 ) && HB_IS_NUMINT( pVal2 ) )
fResult = HB_ITEM_GET_NUMINTRAW( pVal1 ) == HB_ITEM_GET_NUMINTRAW( pVal2 );
else if( HB_IS_NUMERIC( pVal1 ) && HB_IS_NUMERIC( pVal2 ) )
fResult = hb_itemGetND( pVal1 ) == hb_itemGetND( pVal2 );
else if( HB_IS_NIL( pVal1 ) && HB_IS_NIL( pVal2 ) )
fResult = TRUE;
else if( hb_itemType( pVal1 ) & hb_itemType( pVal2 ) )
{
hb_vmPush( pVal1 );
hb_vmPush( pVal2 );
if( !hb_xvmExactlyEqual() )
{
fResult = hb_itemGetL( hb_stackItemFromTop( -1 ) );
hb_stackPop();
}
}
if( fResult )
{
if( pulPos )
*pulPos = ulPos + 1;
return TRUE;
}
}
}
}
if( pulPos )
*pulPos = 0;
return FALSE;
}
HB_EXPORT BOOL hb_hashClear( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashClear(%p)", pHash));
if( HB_IS_HASH( pHash ) )
{
if( pHash->item.asHash.value->ulSize )
{
while( pHash->item.asHash.value->ulLen )
{
pHash->item.asHash.value->ulLen--;
if( HB_IS_COMPLEX( &pHash->item.asHash.value->pPairs[ pHash->item.asHash.value->ulLen ].key ) )
hb_itemClear( &pHash->item.asHash.value->pPairs[ pHash->item.asHash.value->ulLen ].key );
if( HB_IS_COMPLEX( &pHash->item.asHash.value->pPairs[ pHash->item.asHash.value->ulLen ].value ) )
hb_itemClear( &pHash->item.asHash.value->pPairs[ pHash->item.asHash.value->ulLen ].value );
}
/*
* This condition is a protection against recursive call
* from .prg object destructor [druzus]
*/
if( pHash->item.asHash.value->ulSize )
{
hb_xfree( pHash->item.asHash.value->pPairs );
pHash->item.asHash.value->pPairs = NULL;
pHash->item.asHash.value->ulSize = 0;
}
}
return TRUE;
}
return FALSE;
}
HB_EXPORT BOOL hb_hashDel( PHB_ITEM pHash, PHB_ITEM pKey )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashDel(%p,%p)", pHash, pKey));
if( HB_IS_HASH( pHash ) && HB_IS_HASHKEY( pKey ) )
{
PHB_BASEHASH pBaseHash = pHash->item.asHash.value;
ULONG ulPos;
if( hb_hashFind( pBaseHash, pKey, &ulPos ) )
{
hb_hashDelPair( pBaseHash, ulPos );
return TRUE;
}
}
return FALSE;
}
HB_EXPORT BOOL hb_hashRemove( PHB_ITEM pHash, PHB_ITEM pItem )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashRemove(%p,%p)", pHash, pItem));
if( HB_IS_HASH( pHash ) )
{
if( HB_IS_HASHKEY( pItem ) )
{
hb_hashDel( pHash, pItem );
return TRUE;
}
else if( HB_IS_ARRAY( pItem ) )
{
ULONG ul = 0;
PHB_ITEM pKey;
while( ( pKey = hb_arrayGetItemPtr( pItem, ++ul ) ) != NULL )
hb_hashDel( pHash, pKey );
return TRUE;
}
else if( HB_IS_HASH( pItem ) )
{
if( pHash->item.asHash.value == pItem->item.asHash.value )
hb_hashClear( pHash );
else
{
ULONG ulLen = 0;
while( ulLen < pItem->item.asHash.value->ulLen )
hb_hashDel( pHash, &pItem->item.asHash.value->pPairs[ ulLen++ ].key );
}
return TRUE;
}
}
return FALSE;
}
HB_EXPORT BOOL hb_hashAdd( PHB_ITEM pHash, PHB_ITEM pKey, PHB_ITEM pValue )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashAdd(%p,%p,%p)", pHash, pKey, pValue));
if( HB_IS_HASH( pHash ) && HB_IS_HASHKEY( pKey ) )
{
PHB_ITEM pDest = hb_hashValuePtr( pHash->item.asHash.value, pKey, TRUE );
if( pDest )
{
if( HB_IS_BYREF( pDest ) )
pDest = hb_itemUnRef( pDest );
if( pValue )
hb_itemCopyFromRef( pDest, pValue );
else
hb_itemSetNil( pDest );
return TRUE;
}
}
return FALSE;
}
HB_EXPORT BOOL hb_hashAddNew( PHB_ITEM pHash, PHB_ITEM pKey, PHB_ITEM pValue )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashAddNew(%p,%p,%p)", pHash, pKey, pValue));
if( HB_IS_HASH( pHash ) && HB_IS_HASHKEY( pKey ) )
return hb_hashNewValue( pHash->item.asHash.value, pKey, pValue );
else
return FALSE;
}
HB_EXPORT PHB_ITEM hb_hashGetKeyAt( PHB_ITEM pHash, ULONG ulPos )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashGetKeyAt(%p,%lu)", pHash, ulPos));
if( HB_IS_HASH( pHash ) && ulPos > 0 && ulPos <= pHash->item.asHash.value->ulLen )
return &pHash->item.asHash.value->pPairs[ ulPos - 1 ].key;
else
return NULL;
}
HB_EXPORT PHB_ITEM hb_hashGetValueAt( PHB_ITEM pHash, ULONG ulPos )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashGetValueAt(%p,%lu)", pHash, ulPos));
if( HB_IS_HASH( pHash ) && ulPos > 0 && ulPos <= pHash->item.asHash.value->ulLen )
return HB_IS_BYREF( &pHash->item.asHash.value->pPairs[ ulPos - 1 ].value ) ?
hb_itemUnRef( &pHash->item.asHash.value->pPairs[ ulPos - 1 ].value ) :
&pHash->item.asHash.value->pPairs[ ulPos - 1 ].value;
else
return NULL;
}
HB_EXPORT BOOL hb_hashDelAt( PHB_ITEM pHash, ULONG ulPos )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashDelAt(%p,%lu)", pHash, ulPos));
if( HB_IS_HASH( pHash ) && ulPos > 0 && ulPos <= pHash->item.asHash.value->ulLen )
{
hb_hashDelPair( pHash->item.asHash.value, ulPos - 1 );
return TRUE;
}
else
return FALSE;
}
/* retrives the hash unique ID */
HB_EXPORT void * hb_hashId( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashId(%p)", pHash));
if( HB_IS_HASH( pHash ) )
return ( void * ) pHash->item.asHash.value;
else
return NULL;
}
void hb_hashCloneBody( PHB_ITEM pHash, PHB_ITEM pDest, PHB_NESTED_CLONED pClonedList )
{
ULONG ulPos;
HB_TRACE(HB_TR_DEBUG, ("hb_hashCloneBody(%p,%p,%p)", pHash, pDest, pClonedList));
hb_hashNew( pDest );
hb_hashResize( pDest->item.asHash.value, pHash->item.asHash.value->ulSize );
pDest->item.asHash.value->iFlags = pHash->item.asHash.value->iFlags;
if( pHash->item.asHash.value->pDefault )
pDest->item.asHash.value->pDefault =
hb_itemNew( pHash->item.asHash.value->pDefault );
for( ulPos = 0; ulPos < pHash->item.asHash.value->ulLen; ++ulPos )
{
PHB_ITEM pValue = &pHash->item.asHash.value->pPairs[ ulPos ].value;
if( HB_IS_BYREF( pValue ) )
hb_itemUnRef( pValue );
hb_itemCopy( &pDest->item.asHash.value->pPairs[ ulPos ].key,
&pHash->item.asHash.value->pPairs[ ulPos ].key );
pDest->item.asHash.value->ulLen++;
hb_cloneNested( &pDest->item.asHash.value->pPairs[ ulPos ].value, pValue, pClonedList );
}
}
HB_EXPORT PHB_ITEM hb_hashClone( PHB_ITEM pHash )
{
PHB_ITEM pDest;
HB_TRACE(HB_TR_DEBUG, ("hb_hashClone(%p)", pHash));
pDest = hb_itemNew( NULL );
if( HB_IS_HASH( pHash ) )
{
PHB_NESTED_CLONED pClonedList, pCloned;
pClonedList = ( PHB_NESTED_CLONED ) hb_xgrab( sizeof( HB_NESTED_CLONED ) );
pClonedList->value = ( void * ) pHash->item.asHash.value;
pClonedList->pDest = pDest;
pClonedList->pNext = NULL;
hb_hashCloneBody( pHash, pDest, pClonedList );
do
{
pCloned = pClonedList;
pClonedList = pClonedList->pNext;
hb_xfree( pCloned );
}
while( pClonedList );
}
return pDest;
}
HB_EXPORT void hb_hashJoin( PHB_ITEM pDest, PHB_ITEM pSource, int iType )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashJoin(%p,%p,%d)", pDest, pSource, iType));
if( HB_IS_HASH( pDest ) && HB_IS_HASH( pSource ) )
{
PHB_BASEHASH pBaseHash;
ULONG ulPos;
switch( iType )
{
case HB_HASH_UNION: /* OR */
pBaseHash = pSource->item.asHash.value;
for( ulPos = 0; ulPos < pBaseHash->ulLen; ++ulPos )
{
PHB_ITEM pVal = &pBaseHash->pPairs[ ulPos ].value;
if( HB_IS_BYREF( pVal ) )
pVal = hb_itemUnRef( pVal );
hb_hashAdd( pDest, &pBaseHash->pPairs[ ulPos ].key, pVal );
}
break;
case HB_HASH_INTERSECT: /* AND */
pBaseHash = pDest->item.asHash.value;
for( ulPos = 0; ulPos < pBaseHash->ulLen; ++ulPos )
{
if( !hb_hashFind( pSource->item.asHash.value,
&pBaseHash->pPairs[ ulPos ].key, NULL ) )
hb_hashDel( pDest, &pBaseHash->pPairs[ ulPos ].key );
}
break;
case HB_HASH_DIFFERENCE: /* XOR */
pBaseHash = pSource->item.asHash.value;
for( ulPos = 0; ulPos < pBaseHash->ulLen; ++ulPos )
{
if( !hb_hashDel( pDest, &pBaseHash->pPairs[ ulPos ].key ) )
{
PHB_ITEM pVal = &pBaseHash->pPairs[ ulPos ].value;
if( HB_IS_BYREF( pVal ) )
pVal = hb_itemUnRef( pVal );
hb_hashAdd( pDest, &pBaseHash->pPairs[ ulPos ].key, pVal );
}
}
break;
case HB_HASH_REMOVE: /* NOT -> h1 & ( h1 ^ h2 ) */
pBaseHash = pSource->item.asHash.value;
if( pDest->item.asHash.value == pBaseHash )
hb_hashClear( pDest );
else
{
for( ulPos = 0; ulPos < pBaseHash->ulLen; ++ulPos )
hb_hashDel( pDest, &pBaseHash->pPairs[ ulPos ].key );
}
break;
}
}
}
HB_EXPORT PHB_ITEM hb_hashGetKeys( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashGetKeys(%p)", pHash));
if( HB_IS_HASH( pHash ) )
{
PHB_ITEM pKeys = hb_itemArrayNew( hb_hashLen( pHash ) ), pKey;
ULONG ulPos = 0;
while( ( pKey = hb_hashGetKeyAt( pHash, ++ulPos ) ) != NULL )
{
PHB_ITEM pDest = hb_arrayGetItemPtr( pKeys, ulPos );
if( !pDest )
break;
hb_itemCopy( pDest, pKey );
}
return pKeys;
}
return NULL;
}
HB_EXPORT PHB_ITEM hb_hashGetValues( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashGetValues(%p)", pHash));
if( HB_IS_HASH( pHash ) )
{
PHB_ITEM pValues = hb_itemArrayNew( hb_hashLen( pHash ) ), pVal;
ULONG ulPos = 0;
while( ( pVal = hb_hashGetValueAt( pHash, ++ulPos ) ) != NULL )
{
PHB_ITEM pDest = hb_arrayGetItemPtr( pValues, ulPos );
if( !pDest )
break;
hb_itemCopy( pDest, pVal );
}
return pValues;
}
return NULL;
}
HB_EXPORT void hb_hashSetDefault( PHB_ITEM pHash, PHB_ITEM pValue )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashSetDefault(%p,%p)", pHash, pValue));
if( HB_IS_HASH( pHash ) )
{
if( pHash->item.asHash.value->pDefault )
{
hb_itemRelease( pHash->item.asHash.value->pDefault );
pHash->item.asHash.value->pDefault = NULL;
}
if( pValue && !HB_IS_NIL( pValue ) &&
( !HB_IS_HASH( pValue ) || pHash->item.asHash.value !=
pValue->item.asHash.value ) )
pHash->item.asHash.value->pDefault = hb_itemClone( pValue );
}
}
HB_EXPORT PHB_ITEM hb_hashGetDefault( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashGetDefault(%p)", pHash));
if( HB_IS_HASH( pHash ) )
return pHash->item.asHash.value->pDefault;
else
return NULL;
}
HB_EXPORT void hb_hashSetFlags( PHB_ITEM pHash, int iFlags )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashSetFlags(%p,%d)", pHash, iFlags));
if( HB_IS_HASH( pHash ) )
pHash->item.asHash.value->iFlags |= iFlags;
}
HB_EXPORT void hb_hashClearFlags( PHB_ITEM pHash, int iFlags )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashClearFlags(%p,%d)", pHash, iFlags));
if( HB_IS_HASH( pHash ) )
pHash->item.asHash.value->iFlags &= ~iFlags;
}
HB_EXPORT int hb_hashGetFlags( PHB_ITEM pHash )
{
HB_TRACE(HB_TR_DEBUG, ("hb_hashGetFlags(%p)", pHash));
if( HB_IS_HASH( pHash ) )
return pHash->item.asHash.value->iFlags;
else
return 0;
}