* tests/testsha1.prg
* tests/longdev.prg
* tests/clsccast.prg
* tests/cmphello.prg
* tests/clsicast.prg
* tests/testop.prg
* tests/clsscast.prg
* tests/objarr.prg
* tests/rddtest/rddmktst.prg
* tests/rddtest/adscl52.prg
* tests/rddtest/adscl53.prg
* tests/rddtest/ntxcl52.prg
* tests/rddtest/ntxcl53.prg
* tests/rddtest/cdxcl52.prg
* tests/rddtest/rddtst.prg
* tests/rddtest/cdxcl53.prg
* tests/transtst.prg
* tests/output.prg
* tests/ac_test2.prg
* tests/clsscope.prg
* tests/dosshell.prg
* tests/sbartest.prg
* tests/speedold.prg
* tests/sdf_test.prg
* tests/wcecon.prg
* tests/debugtst.prg
* tests/testdyn.c
* tests/multiarg.prg
* tests/fornext.prg
* tests/hbinline.prg
* tests/foreach2.prg
* tests/objasign.prg
* tests/scroll.prg
* tests/inherit.prg
* tests/rto_get.prg
* tests/boxtst2.prg
* tests/inkeytst.prg
* tests/overload.prg
* tests/cpinfo.prg
* tests/gtwin.prg
* tests/mt/mttest08.prg
* tests/mt/mttest09.prg
* tests/mt/mttest01.prg
* tests/mt/mttest10.prg
* tests/mt/mttest02.prg
* tests/mt/mttest11.prg
* tests/mt/mttest03.prg
* tests/mt/mttest12.prg
* tests/mt/mttest04.prg
* tests/mt/mttest05.prg
* tests/mt/mttest06.prg
* tests/mt/mttest07.prg
* tests/speedtst.prg
* tests/testsha2.prg
* tests/hsxtest.prg
* tests/arrayidx.prg
* tests/clsnv.prg
* tests/rto_tb.prg
* tests/gtchars.prg
* tests/disptest.prg
* tests/funcarr.prg
* tests/testhtml.prg
* tests/readhrb.prg
* tests/tstcolor.prg
* tests/gtxfnt.prg
* tests/devtest.prg
* tests/aliaslck.prg
* tests/stripem.prg
* tests/dynobj.prg
* tests/tb1.prg
* tests/round.prg
* tests/longstr.prg
* tests/testdyn.prg
* tests/testdyn1.prg
* tests/delimtst.prg
* tests/tstdspac.prg
* tests/version.prg
* tests/setkeys.prg
* tests/gtcolors.prg
* tests/destruct.prg
* tests/seconds.prg
* tests/gtkeys.prg
* tests/usrrdd/exarr.prg
* doc/en/diskspac.txt
* doc/en/string.txt
* doc/en/rdd.txt
* doc/en/hashes.txt
* doc/en/hb_apigt.txt
* doc/en/rddord.txt
* doc/en/hb_api.txt
* doc/en/hb_date.txt
* doc/en/math.txt
* doc/en/hb_vm.txt
* doc/en/treport.txt
* doc/en/terminal.txt
* doc/en/hb_apiln.txt
* doc/en/dir.txt
* doc/en/command.txt
* doc/en/rddmisc.txt
* doc/en/errsys.txt
* doc/en/nation.txt
* doc/en/var.txt
* doc/en/dbstrux.txt
* doc/en/datetime.txt
* doc/en/memo.txt
* doc/en/tgetlist.txt
* doc/en/tlabel.txt
* doc/en/1stread.txt
* doc/en/hb_set.txt
* doc/en/hb_compa.txt
* doc/en/hb_apier.txt
* doc/en/hbinet.txt
* doc/en/hb_macro.txt
* doc/en/array.txt
* doc/en/hb_apiit.txt
* doc/en/rdddb.txt
* doc/en/dbsdf.txt
* doc/en/hvm.txt
* doc/en/input.txt
* doc/en/dbdelim.txt
* doc/en/browse.txt
* doc/en/menu.txt
* doc/en/hb_apird.txt
* doc/en/hb_apifs.txt
* doc/en/file.txt
* doc/en/lang.txt
* doc/en/objfunc.txt
* doc/en/eval.txt
* doc/en/binnum.txt
* doc/en/tclass.txt
* doc/en/misc.txt
* doc/en/set.txt
* doc/en/readme.txt
* doc/man/harbour.1
* doc/man/hbmk2.1
* doc/man/hbpp.1
* doc/man/hbtest.1
* doc/man/hbrun.1
* examples/hbextern/hbextern.prg
* examples/pp/pp.c
* examples/pp/hbpragma.c
* examples/pp/hbppcore.c
* examples/pp/hbppcomp.c
* examples/pp/hbpptbl.c
* examples/pp/hbppdef.h
* examples/superlib/hbsuper.prg
* examples/hbsqlit2/hbsqlit2.ch
* examples/misc/mankala.prg
* examples/misc/guess.prg
* examples/rddado/adordd.prg
* examples/rddado/adordd.ch
* examples/hbapollo/apollo.ch
* examples/hbapollo/apollo.c
* examples/hbapollo/apollo1.prg
* examples/hbdoc2/gentpl.prg
* examples/hbdoc2/gentxt.prg
* examples/hbdoc2/tmplates.prg
* examples/hbdoc2/genxml.prg
* examples/hbdoc2/genhtml.prg
* examples/hbdoc2/hbdoc2.prg
* examples/hbdoc2/hbdoc2.ch
* examples/guestbk/guestbk.prg
* examples/httpsrv/uhttpd.ini
* examples/httpsrv/uhttpd.prg
* examples/httpsrv/cookie.prg
* examples/httpsrv/cgifunc.prg
* examples/httpsrv/session.prg
* examples/terminal/trm_cli.prg
* examples/terminal/terminal.prg
* examples/terminal/trm_srv.prg
* examples/terminal/trm_app.prg
* examples/hbbtree/hb_btree.h
* examples/hbbtree/hb_btree.ch
* examples/hbbtree/hb_btree.c
* examples/hbbtree/tbtree.prg
* examples/hscript/hscript.prg
* examples/hscript/dir.hs
* examples/hscript/multiply.hs
* examples/hscript/ugly.hs
* examples/hscript/hello.hs
* examples/gtwvw/hbole.h
* examples/gtwvw/wvwdraw.c
* examples/gtwvw/wvwmenu.c
* examples/gtwvw/gtwvw.c
* examples/gtwvw/wvwstbar.c
* examples/gtwvw/wvwcheck.c
* examples/gtwvw/wvwfuncs.c
* examples/gtwvw/wvwpush.c
* examples/gtwvw/wvwedit.c
* examples/gtwvw/wvwtbar.c
* examples/gtwvw/hbgtwvw.h
* Deleted 'www.' from harbour-project.org website name.
(www.harbour-project.org -> harbour-project.org)
1959 lines
59 KiB
C
1959 lines
59 KiB
C
/*
|
|
$Id$
|
|
*/
|
|
|
|
/*
|
|
* Harbour Project source code:
|
|
* HB_BTree source.
|
|
*
|
|
* Copyright 2002-2010 April White <april@users.sourceforge.net>
|
|
* www - http://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 of the License, or
|
|
* (at your option) any later version, with one exception:
|
|
*
|
|
* The exception is that if you link the Harbour Runtime Library (HRL)
|
|
* and/or the Harbour Virtual Machine (HVM) 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 HRL
|
|
* and/or HVM code into it.
|
|
*
|
|
* 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 program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit
|
|
* their web site at http://www.gnu.org/).
|
|
*
|
|
*/
|
|
|
|
/*
|
|
TODO:
|
|
- determine how to handle page buffers in a multi-user environment
|
|
- replace some of the for..next loops through the key/branches that
|
|
move them around with memmove()
|
|
- this may not be feasible - in a multi-user environ, the stack
|
|
would become invalid; I would have to save the current key and then
|
|
locate the next/prev as needed, locking the file for the duration
|
|
(as well, page buffering would not be allowed because a page in
|
|
memory would become invalid)
|
|
- complete Move() API
|
|
- when move right/left, follow any branch down
|
|
- when hit end of a node, pop from stack
|
|
- increment the position and follow branch down
|
|
- impliment ulFlags within hb_btreeopen() - see warning above
|
|
- clear im-memory flag
|
|
- get unique flag from file header
|
|
- detect change to header, only write the header as needed
|
|
- build page output buffer then write it
|
|
- read page input buffer then split it
|
|
- make MT safe
|
|
|
|
TOFIX:
|
|
- remove UNION's as they hide 32/64 size problems
|
|
- use HB_U32 as the standard data type of file reading/writing
|
|
- in-memory btree's cause a fatal error; this is a work in progress
|
|
*/
|
|
|
|
#include "hbvm.h"
|
|
#include "hbapi.h"
|
|
#include "hbapiitm.h"
|
|
#include "hbapifs.h"
|
|
#include "hbapierr.h"
|
|
#include "hbinit.h"
|
|
|
|
#include "hb_btree.h"
|
|
|
|
HB_EXTERN_BEGIN
|
|
|
|
#if !defined( DEBUG ) && !defined( NDEBUG )
|
|
#define NDEBUG
|
|
#else
|
|
#define PrintCRLF() hb_conOutStd( hb_conNewLine(), strlen( hb_conNewLine() ) )
|
|
#endif
|
|
|
|
#if defined( __GNUC__ )
|
|
|
|
#if 0
|
|
#define STRINGIFY_2( n ) #n
|
|
#define STRINGIFY_1( n ) STRINGIFY_2( n )
|
|
#define SRCLINENO __FUNCTION__ " (" STRINGIFY_1( __LINE__ ) ")"
|
|
#define FILESRCLINENO __FILE__ "." SRCLINENO
|
|
#elif 0
|
|
#define SRCLINENO __FUNCTION__
|
|
#else
|
|
#define SRCLINENO "%s (%d)", __FUNCTION__, __LINE__
|
|
#endif
|
|
|
|
#else
|
|
/*
|
|
#define STRINGIFY_2( n ) #n
|
|
#define STRINGIFY_1( n ) STRINGIFY_2( n )
|
|
#define SRCLINENO ( __FILE__ " (" STRINGIFY_1( __LINE__ ) ")" )
|
|
*/
|
|
#define SRCLINENO ""
|
|
#endif
|
|
|
|
#define HEADER_ID "BTR\x10"
|
|
|
|
#define NULLPAGE 0L
|
|
|
|
#define BTREENODEISNULL( pBTree, node ) ( HB_BOOL )( ( node ) == NULLPAGE )
|
|
#define READPAGE_IF_NEEDED( pBTree, node ) if ( node != pBTree->ioBuffer->xPage.ulPage ) ioBufferScan( pBTree, node )
|
|
#define CLEARKEYDATA( pBTree ) ( ( pBTree )->pThisKeyData->szKey[ 0 ] = '\0', \
|
|
( pBTree )->pThisKeyData->xData.lData = 0, \
|
|
( pBTree )->pThisKeyData->xData.pData = NULL )
|
|
|
|
#undef HB_BTREE_HEADERSIZE
|
|
#define HB_BTREE_HEADERSIZE 2048
|
|
|
|
typedef struct stack_item
|
|
{
|
|
HB_ULONG ulNode;
|
|
int iPosition;
|
|
} BTreeStackItem;
|
|
|
|
typedef struct stack_tag
|
|
{
|
|
HB_USHORT usCount;
|
|
BTreeStackItem items[ 1 ];
|
|
} BTreeStack;
|
|
|
|
#define STACKNODE( pStack ) ( *pStack )->items[ ( *pStack )->usCount - 1 ].ulNode
|
|
#define STACKPOSITION( pStack ) ( *pStack )->items[ ( *pStack )->usCount - 1 ].iPosition
|
|
|
|
/* TODO: create two structs, one for in-memory, one for files */
|
|
typedef struct ioBuffer_tag
|
|
{
|
|
struct ioBuffer_tag *prev, *next;
|
|
union {
|
|
HB_ULONG ulPage; /* not in-memory */
|
|
struct ioBuffer_tag * pPage; /* in-memory */
|
|
} xPage;
|
|
HB_BOOL IsDirty;
|
|
|
|
/* was: Buffer_T pBuffer; */
|
|
HB_ULONG * pulPageCount; /* TODO: use LE get macro to retrieve this; better yet, dont use this */
|
|
HB_ULONG * pulBranch;
|
|
union {
|
|
HB_LONG * plData; /* not in-memory */
|
|
PHB_ITEM * ppData; /* in-memory */
|
|
} xData;
|
|
HB_BYTE * szKey;
|
|
HB_BYTE Buffer[ 1 ];
|
|
} ioBuffer_T;
|
|
|
|
typedef int hb_BTreeFlags_T;
|
|
|
|
|
|
#define IsNormal 0
|
|
|
|
/* btree file access flags */
|
|
#define IsReadOnly HB_BTREE_READONLY
|
|
#define IsExclusive HB_BTREE_EXCLUSIVE
|
|
#define IsShared HB_BTREE_SHARED
|
|
|
|
/* btree control flags */
|
|
#define IsUnique HB_BTREE_UNIQUE
|
|
#define IsCaseLess HB_BTREE_CASELESS
|
|
#define IsInMemory HB_BTREE_INMEMORY
|
|
|
|
/* internal flags */
|
|
#define IsRecordFound ( 1 << 16 )
|
|
#define IsDuplicateKey ( 1 << 17 )
|
|
#define IsMultiBuffers ( 1 << 18 )
|
|
#define IsOptimized ( 1 << 19 )
|
|
|
|
#define GETFLAG( pBTree, flag ) ( HB_BOOL )( ( ( int ) ( pBTree )->ulFlags & ( flag ) ) == ( flag ) )
|
|
#define SETFLAG( pBTree, flag ) ( ( pBTree )->ulFlags |= ( flag ) )
|
|
#define RESETFLAG( pBTree, flag ) ( ( pBTree )->ulFlags &= ~( flag ) )
|
|
|
|
/* TODO: if 64 bit the size of the union may be different so this code will fail to work as expected */
|
|
typedef struct hb_KeyData_Tag
|
|
{
|
|
union {
|
|
HB_LONG lData; /* lData is placed first for alignment, thought it is secondary */
|
|
PHB_ITEM pData;
|
|
} xData;
|
|
HB_BYTE szKey[ 1 ];
|
|
} hb_KeyData_T;
|
|
|
|
typedef int ( * BTreeCmpFunc )( const char * l, const char * r, size_t n );
|
|
|
|
struct hb_BTree
|
|
{
|
|
char * szFileName;
|
|
HB_FHANDLE hFile;
|
|
HB_ULONG ulRootPage;
|
|
HB_ULONG ulFreePage;
|
|
HB_USHORT usPageSize;
|
|
HB_USHORT usKeySize;
|
|
HB_USHORT usMaxKeys;
|
|
HB_USHORT usMinKeys;
|
|
hb_BTreeFlags_T ulFlags;
|
|
HB_ULONG ulKeyCount;
|
|
hb_KeyData_T * pThisKeyData;
|
|
BTreeStack * pStack;
|
|
ioBuffer_T * ioBuffer;
|
|
void * BufferEnd;
|
|
HB_BOOL IsDirtyFlagAssignment; /* replaces const TRUE, and !GETFLAG( pBTree, IsInMemory ) */
|
|
|
|
BTreeCmpFunc pStrCompare;
|
|
};
|
|
|
|
#if !defined( DEBUG ) && !defined( NDEBUG )
|
|
HB_BOOL IsDebugging = HB_FALSE;
|
|
#else
|
|
#define IsDebugging HB_FALSE
|
|
#endif
|
|
|
|
static struct hb_BTree **s_BTree_List = NULL;
|
|
static int s_BTree_List_Count = 0;
|
|
|
|
/* forward declarations */
|
|
|
|
static HB_USHORT CountGet( struct hb_BTree * pBTree, HB_ULONG ulNode );
|
|
static hb_KeyData_T *KeyGet( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition, hb_KeyData_T *buffer );
|
|
static HB_ULONG BranchGet( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition );
|
|
|
|
/* end of forward declarations */
|
|
|
|
static void raiseError( HB_ULONG ulGenCode, HB_ULONG ulSubCode, const char * szDescription, const char * szOperation, int uiArguments )
|
|
{
|
|
PHB_ITEM pErr = hb_errRT_New(
|
|
ES_ERROR /* HB_USHORT uiSeverity */,
|
|
"HB_BTREE" /* const char * szSubSystem */,
|
|
ulGenCode /* HB_ULONG ulGenCode */,
|
|
ulSubCode /* HB_ULONG ulSubCode */,
|
|
szDescription /* const char * szDescription */,
|
|
szOperation /* const char * szOperation */,
|
|
0 /* HB_ERRCODE uiOsCode */,
|
|
EF_NONE /* HB_USHORT uiFlags */
|
|
);
|
|
|
|
if ( uiArguments > 0 )
|
|
{
|
|
hb_errPutArgs( pErr, uiArguments, hb_paramError( 1 ), hb_paramError( 2 ), hb_paramError( 3 ), hb_paramError( 4 ), hb_paramError( 5 ) );
|
|
}
|
|
|
|
hb_errLaunch( pErr );
|
|
hb_errRelease( pErr );
|
|
}
|
|
|
|
static void * BufferRealloc( void *buffer, HB_ULONG size )
|
|
{
|
|
void *tmpBuffer = hb_xgrab( size );
|
|
|
|
if ( buffer )
|
|
{
|
|
hb_xmemcpy( tmpBuffer, buffer, size );
|
|
hb_xfree( buffer );
|
|
}
|
|
/* else
|
|
hb_xmemset( tmpBuffer, '\0', size );*/
|
|
|
|
return tmpBuffer;
|
|
}
|
|
|
|
#define BufferAlloc( n ) BufferRealloc( NULL, ( n ) )
|
|
#define BufferRelease( b ) hb_xfree( b )
|
|
|
|
|
|
static ioBuffer_T * ioOneBufferAlloc( struct hb_BTree * pBTree, ioBuffer_T * prev, ioBuffer_T * next )
|
|
{
|
|
ioBuffer_T * thisptr;
|
|
|
|
thisptr = ( ioBuffer_T * ) BufferAlloc( sizeof( ioBuffer_T ) + pBTree->usPageSize );
|
|
hb_xmemset( thisptr, '\0', sizeof( ioBuffer_T ) + pBTree->usPageSize );
|
|
|
|
thisptr->pulPageCount = ( HB_ULONG * )( thisptr->Buffer );
|
|
thisptr->pulBranch = ( HB_ULONG * )&thisptr->pulPageCount[ 1 ];
|
|
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
thisptr->xData.ppData = ( PHB_ITEM * )&thisptr->pulBranch[ pBTree->usMaxKeys + 1 ];
|
|
thisptr->szKey = ( HB_BYTE * )&thisptr->xData.ppData[ pBTree->usMaxKeys ];
|
|
}
|
|
else
|
|
{
|
|
thisptr->xData.plData = ( HB_LONG * )&thisptr->pulBranch[ pBTree->usMaxKeys + 1 ];
|
|
thisptr->szKey = ( HB_BYTE * )&thisptr->xData.plData[ pBTree->usMaxKeys ];
|
|
}
|
|
|
|
thisptr->xPage.ulPage = NULLPAGE;
|
|
thisptr->xPage.pPage = 0;
|
|
thisptr->IsDirty = HB_FALSE;
|
|
|
|
thisptr->prev = prev;
|
|
thisptr->next = next;
|
|
|
|
return thisptr;
|
|
}
|
|
|
|
/* a link list 'most recently access' page buffering system */
|
|
static void ioBufferAlloc( struct hb_BTree * pBTree, HB_ULONG ulBuffers )
|
|
{
|
|
ioBuffer_T *thisptr = NULL, *last = NULL;
|
|
|
|
if ( ulBuffers == 0 )
|
|
ulBuffers = 1;
|
|
|
|
if ( ulBuffers > 1 || GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
SETFLAG( pBTree, IsMultiBuffers );
|
|
}
|
|
else
|
|
{
|
|
RESETFLAG( pBTree, IsMultiBuffers );
|
|
}
|
|
|
|
while ( ulBuffers-- > 0 )
|
|
{
|
|
thisptr = ioOneBufferAlloc( pBTree, NULL, last );
|
|
if ( last ) last->prev = thisptr;
|
|
last = thisptr;
|
|
}
|
|
|
|
pBTree->ioBuffer = thisptr;
|
|
}
|
|
|
|
static void ioBufferWrite( struct hb_BTree * pBTree, ioBuffer_T *thisptr )
|
|
{
|
|
hb_fsSeek( pBTree->hFile, thisptr->xPage.ulPage, FS_SET );
|
|
if ( hb_fsWrite( pBTree->hFile, thisptr->Buffer, pBTree->usPageSize ) != pBTree->usPageSize )
|
|
{
|
|
raiseError( EG_WRITE, HB_BTREE_EC_WRITEERROR, "write error", "ioBufferWrite*", 0 );
|
|
}
|
|
thisptr->IsDirty = HB_FALSE;
|
|
}
|
|
|
|
static void ioBufferRead( struct hb_BTree * pBTree, HB_ULONG ulNode )
|
|
{
|
|
ioBuffer_T *thisptr = pBTree->ioBuffer;
|
|
|
|
if ( thisptr->IsDirty )
|
|
{
|
|
ioBufferWrite( pBTree, thisptr );
|
|
thisptr->IsDirty = HB_FALSE;
|
|
}
|
|
thisptr->xPage.ulPage = ulNode;
|
|
hb_fsSeek( pBTree->hFile, thisptr->xPage.ulPage, FS_SET );
|
|
hb_fsRead( pBTree->hFile, thisptr->Buffer, pBTree->usPageSize );
|
|
}
|
|
|
|
static void ioBufferRelease( struct hb_BTree * pBTree )
|
|
{
|
|
ioBuffer_T *next, *thisptr;
|
|
HB_ULONG n;
|
|
|
|
for ( thisptr = pBTree->ioBuffer; thisptr; thisptr = next )
|
|
{
|
|
next = thisptr->next;
|
|
if ( thisptr->IsDirty ) /* cannot be in-memory in this case */
|
|
{
|
|
ioBufferWrite( pBTree, thisptr );
|
|
}
|
|
else if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
for ( n = 0; n < *thisptr->pulPageCount; n++ )
|
|
{
|
|
/* { printf( "%p", thisptr->xData.ppData[ n ] ); PrintCRLF(); }*/
|
|
hb_itemRelease( thisptr->xData.ppData[ n ] );
|
|
}
|
|
}
|
|
BufferRelease( thisptr );
|
|
}
|
|
pBTree->ioBuffer = NULL;
|
|
}
|
|
|
|
#if 0 /* keep just in case, but its functionality is built into ioBufferRelease */
|
|
static void ioBufferFlush( struct hb_BTree * pBTree )
|
|
{
|
|
ioBuffer_T *thisptr;
|
|
|
|
for ( thisptr = pBTree->ioBuffer; thisptr; thisptr = thisptr->next )
|
|
{
|
|
if ( thisptr->IsDirty ) ioBufferWrite( pBTree, thisptr );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void ioBufferScan( struct hb_BTree * pBTree, HB_ULONG page )
|
|
{
|
|
ioBuffer_T *thisptr;
|
|
ioBuffer_T *thisnext, *thisprev;
|
|
|
|
/* locate either the buffer the page is in, the first empty buffer,
|
|
or the last buffer in the list */
|
|
for ( thisptr = pBTree->ioBuffer;
|
|
thisptr && !BTREENODEISNULL( pBTree, thisptr->xPage.ulPage ) && thisptr->xPage.ulPage != page && thisptr->next;
|
|
thisptr = thisptr->next ) {}
|
|
|
|
/* only shuffle the buffers if the target buffer is not the root buffer */
|
|
if ( thisptr != pBTree->ioBuffer )
|
|
{
|
|
/* minimize writes */
|
|
if ( thisptr->xPage.ulPage != page && thisptr->IsDirty )
|
|
{
|
|
ioBufferWrite( pBTree, thisptr );
|
|
}
|
|
|
|
thisprev = thisptr->prev;
|
|
thisnext = thisptr->next;
|
|
|
|
if ( thisnext ) thisnext->prev = thisprev;
|
|
if ( thisprev ) thisprev->next = thisnext;
|
|
|
|
thisptr->prev = NULL;
|
|
thisptr->next = pBTree->ioBuffer;
|
|
pBTree->ioBuffer->prev = thisptr;
|
|
pBTree->ioBuffer = thisptr;
|
|
}
|
|
else if ( thisptr->IsDirty )
|
|
ioBufferWrite( pBTree, thisptr );
|
|
|
|
/* Grow() will call this with a null page, to flush & shuffle buffers */
|
|
if ( !BTREENODEISNULL( pBTree, page ) && thisptr->xPage.ulPage != page && !GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
ioBufferRead( pBTree, page );
|
|
}
|
|
}
|
|
|
|
static void StackNew( BTreeStack **pStack )
|
|
{
|
|
*pStack = ( BTreeStack * )BufferRealloc( *pStack, sizeof( **pStack ) );
|
|
( *pStack )->usCount = 0;
|
|
}
|
|
|
|
static void StackPush( BTreeStack **pStack, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
*pStack = ( BTreeStack * )BufferRealloc( *pStack, sizeof( **pStack ) + ( ( *pStack )->usCount + 1 ) * sizeof( ( *pStack )->items[ 0 ] ) );
|
|
( *pStack )->items[ ( *pStack )->usCount ].ulNode = ulNode;
|
|
( *pStack )->items[ ( *pStack )->usCount ].iPosition = iPosition;
|
|
( *pStack )->usCount++;
|
|
}
|
|
|
|
static void StackPop( BTreeStack **pStack, HB_ULONG *pulNode, int *piPosition )
|
|
{
|
|
if ( ( *pStack )->usCount )
|
|
{
|
|
( *pStack )->usCount--;
|
|
*pulNode = ( *pStack )->items[ ( *pStack )->usCount ].ulNode;
|
|
*piPosition = ( *pStack )->items[ ( *pStack )->usCount ].iPosition;
|
|
*pStack = ( BTreeStack * )BufferRealloc( *pStack, sizeof( **pStack ) + ( ( *pStack )->usCount + 1 ) * sizeof( ( *pStack )->items[ 0 ] ) );
|
|
}
|
|
}
|
|
|
|
static void StackPeek( BTreeStack **pStack, HB_ULONG *pulNode, int *piPosition )
|
|
{
|
|
if ( ( *pStack )->usCount )
|
|
{
|
|
*pulNode = ( *pStack )->items[ ( *pStack )->usCount - 1 ].ulNode;
|
|
*piPosition = ( *pStack )->items[ ( *pStack )->usCount - 1 ].iPosition;
|
|
}
|
|
else
|
|
{
|
|
*pulNode = 0;
|
|
*piPosition = 0;
|
|
}
|
|
}
|
|
|
|
static HB_LONG StackSkip( struct hb_BTree * pBTree, BTreeStack **pStack, HB_LONG records )
|
|
{
|
|
HB_ULONG ulNode;
|
|
int iPosition;
|
|
HB_LONG recordsskipped = 0;
|
|
|
|
if ( ( *pStack )->usCount == 0 )
|
|
{
|
|
/* todo: raise an error? */
|
|
raiseError( EG_CORRUPTION, HB_BTREE_EC_STACKSKIP, "internal stack skip error", "StackSkip*", 0 );
|
|
}
|
|
|
|
if ( records == 0 )
|
|
{
|
|
StackPeek( pStack, &ulNode, &iPosition );
|
|
}
|
|
else if ( records > 0 )
|
|
{
|
|
while ( records-- > 0 )
|
|
{
|
|
ulNode = BranchGet( pBTree, STACKNODE( pStack ), STACKPOSITION( pStack ) );
|
|
if ( !BTREENODEISNULL( pBTree, ulNode ) )
|
|
{
|
|
StackPush( pStack, ulNode, STACKPOSITION( pStack ) );
|
|
while ( !BTREENODEISNULL( pBTree, ( ulNode = BranchGet( pBTree, ulNode, 0 ) ) ) )
|
|
{
|
|
/* StackPush( pStack, BranchGet( pBTree, ulNode, 0 ), 0 );*/
|
|
StackPush( pStack, ulNode, 0 );
|
|
}
|
|
STACKPOSITION( pStack ) = 1;
|
|
recordsskipped++;
|
|
}
|
|
else if ( STACKPOSITION( pStack ) < CountGet( pBTree, STACKNODE( pStack ) ) )
|
|
{
|
|
STACKPOSITION( pStack )++;
|
|
recordsskipped++;
|
|
}
|
|
else
|
|
{
|
|
StackPop( pStack, &ulNode, &iPosition );
|
|
if ( ( *pStack )->usCount == 0 )
|
|
{
|
|
break;
|
|
}
|
|
else if ( STACKPOSITION( pStack ) < CountGet( pBTree, STACKNODE( pStack ) ) )
|
|
{
|
|
STACKPOSITION( pStack )++;
|
|
recordsskipped++;
|
|
}
|
|
else
|
|
{
|
|
STACKPOSITION( pStack )++;
|
|
records++; /* force the loop to retry */
|
|
}
|
|
}
|
|
}
|
|
StackPeek( pStack, &ulNode, &iPosition );
|
|
}
|
|
else /*if ( records < 0 )*/
|
|
{
|
|
while ( records++ < 0 )
|
|
{
|
|
if ( STACKPOSITION( pStack ) > 0 )
|
|
ulNode = BranchGet( pBTree, STACKNODE( pStack ), --STACKPOSITION( pStack ) );
|
|
else
|
|
ulNode = NULLPAGE;
|
|
if ( !BTREENODEISNULL( pBTree, ulNode ) )
|
|
{
|
|
StackPush( pStack, ulNode, CountGet( pBTree, ulNode ) );
|
|
while ( !BTREENODEISNULL( pBTree, ( ulNode = BranchGet( pBTree, ulNode, CountGet( pBTree, ulNode ) ) ) ) )
|
|
{
|
|
StackPush( pStack, ulNode, CountGet( pBTree, ulNode ) );
|
|
}
|
|
recordsskipped--;
|
|
}
|
|
else if ( STACKPOSITION( pStack ) > 0 )
|
|
{
|
|
recordsskipped--;
|
|
}
|
|
else
|
|
{
|
|
StackPop( pStack, &ulNode, &iPosition );
|
|
if ( ( *pStack )->usCount == 0 )
|
|
{
|
|
break;
|
|
}
|
|
else if ( STACKPOSITION( pStack ) > 0 )
|
|
{
|
|
recordsskipped--;
|
|
}
|
|
else
|
|
{
|
|
records--; /* force the loop to retry */
|
|
}
|
|
}
|
|
}
|
|
StackPeek( pStack, &ulNode, &iPosition );
|
|
}
|
|
|
|
if ( ulNode == 0 && iPosition == 0 )
|
|
CLEARKEYDATA( pBTree );
|
|
else
|
|
KeyGet( pBTree, ulNode, iPosition, pBTree->pThisKeyData );
|
|
|
|
return recordsskipped;
|
|
}
|
|
|
|
static void StackRelease( BTreeStack **pStack )
|
|
{
|
|
if ( *pStack )
|
|
{
|
|
BufferRelease( *pStack );
|
|
*pStack = NULL;
|
|
}
|
|
}
|
|
|
|
static void HeaderWrite( struct hb_BTree * pBTree )
|
|
{
|
|
HB_BYTE TmpHeader[ HB_BTREE_HEADERSIZE ];
|
|
HB_BYTE * pHeader = &TmpHeader[ 0 ];
|
|
HB_U32 uiHeaderSize = HB_BTREE_HEADERSIZE;
|
|
|
|
/*
|
|
header [4 bytes]
|
|
ulFreePage [4 bytes, little endian]
|
|
usPageSize [2 bytes, little endian]
|
|
usKeySize [2 bytes, little endian]
|
|
ulRootPage [4 bytes, little endian]
|
|
ulFlags [4 bytes, little endian]
|
|
ulKeyCount [4 bytes, little endian]
|
|
*/
|
|
|
|
hb_xmemset( TmpHeader, '\0', sizeof( TmpHeader ) );
|
|
|
|
hb_xmemcpy( pHeader, HEADER_ID, sizeof( HEADER_ID ) - 1 ); pHeader += sizeof( HEADER_ID ) - 1;
|
|
HB_PUT_LE_UINT32( pHeader, uiHeaderSize ); pHeader += 4;
|
|
HB_PUT_LE_UINT32( pHeader, ( HB_U32 ) pBTree->usPageSize ); pHeader += 4;
|
|
HB_PUT_LE_UINT32( pHeader, ( HB_U32 ) pBTree->usKeySize ); pHeader += 4;
|
|
HB_PUT_LE_UINT32( pHeader, ( HB_U32 ) pBTree->usMaxKeys ); pHeader += 4;
|
|
HB_PUT_LE_UINT32( pHeader, ( HB_U32 ) pBTree->usMinKeys ); pHeader += 4;
|
|
HB_PUT_LE_UINT32( pHeader, pBTree->ulFlags );
|
|
|
|
pHeader = &TmpHeader[ 64 ];
|
|
HB_PUT_LE_UINT32( pHeader, pBTree->ulRootPage ); pHeader += 4;
|
|
HB_PUT_LE_UINT32( pHeader, pBTree->ulFreePage ); pHeader += 4;
|
|
HB_PUT_LE_UINT32( pHeader, pBTree->ulKeyCount );
|
|
|
|
hb_fsSeek( pBTree->hFile, 0, FS_SET );
|
|
if ( hb_fsWrite( pBTree->hFile, TmpHeader, sizeof( TmpHeader ) ) != sizeof( TmpHeader ) )
|
|
{
|
|
raiseError( EG_WRITE, HB_BTREE_EC_WRITEERROR, "write error", "HeaderWrite*", 0 );
|
|
}
|
|
}
|
|
|
|
static HB_ULONG Grow( struct hb_BTree * pBTree )
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
ioBuffer_T * thisptr;
|
|
|
|
thisptr = ioOneBufferAlloc( pBTree, NULL, pBTree->ioBuffer );
|
|
thisptr->xPage.pPage = thisptr;
|
|
if ( pBTree->ioBuffer ) pBTree->ioBuffer->prev = thisptr;
|
|
pBTree->ioBuffer = thisptr;
|
|
}
|
|
else
|
|
{
|
|
/* locate a page to use */
|
|
ioBufferScan( pBTree, NULLPAGE );
|
|
|
|
if ( BTREENODEISNULL( pBTree, pBTree->ulFreePage ) )
|
|
{
|
|
HB_BYTE *buffer = pBTree->ioBuffer->Buffer;
|
|
|
|
pBTree->ioBuffer->xPage.ulPage = hb_fsSeek( pBTree->hFile, 0, FS_END );
|
|
hb_xmemset( buffer, '\0', pBTree->usPageSize );
|
|
if ( pBTree->usPageSize != hb_fsWrite( pBTree->hFile, buffer, pBTree->usPageSize ) )
|
|
{
|
|
raiseError( EG_WRITE, HB_BTREE_EC_WRITEERROR, "write error", "Grow*", 0 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char buffer[ sizeof( pBTree->ulFreePage ) ];
|
|
pBTree->ioBuffer->xPage.ulPage = hb_fsSeek( pBTree->hFile, pBTree->ulFreePage, FS_SET );
|
|
hb_fsRead( pBTree->hFile, buffer, sizeof( pBTree->ulFreePage ) );
|
|
pBTree->ulFreePage = HB_GET_LE_UINT32( buffer );
|
|
}
|
|
pBTree->ioBuffer->IsDirty = HB_TRUE;
|
|
}
|
|
|
|
return pBTree->ioBuffer->xPage.ulPage;
|
|
}
|
|
|
|
static void Prune( struct hb_BTree * pBTree, HB_ULONG ulNode )
|
|
{
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
ioBuffer_T * thisptr;
|
|
HB_ULONG n;
|
|
|
|
for ( thisptr = pBTree->ioBuffer; thisptr && thisptr->xPage.ulPage != ulNode; thisptr = thisptr->next ) {}
|
|
|
|
if ( thisptr->prev ) thisptr->prev->next = thisptr->next;
|
|
if ( thisptr->next ) thisptr->next->prev = thisptr->prev;
|
|
|
|
if ( thisptr == pBTree->ioBuffer ) /* root page */
|
|
pBTree->ioBuffer = thisptr->next;
|
|
|
|
for ( n = 0; n < *thisptr->pulPageCount; n++ )
|
|
{
|
|
hb_itemRelease( thisptr->xData.ppData[ n ] );
|
|
}
|
|
hb_xfree( thisptr );
|
|
}
|
|
else
|
|
{
|
|
char buffer[ sizeof( pBTree->ulFreePage ) ];
|
|
hb_fsSeek( pBTree->hFile, ulNode, FS_SET );
|
|
HB_PUT_LE_UINT32( buffer, pBTree->ulFreePage );
|
|
if ( hb_fsWrite( pBTree->hFile, buffer, sizeof( pBTree->ulFreePage ) ) != sizeof( pBTree->ulFreePage ) )
|
|
{
|
|
raiseError( EG_WRITE, HB_BTREE_EC_WRITEERROR, "write error", "Prune*", 0 );
|
|
}
|
|
pBTree->ulFreePage = ulNode;
|
|
}
|
|
}
|
|
|
|
static HB_USHORT CountGet( struct hb_BTree * pBTree, HB_ULONG ulNode )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
return ( HB_USHORT )( *pBTree->ioBuffer->pulPageCount );
|
|
}
|
|
|
|
static void CountSet( struct hb_BTree * pBTree, HB_ULONG ulNode, HB_ULONG newPageCount )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
*pBTree->ioBuffer->pulPageCount = newPageCount;
|
|
pBTree->ioBuffer->IsDirty = pBTree->IsDirtyFlagAssignment;
|
|
}
|
|
|
|
static HB_USHORT CountAdj( struct hb_BTree * pBTree, HB_ULONG ulNode, HB_LONG adjPageCount )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
|
|
*pBTree->ioBuffer->pulPageCount += adjPageCount;
|
|
pBTree->ioBuffer->IsDirty = pBTree->IsDirtyFlagAssignment;
|
|
|
|
return ( HB_USHORT )*pBTree->ioBuffer->pulPageCount;
|
|
}
|
|
|
|
static void NodeCopy( struct hb_BTree * pBTree, HB_ULONG toNode, int toPosition, HB_ULONG fromNode, int fromPosition, hb_KeyData_T *buffer )
|
|
{
|
|
HB_ULONG ulBranch;
|
|
HB_LONG lData = 0;
|
|
PHB_ITEM pData = NULL;
|
|
|
|
READPAGE_IF_NEEDED( pBTree, fromNode );
|
|
ulBranch = pBTree->ioBuffer->pulBranch[ fromPosition ];
|
|
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
pData = pBTree->ioBuffer->xData.ppData[ fromPosition - 1 ];
|
|
}
|
|
else
|
|
{
|
|
lData = pBTree->ioBuffer->xData.plData[ fromPosition - 1 ];
|
|
}
|
|
|
|
hb_xmemcpy( buffer->szKey, pBTree->ioBuffer->szKey + ( fromPosition - 1 ) * pBTree->usKeySize, pBTree->usKeySize );
|
|
buffer->szKey[ pBTree->usKeySize ] = '\0';
|
|
|
|
READPAGE_IF_NEEDED( pBTree, toNode );
|
|
pBTree->ioBuffer->pulBranch[ toPosition ] = ulBranch;
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
pBTree->ioBuffer->xData.ppData[ toPosition - 1 ] = pData;
|
|
}
|
|
else
|
|
{
|
|
pBTree->ioBuffer->xData.plData[ toPosition - 1 ] = lData;
|
|
}
|
|
hb_xmemcpy( pBTree->ioBuffer->szKey + ( toPosition - 1 ) * pBTree->usKeySize, buffer->szKey, pBTree->usKeySize );
|
|
pBTree->ioBuffer->IsDirty = pBTree->IsDirtyFlagAssignment;
|
|
}
|
|
|
|
static HB_ULONG BranchGet( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
return pBTree->ioBuffer->pulBranch[ iPosition ];
|
|
}
|
|
|
|
#if defined( DEBUG )
|
|
static void Chk4Loop( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition, HB_ULONG ulBranch )
|
|
{
|
|
/*
|
|
int i;
|
|
for ( i = 0; i < CountGet( pBTree, ulNode ); i++ )
|
|
if ( i != iPosition && pBTree->ioBuffer->pulBranch[ i ] == ulBranch && ulBranch )
|
|
{
|
|
HB_TRACE( HB_TR_ERROR, "Chk4Loop( nbranch %ld exists in page %ld (position %d, found at %d, count %d) )",
|
|
( long )ulBranch,
|
|
( long )ulNode,
|
|
( int )iPosition,
|
|
( int )i,
|
|
CountGet( pBTree, ulNode ) );
|
|
}
|
|
*/
|
|
if ( ulBranch > filelength( pBTree->hFile ) )
|
|
HB_TRACE( HB_TR_ERROR, "Chk4Loop( nbranch %ld exceeds file size in page %ld (position %d, count %d) )",
|
|
( long )ulBranch,
|
|
( long )ulNode,
|
|
( int )iPosition,
|
|
( int )CountGet( pBTree, ulNode ) );
|
|
}
|
|
#endif
|
|
|
|
static void BranchSet( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition, HB_ULONG ulBranch )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
/*
|
|
if ( ulBranch > filelength( pBTree->hFile ) )
|
|
HB_TRACE( HB_TR_ERROR, "BranchSet( nbranch set %ld exceeds file size in page %ld (position %d, count %d) )",
|
|
( long )ulBranch,
|
|
( long )ulNode,
|
|
( int )iPosition,
|
|
CountGet( pBTree, ulNode ) );
|
|
*/
|
|
|
|
pBTree->ioBuffer->pulBranch[ iPosition ] = ulBranch;
|
|
pBTree->ioBuffer->IsDirty = pBTree->IsDirtyFlagAssignment;
|
|
}
|
|
|
|
/* if the parameter is null, allocate a buffer - it is the */
|
|
/* callers responsibility to release the buffer */
|
|
|
|
static hb_KeyData_T *KeyGet( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition, hb_KeyData_T *buffer )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
|
|
if ( buffer == NULL )
|
|
buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
|
|
hb_xmemcpy( buffer->szKey, pBTree->ioBuffer->szKey + ( iPosition - 1 ) * pBTree->usKeySize, pBTree->usKeySize );
|
|
buffer->szKey[ pBTree->usKeySize ] = '\0';
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
buffer->xData.pData = pBTree->ioBuffer->xData.ppData[ iPosition - 1 ];
|
|
}
|
|
else
|
|
{
|
|
buffer->xData.lData = pBTree->ioBuffer->xData.plData[ iPosition - 1 ];
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
static void KeySet( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition, hb_KeyData_T *key )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
|
|
/* if ( pBTree->ioBuffer->szKey + ( iPosition - 1 ) * pBTree->usKeySize + pBTree->usKeySize > pBTree->ioBuffer->Buffer + pBTree->usPageSize ) */
|
|
hb_xmemcpy( pBTree->ioBuffer->szKey + ( iPosition - 1 ) * pBTree->usKeySize, key->szKey, pBTree->usKeySize );
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
pBTree->ioBuffer->xData.ppData[ iPosition - 1 ] = key->xData.pData;
|
|
}
|
|
else
|
|
{
|
|
pBTree->ioBuffer->xData.plData[ iPosition - 1 ] = key->xData.lData;
|
|
}
|
|
pBTree->ioBuffer->IsDirty = pBTree->IsDirtyFlagAssignment;
|
|
}
|
|
|
|
static HB_LONG KeyCompare( struct hb_BTree * pBTree, hb_KeyData_T *left, hb_KeyData_T *right )
|
|
{
|
|
/* HB_LONG lResults = strnicmp( left->szKey, right->szKey, pBTree->usKeySize );*/
|
|
HB_LONG lResults = ( pBTree->pStrCompare )( ( const char * ) left->szKey, ( const char * ) right->szKey, pBTree->usKeySize );
|
|
|
|
if ( lResults == 0 && GETFLAG( pBTree, IsUnique ) )
|
|
{
|
|
lResults = ( left->xData.lData - right->xData.lData );
|
|
}
|
|
|
|
return lResults;
|
|
}
|
|
|
|
static HB_BOOL SearchNode( struct hb_BTree * pBTree, hb_KeyData_T *target, HB_ULONG ulNode, int *iPosition )
|
|
{
|
|
HB_BOOL results;
|
|
hb_KeyData_T *buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
|
|
if ( KeyCompare( pBTree, target, KeyGet( pBTree, ulNode, 1, buffer ) ) < 0 )
|
|
{
|
|
*iPosition = 0;
|
|
results = HB_FALSE;
|
|
}
|
|
else
|
|
{
|
|
*iPosition = CountGet( pBTree, ulNode );
|
|
while ( KeyCompare( pBTree, target, KeyGet( pBTree, ulNode, *iPosition, buffer ) ) < 0 && *iPosition > 1 )
|
|
( *iPosition )--;
|
|
|
|
results = ( HB_BOOL )( KeyCompare( pBTree, target, buffer ) == 0 );
|
|
|
|
/* TODO: change the linear search (above) into a binary search */
|
|
#if 0
|
|
if ( 1 || IsDebugging )
|
|
{
|
|
short int lower_, upper_, middle_, results_;
|
|
|
|
lower_ = 1;
|
|
upper_ = CountGet( pBTree, ulNode );
|
|
do {
|
|
middle_ = ( upper_ + lower_ ) / 2;
|
|
results_ = KeyCompare( pBTree, target, KeyGet( pBTree, ulNode, middle_, buffer ) );
|
|
if ( results_ < 0 )
|
|
upper_ = middle_-1;
|
|
else if ( results_ > 0 )
|
|
lower_ = middle_+1;
|
|
} while ( results_ != 0 && upper_ >= lower_ );
|
|
if ( !( results == (HB_BOOL)( results_ == 0 ) && *iPosition != results_ == 0 ? middle_ : upper_ ) )
|
|
HB_TRACE( HB_TR_ERROR, ( "SearchNode( results=%d results_=%d *iPosition=%d myposition=%d )",
|
|
results, (HB_BOOL)( results_ == 0 ), *iPosition, results_ == 0 ? middle_ : upper_ ) );
|
|
|
|
/* results = ( HB_BOOL )( results_ == 0 );
|
|
*iPosition = results == 0 ? middle_ : upper_; */
|
|
}
|
|
#endif
|
|
}
|
|
|
|
BufferRelease( buffer );
|
|
return results;
|
|
}
|
|
|
|
static HB_ULONG hb_BTreeSearch( struct hb_BTree * pBTree, hb_KeyData_T *target, int *targetpos, BTreeStack **pStack )
|
|
{
|
|
HB_ULONG ulNode = pBTree->ulRootPage;
|
|
|
|
while ( ulNode && !SearchNode( pBTree, target, ulNode, targetpos ) )
|
|
{
|
|
if ( pStack && *pStack )
|
|
{
|
|
StackPush( pStack, ulNode, *targetpos );
|
|
}
|
|
ulNode = BranchGet( pBTree, ulNode, *targetpos );
|
|
}
|
|
|
|
if ( ulNode && pStack && *pStack )
|
|
{
|
|
StackPush( pStack, ulNode, *targetpos );
|
|
}
|
|
|
|
return ulNode;
|
|
}
|
|
|
|
static void PushIn( struct hb_BTree * pBTree, hb_KeyData_T *xkey, HB_ULONG xbranch, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
hb_KeyData_T *buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
int i;
|
|
|
|
for ( i = CountGet( pBTree, ulNode ); i > iPosition; i-- )
|
|
{
|
|
NodeCopy( pBTree, ulNode, i + 1, ulNode, i, buffer );
|
|
}
|
|
|
|
KeySet( pBTree, ulNode, iPosition + 1, xkey );
|
|
BranchSet( pBTree, ulNode, iPosition + 1, xbranch );
|
|
( void )CountAdj( pBTree, ulNode, +1 );
|
|
BufferRelease( buffer );
|
|
}
|
|
|
|
static void Split( struct hb_BTree * pBTree, hb_KeyData_T *xkey, HB_ULONG xbranch, HB_ULONG ulNode, int iPosition, hb_KeyData_T **ykey, HB_ULONG *ybranch )
|
|
{
|
|
int i;
|
|
int median;
|
|
hb_KeyData_T *buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
|
|
if ( iPosition <= pBTree->usMinKeys )
|
|
median = pBTree->usMinKeys;
|
|
else
|
|
median = pBTree->usMinKeys + 1;
|
|
|
|
*ybranch = Grow( pBTree );
|
|
for ( i = median + 1; i <= pBTree->usMaxKeys; i++ )
|
|
{
|
|
NodeCopy( pBTree, *ybranch, i - median, ulNode, i, buffer );
|
|
}
|
|
|
|
CountSet( pBTree, *ybranch, pBTree->usMaxKeys - median );
|
|
CountSet( pBTree, ulNode, median );
|
|
|
|
if ( iPosition <= pBTree->usMinKeys )
|
|
PushIn( pBTree, xkey, xbranch, ulNode, iPosition );
|
|
else
|
|
PushIn( pBTree, xkey, xbranch, *ybranch, iPosition - median );
|
|
|
|
/* hb_xmemcpy( *ykey, KeyGet( pBTree, ulNode, CountGet( pBTree, ulNode ), buffer ), pBTree->usKeySize ); */
|
|
KeyGet( pBTree, ulNode, CountGet( pBTree, ulNode ), *ykey );
|
|
|
|
/* *ykey = KeyGet( pBTree, ulNode, CountGet( pBTree, ulNode ) );*/
|
|
BranchSet( pBTree, *ybranch, 0, BranchGet( pBTree, ulNode, CountGet( pBTree, ulNode ) ) );
|
|
( void )CountAdj( pBTree, ulNode, -1 );
|
|
BufferRelease( buffer );
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static HB_ULONG sulMaxPushDownDepth = 0;
|
|
#endif
|
|
static HB_BOOL PushDown( struct hb_BTree * pBTree, hb_KeyData_T *newkey, HB_ULONG ulNode, hb_KeyData_T **xkey, HB_ULONG *xbranch )
|
|
{
|
|
int iPosition;
|
|
|
|
#ifdef DEBUG
|
|
static HB_ULONG PushDownDepth = 0;
|
|
|
|
if( ++PushDownDepth > sulMaxPushDownDepth )
|
|
{
|
|
sulMaxPushDownDepth = PushDownDepth;
|
|
}
|
|
#endif
|
|
|
|
if ( BTREENODEISNULL( pBTree, ulNode ) )
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
hb_xmemcpy( *xkey, newkey, sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );/* *xkey = newkey;*/
|
|
*xbranch = NULLPAGE;
|
|
return HB_TRUE;
|
|
}
|
|
else
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( SearchNode( pBTree, newkey, ulNode, &iPosition ) )
|
|
{
|
|
SETFLAG( pBTree, IsDuplicateKey );
|
|
return HB_FALSE; /* error */
|
|
}
|
|
|
|
if ( PushDown( pBTree, newkey, BranchGet( pBTree, ulNode, iPosition ), xkey, xbranch ) )
|
|
{
|
|
if ( CountGet( pBTree, ulNode ) < pBTree->usMaxKeys )
|
|
{
|
|
PushIn( pBTree, *xkey, *xbranch, ulNode, iPosition );
|
|
return HB_FALSE;
|
|
}
|
|
else
|
|
{
|
|
Split( pBTree, *xkey, *xbranch, ulNode, iPosition, xkey, xbranch );
|
|
return HB_TRUE;
|
|
}
|
|
}
|
|
return HB_FALSE;
|
|
}
|
|
}
|
|
|
|
HB_BOOL hb_BTreeInsert( struct hb_BTree * pBTree, const char * szKey, PHB_ITEM pData )
|
|
{
|
|
hb_KeyData_T *xkey = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
HB_ULONG xbranch;
|
|
HB_ULONG newnode;
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
RESETFLAG( pBTree, IsDuplicateKey );
|
|
|
|
hb_xmemcpy( pBTree->pThisKeyData->szKey, szKey, pBTree->usKeySize );
|
|
pBTree->pThisKeyData->szKey[ pBTree->usKeySize ] = '\0';
|
|
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
pBTree->pThisKeyData->xData.pData = hb_itemNew( pData );
|
|
/*printf( "[%p %p] ", pBTree->pThisKeyData->xData.pData, pData );PrintCRLF();*/
|
|
}
|
|
else
|
|
{
|
|
pBTree->pThisKeyData->xData.lData = hb_itemGetNL( pData );
|
|
}
|
|
|
|
if ( PushDown( pBTree, pBTree->pThisKeyData, pBTree->ulRootPage, &xkey, &xbranch ) )
|
|
{
|
|
newnode = Grow( pBTree );
|
|
CountSet( pBTree, newnode, 1 );
|
|
KeySet( pBTree, newnode, 1, xkey );
|
|
BranchSet( pBTree, newnode, 0, pBTree->ulRootPage );
|
|
BranchSet( pBTree, newnode, 1, xbranch );
|
|
pBTree->ulRootPage = newnode;
|
|
}
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
BufferRelease( xkey );
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
if ( !GETFLAG( pBTree, IsDuplicateKey ) )
|
|
pBTree->ulKeyCount++;
|
|
|
|
return !GETFLAG( pBTree, IsDuplicateKey );
|
|
}
|
|
|
|
static void MoveRight( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
int c;
|
|
HB_ULONG tmpnode;
|
|
hb_KeyData_T *buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
|
|
tmpnode = BranchGet( pBTree, ulNode, iPosition );
|
|
for ( c = CountGet( pBTree, tmpnode ); c > 0; c-- )
|
|
{
|
|
NodeCopy( pBTree, tmpnode, c + 1, tmpnode, c, buffer );
|
|
}
|
|
|
|
BranchSet( pBTree, tmpnode, 1, BranchGet( pBTree, tmpnode, 0 ) );
|
|
( void )CountAdj( pBTree, tmpnode, +1 );
|
|
KeySet( pBTree, tmpnode, 1, KeyGet( pBTree, ulNode, iPosition, buffer ) );
|
|
|
|
tmpnode = BranchGet( pBTree, ulNode, iPosition - 1 );
|
|
KeySet( pBTree, ulNode, iPosition, KeyGet( pBTree, tmpnode, CountGet( pBTree, tmpnode ), buffer ) );
|
|
BranchSet( pBTree, BranchGet( pBTree, ulNode, iPosition ), 0, BranchGet( pBTree, tmpnode, CountGet( pBTree, tmpnode ) ) );
|
|
( void )CountAdj( pBTree, tmpnode, -1 );
|
|
BufferRelease( buffer );
|
|
}
|
|
|
|
static void MoveLeft( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
int c, c_count;
|
|
HB_ULONG tmpnode;
|
|
hb_KeyData_T *buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
|
|
tmpnode = BranchGet( pBTree, ulNode, iPosition - 1 );
|
|
c_count = CountAdj( pBTree, tmpnode, +1 );
|
|
KeySet( pBTree, tmpnode, c_count, KeyGet( pBTree, ulNode, iPosition, buffer ) );
|
|
BranchSet( pBTree, tmpnode, c_count, BranchGet( pBTree, BranchGet( pBTree, ulNode, iPosition ), 0 ) );
|
|
|
|
tmpnode = BranchGet( pBTree, ulNode, iPosition );
|
|
KeySet( pBTree, ulNode, iPosition, KeyGet( pBTree, tmpnode, 1, buffer ) );
|
|
BranchSet( pBTree, tmpnode, 0, BranchGet( pBTree, tmpnode, 1 ) );
|
|
c_count = CountAdj( pBTree, tmpnode, -1 );
|
|
|
|
for ( c = 1; c <= c_count; c++ )
|
|
{
|
|
NodeCopy( pBTree, tmpnode, c, tmpnode, c + 1, buffer );
|
|
}
|
|
|
|
BufferRelease( buffer );
|
|
}
|
|
|
|
static void Combine( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
int c, c_count;
|
|
HB_ULONG tmpnode;
|
|
HB_ULONG leftnode;
|
|
HB_ULONG leftpagecount;
|
|
hb_KeyData_T *buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
|
|
tmpnode = BranchGet( pBTree, ulNode, iPosition );
|
|
leftnode = BranchGet( pBTree, ulNode, iPosition - 1 );
|
|
leftpagecount = CountAdj( pBTree, leftnode, +1 );
|
|
KeySet( pBTree, leftnode, leftpagecount, KeyGet( pBTree, ulNode, iPosition, buffer ) );
|
|
BranchSet( pBTree, leftnode, leftpagecount, BranchGet( pBTree, tmpnode, 0 ) );
|
|
|
|
c_count = CountGet( pBTree, tmpnode );
|
|
for ( c = 1; c <= c_count; c++ )
|
|
{
|
|
leftpagecount = CountAdj( pBTree, leftnode, +1 );
|
|
NodeCopy( pBTree, leftnode, leftpagecount, tmpnode, c, buffer );
|
|
}
|
|
|
|
c_count = CountGet( pBTree, ulNode );
|
|
for ( c = iPosition; c < c_count; c++ )
|
|
{
|
|
NodeCopy( pBTree, ulNode, c, ulNode, c + 1, buffer );
|
|
}
|
|
( void )CountAdj( pBTree, ulNode, -1 );
|
|
|
|
Prune( pBTree, tmpnode );
|
|
BufferRelease( buffer );
|
|
}
|
|
|
|
static void Restore( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
READPAGE_IF_NEEDED( pBTree, ulNode );
|
|
|
|
if ( iPosition == 0 ) /* left-most key */
|
|
{
|
|
if ( CountGet( pBTree, BranchGet( pBTree, ulNode, 1 ) ) > pBTree->usMinKeys )
|
|
{
|
|
MoveLeft( pBTree, ulNode, 1 );
|
|
}
|
|
else
|
|
{
|
|
Combine( pBTree, ulNode, 1 );
|
|
}
|
|
}
|
|
else if ( iPosition == CountGet( pBTree, ulNode ) ) /* right-most key */
|
|
{
|
|
if ( CountGet( pBTree, BranchGet( pBTree, ulNode, iPosition - 1 ) ) > pBTree->usMinKeys )
|
|
{
|
|
MoveRight( pBTree, ulNode, iPosition );
|
|
}
|
|
else
|
|
{
|
|
Combine( pBTree, ulNode, iPosition );
|
|
}
|
|
}
|
|
else if ( CountGet( pBTree, BranchGet( pBTree, ulNode, iPosition - 1 ) ) > pBTree->usMinKeys )
|
|
{
|
|
MoveRight( pBTree, ulNode, iPosition );
|
|
}
|
|
else if ( CountGet( pBTree, BranchGet( pBTree, ulNode, iPosition + 1 ) ) > pBTree->usMinKeys )
|
|
{
|
|
MoveLeft( pBTree, ulNode, iPosition + 1 );
|
|
}
|
|
else
|
|
Combine( pBTree, ulNode, iPosition );
|
|
}
|
|
|
|
static void Remove( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
int c, c_count;
|
|
hb_KeyData_T *buffer = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
|
|
c_count = CountGet( pBTree, ulNode );
|
|
for ( c = iPosition + 1; c <= c_count; c++ )
|
|
{
|
|
NodeCopy( pBTree, ulNode, c - 1, ulNode, c, buffer );
|
|
}
|
|
( void )CountAdj( pBTree, ulNode, -1 );
|
|
BufferRelease( buffer );
|
|
}
|
|
|
|
static void Successor( struct hb_BTree * pBTree, HB_ULONG ulNode, int iPosition )
|
|
{
|
|
HB_ULONG tmpnode;
|
|
hb_KeyData_T *buffer;
|
|
|
|
for ( tmpnode = BranchGet( pBTree, ulNode, iPosition );
|
|
!BTREENODEISNULL( pBTree, BranchGet( pBTree, tmpnode, 0 ) );
|
|
tmpnode = BranchGet( pBTree, tmpnode, 0 ) ) {}
|
|
|
|
KeySet( pBTree, ulNode, iPosition, ( buffer = KeyGet( pBTree, tmpnode, 1, NULL ) ) );
|
|
BufferRelease( buffer );
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static HB_ULONG sulMaxRecDeleteDepth = 0;
|
|
#endif
|
|
static HB_BOOL RecDelete( struct hb_BTree * pBTree, hb_KeyData_T *target, HB_ULONG ulNode )
|
|
{
|
|
#ifdef DEBUG
|
|
static HB_ULONG RecDeleteDepth = 0;
|
|
#endif
|
|
|
|
int iPosition;
|
|
HB_BOOL found;
|
|
|
|
if ( BTREENODEISNULL( pBTree, ulNode ) )
|
|
{
|
|
/* todo: hitting an empty pBTree is an error */
|
|
return HB_FALSE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
if( ++RecDeleteDepth > sulMaxRecDeleteDepth )
|
|
{
|
|
sulMaxRecDeleteDepth = RecDeleteDepth;
|
|
if ( RecDeleteDepth > 10 ) { HB_TRACE( HB_TR_ERROR, ( "RecDelete( Exiting 2! )" ) ); exit(0); }
|
|
}
|
|
#endif
|
|
/* inline assignment & comparision was generating a Borland warning */
|
|
found = SearchNode( pBTree, target, ulNode, &iPosition );
|
|
if ( found )
|
|
{
|
|
if ( !BTREENODEISNULL( pBTree, BranchGet( pBTree, ulNode, iPosition - 1 ) ) )
|
|
{
|
|
Successor( pBTree, ulNode, iPosition ); /* move successor to this iPosition */
|
|
KeyGet( pBTree, ulNode, iPosition, target );
|
|
found = RecDelete( pBTree, target, BranchGet( pBTree, ulNode, iPosition ) );
|
|
#if 0
|
|
if ( !found )
|
|
error( "key not found" ); /* key exists, so this shouldn't occur*/
|
|
#endif
|
|
}
|
|
else
|
|
Remove( pBTree, ulNode, iPosition );
|
|
}
|
|
else
|
|
found = RecDelete( pBTree, target, BranchGet( pBTree, ulNode, iPosition ) );
|
|
|
|
/* the recursive call has returned */
|
|
if ( found && !BTREENODEISNULL( pBTree, BranchGet( pBTree, ulNode, iPosition ) ) )
|
|
if ( CountGet( pBTree, BranchGet( pBTree, ulNode, iPosition ) ) < pBTree->usMinKeys )
|
|
Restore( pBTree, ulNode, iPosition );
|
|
#ifdef DEBUG
|
|
--RecDeleteDepth;
|
|
#endif
|
|
return found;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static HB_BOOL __RecDelete( struct hb_BTree * pBTree, hb_KeyData_T target, HB_ULONG ulNode )
|
|
{
|
|
static HB_ULONG RecDeleteDepth = 0;
|
|
HB_BOOL found = HB_FALSE;
|
|
|
|
if( ++RecDeleteDepth > sulMaxRecDeleteDepth )
|
|
{
|
|
sulMaxRecDeleteDepth = RecDeleteDepth;
|
|
}
|
|
|
|
if ( !BTREENODEISNULL( pBTree, ulNode ) )
|
|
{
|
|
int iPosition;
|
|
/* hb_KeyData_T tmpTarget; */
|
|
/* */
|
|
/* tmpTarget = BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 ); */
|
|
/* hb_xmemcpy( tmpTarget, target, pBTree->usKeySize ); */
|
|
|
|
#define tmpTarget target
|
|
|
|
/* inline assignment & comparision was generating a Borland warning */
|
|
found = SearchNode( pBTree, tmpTarget, ulNode, &iPosition )
|
|
if ( found )
|
|
{
|
|
if ( !BTREENODEISNULL( pBTree, BranchGet( pBTree, ulNode, iPosition - 1 ) ) )
|
|
{
|
|
Successor( pBTree, ulNode, iPosition );
|
|
hb_xmemcpy( tmpTarget, KeyGet( pBTree, ulNode, iPosition ), pBTree->usKeySize );
|
|
/*if ( !( found =*/ RecDelete( pBTree, tmpTarget, BranchGet( pBTree, ulNode, iPosition ) );/*) )*/
|
|
/* RESETFLAG( pBTree, IsRecordFound )*/ /*pBTree->bRecordNotFound = HB_TRUE*/; /* error */
|
|
}
|
|
else
|
|
{
|
|
Remove( pBTree, ulNode, iPosition );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !( found = RecDelete( pBTree, tmpTarget, BranchGet( pBTree, ulNode, iPosition ) ) ) )
|
|
RESETFLAG( pBTree, IsRecordFound ); /* error */
|
|
}
|
|
|
|
/* if ( GETFLAG( pBTree, IsRecordFound )*/ /*!tree->bRecordNotFound )*/
|
|
{
|
|
HB_ULONG tmpNode;
|
|
|
|
tmpNode = BranchGet( pBTree, ulNode, iPosition );
|
|
if ( !BTREENODEISNULL( pBTree, tmpNode ) )
|
|
if ( CountGet( pBTree, tmpNode ) < pBTree->usMinKeys )
|
|
Restore( pBTree, ulNode, iPosition );
|
|
}
|
|
|
|
/*BufferRelease( tmpTarget );*/
|
|
#undef tmpTarget
|
|
}
|
|
else
|
|
RESETFLAG( pBTree, IsRecordFound ); /* error */
|
|
|
|
--RecDeleteDepth;
|
|
|
|
return found;
|
|
}
|
|
#endif
|
|
|
|
HB_BOOL hb_BTreeDelete( struct hb_BTree * pBTree, const char *target, HB_LONG lData )
|
|
{
|
|
HB_ULONG ulNode;
|
|
int iPosition;
|
|
hb_KeyData_T *tmpTarget;
|
|
HB_BOOL found = HB_FALSE;
|
|
|
|
SETFLAG( pBTree, IsRecordFound );
|
|
CLEARKEYDATA( pBTree );
|
|
|
|
tmpTarget = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
hb_xmemcpy( tmpTarget->szKey, target, pBTree->usKeySize );
|
|
tmpTarget->xData.lData = lData;
|
|
|
|
if ( hb_BTreeSearch( pBTree, tmpTarget, &iPosition, NULL ) )
|
|
{
|
|
if ( RecDelete( pBTree, tmpTarget, pBTree->ulRootPage ) )
|
|
{
|
|
found = HB_TRUE;
|
|
if ( CountGet( pBTree, pBTree->ulRootPage ) == 0 )
|
|
{
|
|
ulNode = pBTree->ulRootPage;
|
|
pBTree->ulRootPage = BranchGet( pBTree, pBTree->ulRootPage, 0 );
|
|
Prune( pBTree, ulNode );
|
|
}
|
|
}
|
|
#if 0
|
|
else
|
|
{} /* error - key does not exist */
|
|
#endif
|
|
}
|
|
else
|
|
RESETFLAG( pBTree, IsRecordFound );
|
|
|
|
BufferRelease( tmpTarget );
|
|
|
|
if ( found )
|
|
pBTree->ulKeyCount--;
|
|
|
|
return found;/*!tree->bRecordNotFound;*/
|
|
}
|
|
|
|
void hb_BTreeGoTop( struct hb_BTree * pBTree )
|
|
{
|
|
HB_ULONG ulNode;
|
|
HB_ULONG ulLastNode;
|
|
|
|
for ( ulLastNode = ulNode = pBTree->ulRootPage; !BTREENODEISNULL( pBTree, ulNode ); ulLastNode = ulNode, ulNode = BranchGet( pBTree, ulNode, 0 ) ) {}
|
|
|
|
if ( BTREENODEISNULL( pBTree, ulLastNode ) )
|
|
CLEARKEYDATA( pBTree );
|
|
else
|
|
KeyGet( pBTree, ulLastNode, 1, pBTree->pThisKeyData );
|
|
}
|
|
|
|
void hb_BTreeGoBottom( struct hb_BTree * pBTree )
|
|
{
|
|
HB_ULONG ulNode;
|
|
HB_ULONG ulLastNode;
|
|
|
|
for ( ulLastNode = ulNode = pBTree->ulRootPage; !BTREENODEISNULL( pBTree, ulNode ); ulLastNode = ulNode, ulNode = BranchGet( pBTree, ulNode, CountGet( pBTree, ulNode ) ) ) {}
|
|
|
|
if ( BTREENODEISNULL( pBTree, ulLastNode ) )
|
|
CLEARKEYDATA( pBTree );
|
|
else
|
|
KeyGet( pBTree, ulLastNode, CountGet( pBTree, ulLastNode ), pBTree->pThisKeyData );
|
|
}
|
|
|
|
HB_LONG hb_BTreeSkip( struct hb_BTree * pBTree, HB_LONG records )
|
|
{
|
|
HB_LONG results = 0;
|
|
int iPosition;
|
|
hb_KeyData_T *keydata;
|
|
BTreeStack *pStack = NULL;
|
|
|
|
if ( BTREENODEISNULL( pBTree, pBTree->ulRootPage ) )
|
|
{
|
|
CLEARKEYDATA( pBTree );
|
|
return 0;
|
|
}
|
|
|
|
keydata = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
hb_xmemcpy( keydata->szKey, pBTree->pThisKeyData->szKey, pBTree->usKeySize );
|
|
keydata->xData.lData = pBTree->pThisKeyData->xData.lData;
|
|
|
|
StackNew( &pStack );
|
|
if ( !BTREENODEISNULL( pBTree, hb_BTreeSearch( pBTree, keydata, &iPosition, &pStack ) ) )
|
|
{
|
|
results = StackSkip( pBTree, &pStack, records );
|
|
}
|
|
else
|
|
{
|
|
CLEARKEYDATA( pBTree );
|
|
}
|
|
|
|
StackRelease( &pStack );
|
|
BufferRelease( keydata );
|
|
|
|
return results;
|
|
}
|
|
|
|
HB_BOOL hb_BTreeSeek( struct hb_BTree * pBTree, const char *szKey, HB_LONG lData, HB_BOOL bSoftSeek )
|
|
{
|
|
HB_BOOL results = HB_FALSE;
|
|
int iPosition;
|
|
hb_KeyData_T *tmpTarget;
|
|
BTreeStack *pStack = NULL;
|
|
|
|
tmpTarget = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
hb_xmemcpy( tmpTarget->szKey, szKey, pBTree->usKeySize );
|
|
tmpTarget->xData.lData = lData;
|
|
|
|
StackNew( &pStack );
|
|
if ( !BTREENODEISNULL( pBTree, hb_BTreeSearch( pBTree, tmpTarget, &iPosition, &pStack ) ) ||
|
|
( bSoftSeek && pStack->usCount > 0 && ( 1 == StackSkip( pBTree, &pStack, 1 ) ) ) )
|
|
{
|
|
KeyGet( pBTree, STACKNODE( &pStack ), STACKPOSITION( &pStack ), pBTree->pThisKeyData );
|
|
results = HB_TRUE;
|
|
}
|
|
else
|
|
{
|
|
CLEARKEYDATA( pBTree );
|
|
}
|
|
|
|
StackRelease( &pStack );
|
|
BufferRelease( tmpTarget );
|
|
|
|
return results;
|
|
}
|
|
|
|
|
|
static int hb_BTstrncmp( const char *s1, const char *s2, size_t n )
|
|
{
|
|
return strncmp( s1, s2, n );
|
|
}
|
|
|
|
/* allocate hb_BTree structure */
|
|
struct hb_BTree * hb_BTreeNew( const char * FileName, HB_USHORT usPageSize, HB_USHORT usKeySize, HB_ULONG ulFlags, HB_ULONG ulBuffers )
|
|
{
|
|
struct hb_BTree *pBTree = ( struct hb_BTree * ) BufferAlloc( sizeof( struct hb_BTree ) );
|
|
int iFileIOmode;
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
/* correct parameters to default values */
|
|
if ( usKeySize < 8 ) usKeySize = 8;
|
|
if ( usPageSize % HB_BTREE_HEADERSIZE ) usPageSize -= ( usPageSize % HB_BTREE_HEADERSIZE );
|
|
if ( usPageSize < HB_BTREE_HEADERSIZE ) usPageSize = HB_BTREE_HEADERSIZE;
|
|
|
|
/*
|
|
number of keys is:
|
|
( usPageSize - ( sizeof count + sizeof branch[0] ) ) / ( sizeof branch[0] + sizeof key ) - 1
|
|
*/
|
|
|
|
/* this assumes that the count field and a branch field are HB_U32 */
|
|
#define MAXKEYS( pagesize, keysize, flags ) \
|
|
( ( ( pagesize ) - ( sizeof( HB_U32 ) + sizeof( HB_U32 ) ) ) / \
|
|
( sizeof( HB_U32 ) \
|
|
+ ( ( ( flags ) & HB_BTREE_INMEMORY ) == HB_BTREE_INMEMORY \
|
|
? sizeof( PHB_ITEM * ) \
|
|
: ( keysize ) ) ) - 1 \
|
|
)
|
|
#define MINKEYS( maxkeys ) ( ( maxkeys + 1 ) / 2 - ( maxkeys % 2 ) )
|
|
|
|
if ( ( ulFlags & HB_BTREE_INMEMORY ) == HB_BTREE_INMEMORY )
|
|
{
|
|
pBTree->hFile = 0;
|
|
pBTree->szFileName = NULL;
|
|
pBTree->IsDirtyFlagAssignment = HB_FALSE; /* replaces const value for assignment */
|
|
}
|
|
else
|
|
{
|
|
pBTree->IsDirtyFlagAssignment = HB_TRUE; /* replaces const value for assignment */
|
|
|
|
if ( ( ulFlags & ( HB_BTREE_READONLY ) ) == HB_BTREE_READONLY )
|
|
{
|
|
iFileIOmode = FC_READONLY;
|
|
ulFlags &= ~HB_BTREE_READONLY;
|
|
ulFlags &= ~HB_BTREE_SHARED;
|
|
}
|
|
else
|
|
{
|
|
iFileIOmode = FC_NORMAL;
|
|
}
|
|
|
|
pBTree->hFile = hb_fsCreate( FileName, iFileIOmode );
|
|
if ( pBTree->hFile == -1 )
|
|
{
|
|
BufferRelease( pBTree );
|
|
return NULL;
|
|
}
|
|
|
|
if ( ( ulFlags & ( HB_BTREE_SHARED ) ) == HB_BTREE_SHARED )
|
|
{
|
|
hb_fsClose( pBTree->hFile );
|
|
pBTree->hFile = hb_fsOpen( FileName, FO_READWRITE | FO_SHARED );
|
|
if ( pBTree->hFile == -1 )
|
|
{
|
|
BufferRelease( pBTree );
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
pBTree->szFileName = hb_strdup( FileName );
|
|
}
|
|
|
|
pBTree->ulFreePage = NULLPAGE;
|
|
pBTree->ulRootPage = NULLPAGE;
|
|
pBTree->usPageSize = usPageSize;
|
|
pBTree->usKeySize = usKeySize;
|
|
pBTree->usMaxKeys = ( HB_USHORT ) MAXKEYS( usPageSize, usKeySize, ulFlags );
|
|
pBTree->usMinKeys = ( HB_USHORT ) MINKEYS( pBTree->usMaxKeys );
|
|
pBTree->ulFlags = ulFlags;
|
|
pBTree->ulKeyCount = 0;
|
|
pBTree->pStack = NULL;
|
|
/* TODO: use stack optimization if flags warrant: if ( flag... ) StackNew( &pBTree->pStack ); */
|
|
pBTree->pThisKeyData = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
CLEARKEYDATA( pBTree );
|
|
ioBufferAlloc( pBTree, ulBuffers );
|
|
|
|
if ( GETFLAG( pBTree, IsCaseLess ) )
|
|
{
|
|
pBTree->pStrCompare = ( BTreeCmpFunc ) hb_strnicmp;
|
|
}
|
|
else
|
|
{
|
|
pBTree->pStrCompare = ( BTreeCmpFunc ) hb_BTstrncmp;
|
|
}
|
|
|
|
if ( GETFLAG( pBTree, IsInMemory ) == HB_FALSE )
|
|
{
|
|
HeaderWrite( pBTree );
|
|
}
|
|
else /* IsInMemory == HB_TRUE */
|
|
{
|
|
RESETFLAG( pBTree, HB_BTREE_UNIQUE ); /* clear this flag */
|
|
}
|
|
|
|
return pBTree;
|
|
}
|
|
|
|
/* open an existing structure */
|
|
struct hb_BTree *hb_BTreeOpen( const char *FileName, HB_ULONG ulFlags, HB_ULONG ulBuffers )
|
|
{
|
|
struct hb_BTree *pBTree = ( struct hb_BTree * ) BufferAlloc( sizeof( struct hb_BTree ) );
|
|
HB_BYTE TmpHeader[ HB_BTREE_HEADERSIZE ];
|
|
HB_BYTE * pHeader = &TmpHeader[ 0 ];
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
pBTree->szFileName = hb_strdup( FileName );
|
|
pBTree->hFile = hb_fsOpen( pBTree->szFileName, ( ( ulFlags & HB_BTREE_READONLY ) ? FO_READ : FO_READWRITE ) );
|
|
if ( pBTree->hFile == -1 )
|
|
{
|
|
BufferRelease( pBTree );
|
|
return NULL;
|
|
}
|
|
|
|
hb_fsRead( pBTree->hFile, TmpHeader, sizeof( TmpHeader ) );
|
|
if ( memcmp( TmpHeader, HEADER_ID, sizeof( HEADER_ID ) ) != 0 )
|
|
{
|
|
hb_fsClose( pBTree->hFile );
|
|
BufferRelease( pBTree );
|
|
return NULL;
|
|
}
|
|
|
|
pHeader += sizeof( HEADER_ID ) - 1; /* the trailing null byte */
|
|
pHeader += sizeof( HB_U32 ); /* skip over the header size (HB_BTREE_HEADERSIZE) field */
|
|
pBTree->usPageSize = ( HB_U16 ) HB_GET_LE_UINT32( pHeader ); pHeader += 4;
|
|
pBTree->usKeySize = ( HB_U16 ) HB_GET_LE_UINT32( pHeader ); pHeader += 4;
|
|
pBTree->usMaxKeys = ( HB_U16 ) HB_GET_LE_UINT32( pHeader ); pHeader += 4;
|
|
pBTree->usMinKeys = ( HB_U16 ) HB_GET_LE_UINT32( pHeader ); pHeader += 4;
|
|
pBTree->ulFlags = HB_GET_LE_UINT32( pHeader ) | ( ulFlags & HB_BTREE_READONLY );
|
|
|
|
pHeader = &TmpHeader[ 64 ];
|
|
pBTree->ulRootPage = HB_GET_LE_UINT32( pHeader ); pHeader += 4;
|
|
pBTree->ulFreePage = HB_GET_LE_UINT32( pHeader ); pHeader += 4;
|
|
pBTree->ulKeyCount = HB_GET_LE_UINT32( pHeader );
|
|
|
|
pBTree->pThisKeyData = ( hb_KeyData_T * ) BufferAlloc( sizeof( hb_KeyData_T ) + pBTree->usKeySize + 1 );
|
|
CLEARKEYDATA( pBTree );
|
|
pBTree->pStack = NULL;
|
|
/* TODO: use stack optimization if flags warrant: if ( flag... ) StackNew( &pBTree->pStack ); */
|
|
ioBufferAlloc( pBTree, ulBuffers );
|
|
|
|
RESETFLAG( pBTree, HB_BTREE_INMEMORY ); /* clear this flag */
|
|
pBTree->IsDirtyFlagAssignment = HB_TRUE; /* replaces const value for assignment */
|
|
|
|
if ( GETFLAG( pBTree, IsCaseLess ) )
|
|
{
|
|
pBTree->pStrCompare = ( BTreeCmpFunc ) hb_strnicmp;
|
|
}
|
|
else
|
|
{
|
|
pBTree->pStrCompare = ( BTreeCmpFunc ) hb_BTstrncmp;
|
|
}
|
|
|
|
return pBTree;
|
|
}
|
|
|
|
void hb_BTreeClose( struct hb_BTree * pBTree )
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
ioBufferRelease( pBTree );
|
|
|
|
if ( GETFLAG( pBTree, IsInMemory ) == HB_FALSE &&
|
|
GETFLAG( pBTree, IsReadOnly ) == HB_FALSE )
|
|
{
|
|
HeaderWrite( pBTree );
|
|
}
|
|
|
|
if ( pBTree->hFile != 0 )
|
|
{
|
|
hb_fsClose( pBTree->hFile );
|
|
}
|
|
|
|
if ( pBTree->szFileName != NULL )
|
|
{
|
|
BufferRelease( pBTree->szFileName );
|
|
}
|
|
|
|
StackRelease( &pBTree->pStack );
|
|
BufferRelease( pBTree->pThisKeyData );
|
|
BufferRelease( pBTree );
|
|
}
|
|
|
|
const char * hb_BTreeKey( struct hb_BTree * pBTree )
|
|
{
|
|
return ( const char * )pBTree->pThisKeyData->szKey;
|
|
}
|
|
|
|
HB_LONG hb_BTreeData( struct hb_BTree * pBTree )
|
|
{
|
|
return pBTree->pThisKeyData->xData.lData;
|
|
}
|
|
|
|
PHB_ITEM hb_BTreeDataItem( struct hb_BTree * pBTree )
|
|
{
|
|
return ( PHB_ITEM ) pBTree->pThisKeyData->xData.pData;
|
|
}
|
|
|
|
static int BTree_SetTreeIndex( struct hb_BTree * pBTree )
|
|
{
|
|
int n;
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
if ( pBTree == NULL )
|
|
return -1;
|
|
|
|
for ( n = 0; n < s_BTree_List_Count && s_BTree_List[ n ] != NULL; n++ ) {}
|
|
|
|
if ( n == s_BTree_List_Count )
|
|
{
|
|
s_BTree_List = (struct hb_BTree **) BufferRealloc( s_BTree_List, ++s_BTree_List_Count * sizeof( s_BTree_List[ 0 ] ) );
|
|
}
|
|
|
|
s_BTree_List[ n ] = pBTree;
|
|
|
|
return n + 1;
|
|
}
|
|
|
|
static struct hb_BTree *BTree_GetTreeIndex( const char * GetSource )
|
|
{
|
|
int index;
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
index = hb_parni( 1 );
|
|
if ( index < 1 || index > s_BTree_List_Count || s_BTree_List[ index - 1 ] == NULL ) \
|
|
{
|
|
raiseError( EG_ARG, HB_BTREE_EC_TREEHANDLE, "Bad HB_BTree handle", GetSource, 1 );
|
|
return NULL;
|
|
}
|
|
else
|
|
return s_BTree_List[ index - 1 ];
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEOPEN ) /* hb_BTreeOpen( CHAR cFileName, HB_ULONG ulFlags [ , int nBuffers=1 ] ) -> hb_Btree_Handle */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( HB_ISCHAR( 1 ) && hb_parclen( 1 ) > 0 )
|
|
{
|
|
hb_retni( BTree_SetTreeIndex( hb_BTreeOpen( hb_parc( 1 ), hb_parnl( 2 ), hb_parnl( 3 ) ) ) );
|
|
}
|
|
else
|
|
{
|
|
raiseError( EG_ARG, HB_BTREE_EC_INVALIDARG, "Bad argument(s)", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retni( 0 );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BTREENEW ) /* hb_BTreeNew( CHAR cFileName, int nPageSize, int nKeySize, [ HB_ULONG ulFlags ], [ int nBuffers=1 ] ) -> hb_Btree_Handle */
|
|
{
|
|
int iPageSize = hb_parni( 2 );
|
|
int iKeySize = hb_parni( 3 );
|
|
HB_ULONG ulFlags = hb_parnl( 4 );
|
|
int iMaxKeys = MAXKEYS( ( HB_U32 ) iPageSize, ( HB_U32 ) iKeySize, ulFlags );
|
|
int iMinKeys = MINKEYS( iMaxKeys );
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( ( ( ulFlags & HB_BTREE_INMEMORY ) == HB_BTREE_INMEMORY || ( HB_ISCHAR( 1 ) && hb_parclen( 1 ) > 0 ) ) &&
|
|
( HB_ISNUM( 2 ) && iPageSize >= 1024 && iPageSize < ( int )USHRT_MAX ) &&
|
|
( ( ulFlags & HB_BTREE_INMEMORY ) == HB_BTREE_INMEMORY || ( HB_ISNUM( 3 ) && iKeySize >= 4 && iMinKeys > 0 && iMaxKeys > 2 ) ) &&
|
|
( 1 == 1 ) )
|
|
{
|
|
hb_retni( BTree_SetTreeIndex( hb_BTreeNew( hb_parc( 1 ), ( HB_USHORT ) iPageSize, ( HB_USHORT ) iKeySize, ulFlags, hb_parnl( 5 ) ) ) );
|
|
}
|
|
else
|
|
{
|
|
raiseError( EG_ARG, HB_BTREE_EC_INVALIDARG, "Bad argument(s)", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retni( 0 );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BTREECLOSE ) /* hb_BTreeClose( hb_BTree_Handle ) -> NIL */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
hb_BTreeClose( BTree_GetTreeIndex( "hb_btreeclose" ) );
|
|
s_BTree_List[ hb_parni( 1 ) - 1 ] = NULL;
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEINSERT ) /* hb_BTreeInsert( hb_BTree_Handle, CHAR cKey, HB_LONG lData | ANY xData ) -> lSuccess */
|
|
{
|
|
struct hb_BTree * pBTree = BTree_GetTreeIndex( "hb_btreeinsert" );
|
|
/* PHB_ITEM pKeyCode = hb_param( 1, HB_IT_NUMERIC ); */
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( HB_ISNUM( 1 ) && HB_ISCHAR( 2 ) && ( hb_pcount() == 2 || GETFLAG( pBTree, IsInMemory ) || HB_ISNUM( 3 ) ) )
|
|
{
|
|
if ( GETFLAG( pBTree, IsReadOnly ) == HB_FALSE )
|
|
{
|
|
hb_retl( hb_BTreeInsert( pBTree, hb_parc( 2 ), hb_paramError( 3 ) ) );
|
|
}
|
|
else
|
|
{
|
|
raiseError( EG_WRITE, HB_BTREE_EC_WRITEERROR, "Cannot insert into a read-only file", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retl( HB_FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
raiseError( EG_ARG, HB_BTREE_EC_INVALIDARG, "Bad argument(s)", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retl( HB_FALSE );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEDELETE ) /* hb_BTreeDelete( hb_BTree_Handle, CHAR cKey, HB_LONG lData ) -> lSuccess */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( HB_ISNUM( 1 ) && HB_ISCHAR( 2 ) && ( hb_pcount() == 2 || HB_ISNUM( 3 ) ) )
|
|
{
|
|
struct hb_BTree * pBTree = BTree_GetTreeIndex( "hb_btreedelete" );
|
|
if ( GETFLAG( pBTree, IsReadOnly ) == HB_FALSE )
|
|
{
|
|
hb_retl( hb_BTreeDelete( pBTree, hb_parc( 2 ), hb_parnl( 3 ) ) );
|
|
}
|
|
else
|
|
{
|
|
raiseError( EG_WRITE, HB_BTREE_EC_WRITEERROR, "Cannot delete from a read-only file", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retl( HB_FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
raiseError( EG_ARG, HB_BTREE_EC_INVALIDARG, "Bad argument(s)", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retl( HB_FALSE );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEKEY ) /* hb_BTreeKey( hb_BTree_Handle ) -> CHAR cKey */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
hb_retc( ( char * ) BTree_GetTreeIndex( "hb_btreekey" )->pThisKeyData->szKey );
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEDATA ) /* hb_BtreeData( hb_BTree_Handle ) -> HB_LONG lOldData | xOldData */
|
|
{ /*, [ HB_LONG lNewData | ANY xNewData ] ??? */
|
|
struct hb_BTree * pBTree = BTree_GetTreeIndex( "hb_btreeinfo" );
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( GETFLAG( pBTree, IsInMemory ) )
|
|
{
|
|
if ( pBTree->pThisKeyData->xData.pData )
|
|
{
|
|
hb_itemReturn( pBTree->pThisKeyData->xData.pData );
|
|
}
|
|
else
|
|
{
|
|
hb_ret();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hb_retnl( pBTree->pThisKeyData->xData.lData );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEGOTOP ) /* hb_BTreeGoTop( hb_BTree_Handle ) --> NIL */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
hb_BTreeGoTop( BTree_GetTreeIndex( "hb_btreegotop" ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEGOBOTTOM ) /* hb_BTreeGoBottom( hb_BTree_Handle ) --> NIL */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
hb_BTreeGoBottom( BTree_GetTreeIndex( "hb_btreegobottom" ) );
|
|
}
|
|
|
|
HB_FUNC( HB_BTREESKIP ) /* hb_BTreeSkip( hb_BTree_Handle, HB_LONG nRecords ) -> HB_LONG nRecordsSkipped */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( HB_ISNUM( 1 ) && ( hb_pcount() == 1 || HB_ISNUM( 2 ) ) )
|
|
{
|
|
HB_LONG nSkip = hb_pcount() == 1 ? 1 : hb_parnl( 2 );
|
|
hb_retnl( hb_BTreeSkip( BTree_GetTreeIndex( "hb_btreeskip" ), nSkip ) );
|
|
}
|
|
else
|
|
{
|
|
raiseError( EG_ARG, HB_BTREE_EC_INVALIDARG, "Bad argument(s)", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retnl( 0 );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BTREESEEK ) /* hb_BTreeSeek( hb_BTree_Handle, CHAR cKey, HB_LONG lData, BOOL lSoftSeek ) -> lSuccess */
|
|
{
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
if ( HB_ISNUM( 1 ) && HB_ISCHAR( 2 ) )
|
|
hb_retl( hb_BTreeSeek( BTree_GetTreeIndex( "hb_btreeseek" ), hb_parc( 2 ), hb_parnl( 3 ), hb_parl( 4 ) ) );
|
|
else
|
|
{
|
|
raiseError( EG_ARG, HB_BTREE_EC_INVALIDARG, "Bad argument(s)", HB_ERR_FUNCNAME, hb_pcount() );
|
|
hb_retl( HB_FALSE );
|
|
}
|
|
}
|
|
|
|
HB_FUNC( HB_BTREEINFO ) /* hb_BTreeInfo( hb_BTree_Handle, [index] ) -> aResults | cResults | nResults */
|
|
{
|
|
struct hb_BTree * pBTree = BTree_GetTreeIndex( "hb_btreeinfo" );
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
if ( pBTree )
|
|
switch ( hb_parni( 2 ) )
|
|
{
|
|
case HB_BTREEINFO_FILENAME: hb_retc( pBTree->szFileName ); break;
|
|
case HB_BTREEINFO_PAGESIZE: hb_retni( pBTree->usPageSize ); break;
|
|
case HB_BTREEINFO_KEYSIZE: hb_retni( pBTree->usKeySize ); break;
|
|
case HB_BTREEINFO_MAXKEYS: hb_retni( pBTree->usMaxKeys ); break;
|
|
case HB_BTREEINFO_MINKEYS: hb_retni( pBTree->usMinKeys ); break;
|
|
case HB_BTREEINFO_FLAGS: hb_retnl( pBTree->ulFlags ); break;
|
|
case HB_BTREEINFO_KEYCOUNT: hb_retnl( pBTree->ulKeyCount ); break;
|
|
case HB_BTREEINFO_ALL:
|
|
default: /* build an array and store all elements from above into it */
|
|
{
|
|
PHB_ITEM info = hb_itemArrayNew( HB_BTREEINFO__SIZE );
|
|
|
|
hb_arraySetC( info, HB_BTREEINFO_FILENAME, pBTree->szFileName );
|
|
hb_arraySetNI( info, HB_BTREEINFO_PAGESIZE, pBTree->usPageSize );
|
|
hb_arraySetNI( info, HB_BTREEINFO_KEYSIZE , pBTree->usKeySize );
|
|
hb_arraySetNI( info, HB_BTREEINFO_MAXKEYS , pBTree->usMaxKeys );
|
|
hb_arraySetNI( info, HB_BTREEINFO_MINKEYS , pBTree->usMinKeys );
|
|
hb_arraySetNL( info, HB_BTREEINFO_FLAGS , pBTree->ulFlags );
|
|
hb_arraySetNL( info, HB_BTREEINFO_KEYCOUNT, pBTree->ulKeyCount );
|
|
|
|
hb_itemReturnRelease( info );
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
HB_FUNB( HB_BTREEEVAL ) /* hb_BTreeEval( hb_BTree_Handle, bBlock, [bForCondition], [bWhileCondition], [nNextRecords], [nRecord], [lRest] ) -- NIL */
|
|
{
|
|
if ( HB_ISNUM( 1 ) && HB_ISBLOCK( 2 ) )
|
|
hb_BTreeEval( BTree_GetTreeIndex( "hb_btreeeval" ), 0 );
|
|
else
|
|
raiseError( EG_ARG, HB_BTREE_EC_INVALIDARG, "Bad argument(s)", HB_ERR_FUNCNAME, hb_pcount() );
|
|
}
|
|
#endif
|
|
|
|
|
|
static void hb_BTree_Initialize( void * cargo )
|
|
{
|
|
/* TODO: initialization code */
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
HB_SYMBOL_UNUSED( cargo );
|
|
|
|
}
|
|
|
|
static void hb_BTree_Terminate( void * cargo )
|
|
{
|
|
/* TODO: termination (cleanup) code */
|
|
|
|
int n;
|
|
|
|
HB_TRACE( HB_TR_DEBUG, ( SRCLINENO ) );
|
|
|
|
HB_SYMBOL_UNUSED( cargo );
|
|
|
|
for ( n = 0; n < s_BTree_List_Count; n++ )
|
|
{
|
|
if ( s_BTree_List[ n ] ) hb_BTreeClose( s_BTree_List[ n ] );
|
|
}
|
|
|
|
if ( s_BTree_List_Count > 0 )
|
|
BufferRelease( s_BTree_List );
|
|
}
|
|
|
|
HB_CALL_ON_STARTUP_BEGIN( _hb_BTree_Initialize_ )
|
|
hb_vmAtInit( hb_BTree_Initialize, NULL );
|
|
hb_vmAtExit( hb_BTree_Terminate, NULL );
|
|
HB_CALL_ON_STARTUP_END( _hb_BTree_Initialize_ )
|
|
|
|
#if defined( HB_PRAGMA_STARTUP )
|
|
#pragma startup _hb_BTree_Initialize_
|
|
#elif defined( HB_DATASEG_STARTUP )
|
|
#define HB_DATASEG_BODY HB_DATASEG_FUNC( _hb_BTree_Initialize_ )
|
|
#include "hbiniseg.h"
|
|
#endif
|
|
|
|
HB_EXTERN_END
|