/* i4remove.c (c)Copyright Sequiter Software Inc., 1988-1996. All rights reserved. */ #include "d4all.h" #ifndef S4UNIX #ifdef __TURBOC__ #pragma hdrstop #endif #endif #ifndef S4CLIENT #ifndef S4OFF_WRITE #ifndef S4INDEX_OFF int tfile4remove( TAG4FILE *t4, const unsigned char *ptr, const long rec ) { int rc ; #ifdef E4PARM_LOW if ( t4 == 0 ) return error4( 0, e4parm_null, E95404 ) ; #endif rc = tfile4go( t4, ptr, rec, 0 ) ; /* returns -1 if error4code( codeBase ) < 0 */ if ( rc < 0 ) return error4stack( t4->codeBase, rc, E95404 ) ; if ( rc ) return r4entry ; return tfile4removeCurrent( t4 ) ; } int tfile4removeCalc( TAG4FILE *t4, long rec ) { unsigned char *ptr ; #ifdef E4PARM_LOW if ( t4 == 0 ) return error4( 0, e4parm_null, E95405 ) ; #endif if ( error4code( t4->codeBase ) < 0 ) return e4codeBase ; tfile4exprKey( t4, &ptr ) ; return tfile4remove( t4, ptr, rec ) ; } #ifndef N4OTHER #ifdef S4FOX /* remove the (current) branch block */ int tfile4removeBranch( TAG4FILE *t4, B4BLOCK *blockOn ) { long lNode, rNode ; INDEX4FILE *i4 ; #ifdef S4BYTE_SWAP char *swapPtr ; S4LONG longVal ; short shortVal ; int i ; #endif i4 = t4->indexFile ; if ( blockOn == (B4BLOCK *)l4first( &t4->blocks ) ) { /* Root Block, do not delete */ #ifdef E4ANALYZE if ( blockOn->header.leftNode != -1 || blockOn->header.rightNode != -1 || blockOn->header.nKeys != 1 ) return error4describe( t4->codeBase, e4info, E80401, tfile4alias( t4 ), i4->file.name, 0 ) ; #endif memset( blockOn->data, 0, B4BLOCK_SIZE - sizeof(B4STD_HEADER) - sizeof(B4NODE_HEADER) ) ; if ( !b4leaf( blockOn ) ) /* if not a leaf, then reset nodeHdr too */ { memset( (void *)&blockOn->nodeHdr, 0, sizeof(B4NODE_HEADER) ) ; b4leafInit( blockOn ) ; } else blockOn->nodeHdr.freeSpace = B4BLOCK_SIZE - sizeof( B4STD_HEADER ) - sizeof( B4NODE_HEADER ) ; blockOn->header.nKeys = 0 ; blockOn->header.leftNode = -1 ; blockOn->header.rightNode = -1 ; blockOn->keyOn = -1 ; blockOn->builtOn = -1 ; blockOn->header.nodeAttribute = 3 ; /* root and leaf */ blockOn->changed = 1 ; } else /* This block is to be deleted */ { lNode = blockOn->header.leftNode ; rNode = blockOn->header.rightNode ; l4remove( &t4->blocks, blockOn ) ; if ( index4shrink( i4, blockOn->fileBlock ) < 0 ) return 0 ; blockOn->changed = 0 ; if ( lNode != -1L ) { if ( file4readAll( &i4->file, I4MULTIPLY*lNode, &blockOn->header, B4BLOCK_SIZE) < 0 ) return 0 ; #ifdef S4BYTE_SWAP blockOn->header.nodeAttribute = x4reverseShort( (void *)&blockOn->header.nodeAttribute ) ; blockOn->header.nKeys = x4reverseShort( (void *)&blockOn->header.nKeys ) ; blockOn->header.leftNode = x4reverseLong( (void *)&blockOn->header.leftNode ) ; blockOn->header.rightNode = x4reverseLong( (void *)&blockOn->header.rightNode ) ; /* if block is a leaf */ if (blockOn->header.nodeAttribute >= 2 ) { blockOn->nodeHdr.freeSpace = x4reverseShort( (void *)&blockOn->nodeHdr.freeSpace ) ; longVal = x4reverseLong( (void *)&blockOn->nodeHdr.recNumMask[0] ) ; memcpy( (void *)&blockOn->nodeHdr.recNumMask[0], (void *)&longVal, sizeof(long) ) ; } else /* if block is a branch */ { shortVal = blockOn->tag->header.keyLen + sizeof(S4LONG) ; /* position swapPtr to end of first key expression */ swapPtr = (char *) &blockOn->nodeHdr.freeSpace + blockOn->tag->header.keyLen ; /* move through all B4KEY's to swap 'long's */ for ( i = 0 ; i < (int) blockOn->header.nKeys ; i++ ) { longVal = x4reverseLong( (void *)swapPtr ) ; memcpy( swapPtr, (void *) &longVal, sizeof(S4LONG) ) ; swapPtr += sizeof(S4LONG) ; longVal = x4reverseLong( (void *)swapPtr ) ; memcpy( swapPtr, (void *) &longVal, sizeof(S4LONG) ) ; swapPtr += shortVal ; } } #endif blockOn->fileBlock = lNode ; blockOn->header.rightNode = rNode ; blockOn->changed = 1 ; b4flush( blockOn ) ; } if ( rNode != -1L ) { if ( file4readAll( &i4->file, I4MULTIPLY*rNode, &blockOn->header, B4BLOCK_SIZE) < 0 ) return 0 ; #ifdef S4BYTE_SWAP blockOn->header.nodeAttribute = x4reverseShort( (void *)&blockOn->header.nodeAttribute ) ; blockOn->header.nKeys = x4reverseShort( (void *)&blockOn->header.nKeys ) ; blockOn->header.leftNode = x4reverseLong( (void *)&blockOn->header.leftNode ) ; blockOn->header.rightNode = x4reverseLong( (void *)&blockOn->header.rightNode ) ; /* if block is a leaf */ if (blockOn->header.nodeAttribute >= 2 ) { blockOn->nodeHdr.freeSpace = x4reverseShort( (void *)&blockOn->nodeHdr.freeSpace ) ; longVal = x4reverseLong( (void *)blockOn->nodeHdr.recNumMask ) ; memcpy( (void *)blockOn->nodeHdr.recNumMask, (void *)&longVal, sizeof(long) ) ; } else /* if block is a branch */ { shortVal = blockOn->tag->header.keyLen + sizeof(S4LONG) ; /* position swapPtr to end of first key expression */ swapPtr = (char *) &blockOn->nodeHdr.freeSpace + blockOn->tag->header.keyLen ; /* move through all B4KEY's to swap 'long's */ for ( i = 0 ; i < (int) blockOn->header.nKeys ; i++ ) { longVal = x4reverseLong( (void *)swapPtr ) ; memcpy( swapPtr, (void *) &longVal, sizeof(S4LONG) ) ; swapPtr += sizeof(S4LONG) ; longVal = x4reverseLong( (void *)swapPtr ) ; memcpy( swapPtr, (void *) &longVal, sizeof(S4LONG) ) ; swapPtr += shortVal ; } } #endif blockOn->fileBlock = rNode ; blockOn->header.leftNode = lNode ; blockOn->changed = 1 ; b4flush( blockOn ) ; } return b4free( blockOn ) ; } return 0 ; } #endif /* Remove the current key */ int tfile4removeCurrent( TAG4FILE *t4 ) { B4BLOCK *blockOn ; INDEX4FILE *i4 ; #ifdef S4FOX void *newKeyInfo ; long rec = 0L ; int blRemoved, lessThanLast ; #else int removeDone, updateReqd ; B4BLOCK *blockIterate ; #endif i4 = t4->indexFile ; #ifdef S4FOX i4->tagIndex->header.version = i4->versionOld+1 ; newKeyInfo = 0 ; for ( blockOn = (B4BLOCK *)t4->blocks.lastNode ; blockOn ; ) { blRemoved = 0 ; if ( newKeyInfo == 0 ) /* then delete entry */ { if ( b4lastpos( blockOn ) == 0 ) { if ( blockOn != (B4BLOCK *)l4first( &t4->blocks ) ) blRemoved = 1 ; tfile4removeBranch( t4, blockOn ) ; blockOn = (B4BLOCK *)t4->blocks.lastNode ; } else { lessThanLast = 0 ; if ( blockOn->keyOn < b4lastpos( blockOn ) ) lessThanLast = 1 ; b4remove( blockOn ) ; if ( lessThanLast ) return 0 ; /* On last entry */ b4goEof( blockOn ) ; blockOn->keyOn-- ; /* update keyOn for goint to last spot */ newKeyInfo = b4keyKey( blockOn, blockOn->keyOn ) ; rec = b4recNo( blockOn, blockOn->keyOn ) ; } } else /* Adjust entry */ { if ( b4brReplace( blockOn, (unsigned char *)newKeyInfo, rec ) < 0 ) return -1 ; if ( blockOn->keyOn != b4lastpos( blockOn ) ) /* not on end key, so exit, else continue */ return 0 ; } if ( !blRemoved ) { blockOn = (B4BLOCK *)blockOn->link.p ; if ( blockOn == (B4BLOCK *)t4->blocks.lastNode ) break ; } } #endif #ifdef S4MDX removeDone = 0 ; updateReqd = 0 ; i4->changed = 1 ; t4->changed = 1 ; t4->header.version++ ; blockIterate = (B4BLOCK *)l4last( &t4->blocks ) ; for ( ;; ) { blockOn = blockIterate ; if ( blockOn == 0 ) break ; blockIterate = (B4BLOCK *)l4prev( &t4->blocks, blockIterate ) ; /* Calculate the previous block while the current block exists. */ if ( !removeDone ) /* either removing or else updating */ { if ( b4lastpos( blockOn ) == 0 ) /* delete block */ { if ( blockOn == (B4BLOCK *)l4first( &t4->blocks ) ) /* root block, don't delete */ { blockOn->changed = 1 ; blockOn->keyOn = 0 ; memset( (void *)&blockOn->nKeys, 0, i4->header.blockRw ) ; if (t4->filter ) /* must modify the filter setting for dBASE IV compatibility */ { file4write( &t4->indexFile->file, t4->headerOffset+sizeof(t4->header)+222, (char *)"\0", (int)1 ) ; t4->hasKeys = 0 ; #ifdef S4MDX t4->hadKeys = 1 ; #endif } return 0 ; } else { l4remove( &t4->blocks, blockOn ) ; if ( index4shrink( i4, blockOn->fileBlock) < 0 ) return -1 ; b4free( blockOn ) ; blockOn = 0 ; } } else /* just remove entry */ { if ( blockIterate && blockOn->keyOn == b4lastpos( blockOn ) && b4lastpos( blockOn ) > 0 ) /* save to become last key of the block for parent update */ { memcpy( t4->codeBase->savedKey, b4keyKey( blockOn, blockOn->nKeys - 1 - b4leaf(blockOn) ), t4->header.keyLen ) ; updateReqd = 1 ; } else updateReqd = 0 ; b4remove( blockOn ) ; if ( !b4leaf( blockOn ) && b4lastpos( blockOn ) == 0 ) /* branch with only one entry, so have the entry take place of this block */ { if (blockOn == (B4BLOCK *)l4first( &t4->blocks ) ) /* the entry becomes the new root */ { /* first update the tags root */ t4->header.root = b4key( blockOn, 0 )->num ; #ifdef S4BYTE_SWAP t4->header.root = x4reverseLong( (void *)&t4->header.root ) ; #endif file4write( &t4->indexFile->file, t4->headerOffset, (void *)&t4->header.root, sizeof(t4->header.root) ) ; #ifdef S4BYTE_SWAP t4->header.root = x4reverseLong( (void *)&t4->header.root ) ; #endif updateReqd = 0 ; } else /* remove this branch block */ { blockIterate->changed = 1 ; b4key( blockIterate, blockIterate->keyOn )->num = b4key( blockOn, 0 )->num ; } l4remove( &t4->blocks, blockOn ) ; if ( index4shrink( t4->indexFile, blockOn->fileBlock) < 0 ) return -1 ; blockOn->changed = 0 ; b4free( blockOn ) ; blockOn = 0 ; } else blockOn->keyOn = b4lastpos( blockOn ) ; if ( !updateReqd ) return 0 ; removeDone = 1 ; } } else /* Adjust entry - at most one update will be required in MDX */ { if ( blockOn->keyOn < b4lastpos( blockOn ) ) { blockOn->changed = 1 ; memcpy( b4keyKey( blockOn, blockOn->keyOn), t4->codeBase->savedKey, t4->header.keyLen ) ; return 0 ; } } } #endif return 0 ; } #endif /* ifndef N4OTHER */ #ifdef S4CLIPPER int tfile4balanceBranch( TAG4FILE *, B4BLOCK * ) ; static int tfile4removeRef( TAG4FILE *t4 ) { B4KEY_DATA *myKeyData ; B4BLOCK *blockOn, *blockUp, *neighborBlock ; long reference, neighborRef ; int i, rc ; blockOn = (B4BLOCK *)t4->blocks.lastNode ; tfile4up( t4 ) ; blockUp = (B4BLOCK *)t4->blocks.lastNode ; if ( blockUp == 0 ) /* root block only, so reference doesn't exist */ { /* reset the block */ short offset = ( t4->header.keysMax + 2 + ( (t4->header.keysMax/2)*2 != t4->header.keysMax ) ) * sizeof(short) ; for ( i = 0 ; i <= t4->header.keysMax ; i++ ) blockOn->pointers[i] = (short)( t4->header.groupLen * i ) + offset ; return 0 ; } else /* delete the block */ { tfile4shrink( t4, blockOn->fileBlock ) ; l4pop( &t4->saved ) ; blockOn->changed = 0 ; b4free( blockOn ) ; blockOn = 0 ; } blockOn = blockUp ; myKeyData = (B4KEY_DATA *)u4allocEr( t4->codeBase, t4->header.groupLen ) ; if ( myKeyData == 0 ) return e4memory ; if ( blockOn->keyOn >= blockOn->nKeys ) memcpy( &(myKeyData->num), &( b4key( blockOn, blockOn->keyOn - 1 )->num), sizeof(long) + t4->header.keyLen ) ; else memcpy( &(myKeyData->num), &( b4key( blockOn, blockOn->keyOn )->num), sizeof(long) + t4->header.keyLen ) ; if ( blockOn->nKeys == 1 ) { /* take the branch, and put in place of me, then delete and add me */ if ( blockOn->keyOn >= blockOn->nKeys ) reference = (long)b4key( blockOn, 0 )->pointer ; else reference = (long)b4key( blockOn, 1 )->pointer ; b4remove( blockOn ) ; if ( t4->blocks.nLink == 1 ) /* root block, so reference will take over */ { tfile4shrink( t4, blockOn->fileBlock ) ; tfile4up( t4 ) ; l4pop(&t4->saved ) ; blockOn->changed = 0 ; b4free( blockOn ) ; blockOn = (B4BLOCK *)t4->blocks.lastNode ; t4->header.root = reference ; } else /* special case -- blocks contain very large keys. try balancing */ { tfile4up( t4 ) ; blockUp = (B4BLOCK *)t4->blocks.lastNode ; if ( blockUp->keyOn == 0 ) { /* move one right neighbour into left */ neighborRef = (long)b4key( blockUp, 1 )->pointer ; neighborBlock = b4alloc( t4, neighborRef ) ; if ( neighborBlock == 0 ) { u4free( myKeyData ) ; return e4memory ; } if ( i4readBlock( &t4->file, neighborRef, 0, neighborBlock ) < 0 ) { b4free( neighborBlock ) ; neighborBlock = 0 ; u4free( myKeyData ) ; return -1 ; } if ( neighborBlock->nKeys == t4->header.keysMax ) /* already maxed out, must shift */ { /* borrow for the current block */ reference = b4key( blockOn, 0 )->pointer ; blockOn->keyOn = 0 ; b4insert( blockOn, b4keyKey( blockUp, 0 ), b4recNo( blockUp, 0 ), reference / 512 ) ; memcpy( b4keyKey( blockUp, 0 ), b4keyKey( neighborBlock, 0 ), t4->header.keyLen ) ; b4key( blockUp, 0 )->num = b4key( neighborBlock, 0 )->num ; b4key( blockOn, 1 )->pointer = b4key( neighborBlock, 0 )->pointer ; neighborBlock->keyOn = 0 ; b4remove( neighborBlock ) ; blockUp->changed = 1 ; blockOn->changed = 1 ; neighborBlock->changed = 1 ; b4flush( neighborBlock ) ; b4free( neighborBlock ) ; neighborBlock = 0 ; } else { /* move current reference over, and delete from parent */ neighborBlock->keyOn = 0 ; b4insert( neighborBlock, b4keyKey( blockUp, blockUp->keyOn ), b4key( blockUp, blockUp->keyOn )->num, b4key( blockOn, 0 )->pointer / 512 ) ; b4remove( blockUp ) ; #ifdef E4INDEX_VERIFY if ( (B4BLOCK *)t4->saved.lastNode != blockOn ) return error4( t4->codeBase, e4index, E81601 ) ; #endif l4pop( &t4->saved ) ; blockOn->changed = 0 ; tfile4shrink( t4, blockOn->fileBlock ) ; b4free( blockOn ) ; blockOn = 0 ; blockUp->changed = 1 ; neighborBlock->changed = 1 ; /* now check that parent is still valid */ if ( blockUp->nKeys == 0 ) /* just removed last entry... */ { if ( t4->header.root == blockUp->fileBlock * 512 ) { tfile4shrink( t4, blockUp->fileBlock ) ; l4pop(&t4->blocks ) ; /* remov ourselves */ reference = (long)b4key( blockUp, 0 )->pointer ; blockUp->changed = 0 ; b4free( blockUp ) ; blockOn = 0 ; l4add( &t4->blocks, neighborBlock ) ; t4->header.root = reference ; } else { b4flush( neighborBlock ) ; b4free( neighborBlock ) ; neighborBlock = 0 ; tfile4balanceBranch( t4, blockUp ) ; } } else /* save and free */ { b4flush( neighborBlock ) ; b4free( neighborBlock ) ; neighborBlock = 0 ; } } } else { /* move to left neighbor */ neighborRef = (long)b4key( blockUp, blockUp->keyOn - 1 )->pointer ; neighborBlock = b4alloc( t4, neighborRef ) ; if ( neighborBlock == 0 ) { u4free( myKeyData ) ; return e4memory ; } if ( i4readBlock( &t4->file, neighborRef, 0, neighborBlock ) < 0 ) { b4free( neighborBlock ) ; neighborBlock = 0 ; u4free( myKeyData ) ; return -1 ; } if ( neighborBlock->nKeys == t4->header.keysMax ) /* already maxed out */ { /* borrow for the current block */ blockOn->keyOn = 0 ; reference = b4key( blockOn, 0 )->pointer ; b4insert( blockOn, b4keyKey( blockUp, blockUp->keyOn - 1 ), b4key( blockUp, blockUp->keyOn - 1 )->num, b4key( neighborBlock, neighborBlock->nKeys )->pointer / 512 ) ; memcpy( b4keyKey( blockUp, blockUp->keyOn - 1 ), b4keyKey( neighborBlock, neighborBlock->nKeys - 1 ), t4->header.keyLen ) ; b4key( blockUp, blockUp->keyOn - 1 )->num = b4key( neighborBlock, neighborBlock->nKeys - 1 )->num ; b4key( blockOn, 1 )->pointer = reference ; neighborBlock->nKeys-- ; blockUp->changed = 1 ; blockOn->changed = 1 ; neighborBlock->changed = 1 ; b4flush( neighborBlock ) ; b4free( neighborBlock ) ; neighborBlock = 0 ; } else { /* move current reference over, and delete from parent */ neighborBlock->keyOn = neighborBlock->nKeys ; /* just re-insert the last key, then change the values */ b4insert( neighborBlock, b4keyKey( neighborBlock, neighborBlock->keyOn - 1 ), b4key( neighborBlock, neighborBlock->keyOn - 1 )->num, b4key( neighborBlock, neighborBlock->keyOn )->pointer / 512 ) ; memcpy( b4keyKey( neighborBlock, neighborBlock->nKeys - 1 ), b4keyKey( blockUp, blockUp->keyOn - 1 ), t4->header.keyLen ) ; b4key( neighborBlock, neighborBlock->nKeys - 1 )->num = b4key( blockUp, blockUp->keyOn - 1 )->num ; b4key( neighborBlock, neighborBlock->nKeys )->pointer = b4key( blockOn, 0 )->pointer ; #ifdef E4INDEX_VERIFY if ( (B4BLOCK *)t4->saved.lastNode != blockOn ) return error4( t4->codeBase, e4index, E81601 ) ; #endif l4pop( &t4->saved ) ; blockOn->changed = 0 ; tfile4shrink( t4, blockOn->fileBlock ) ; b4free( blockOn ) ; blockOn = 0 ; b4key( blockUp, blockUp->keyOn )->pointer = b4key( blockUp, blockUp->keyOn - 1 )->pointer ; blockUp->keyOn-- ; b4remove( blockUp ) ; blockUp->changed = 1 ; neighborBlock->changed = 1 ; /* now check that parent is still valid */ if ( blockUp->nKeys == 0 ) /* just removed last entry... */ { if ( t4->header.root == blockUp->fileBlock * 512 ) { reference = (long)b4key( blockUp, 0 )->pointer ; tfile4shrink( t4, blockUp->fileBlock ) ; l4pop(&t4->blocks ) ; /* remov ourselves */ blockUp->changed = 0 ; b4free( blockUp ) ; blockUp = 0 ; l4add( &t4->blocks, neighborBlock ) ; t4->header.root = reference ; } else { b4flush( neighborBlock ) ; b4free( neighborBlock ) ; neighborBlock = 0 ; tfile4balanceBranch( t4, blockUp ) ; } } else /* save and free */ { b4flush( neighborBlock ) ; b4free( neighborBlock ) ; neighborBlock = 0 ; } } } } } else /* just remove myself, add later */ { b4remove( blockOn ) ; if ( blockOn->nKeys < t4->header.keysHalf && blockOn->fileBlock != t4->header.root ) /* if not root may have to balance the tree */ { if ( tfile4balanceBranch( t4, blockOn ) < 0 ) { u4free( myKeyData ) ; return -1 ; } } } /* now add the removed reference back into the index */ rc = tfile4add( t4, (unsigned char *)myKeyData->value, myKeyData->num, 0 ) ; u4free( myKeyData ) ; return rc ; } static int tfile4balanceBlock( TAG4FILE *t4, B4BLOCK *parent, B4BLOCK *b4, B4BLOCK *b4temp, char isRight, char balanceMode ) { B4KEY_DATA *bdata, *bdata2 ; int avg, i ; long tempFb, reference ; if ( balanceMode == 2 && b4leaf( b4temp ) ) return error4( t4->codeBase, e4index, E81601 ) ; else { if ( b4temp->nKeys + b4->nKeys < t4->header.keysMax ) { if ( isRight ) { if ( balanceMode == 2 ) { b4->keyOn = 0 ; parent->keyOn-- ; bdata = b4key( parent, parent->keyOn ) ; if ( b4->keyOn == 0 && b4->nKeys == 0 ) /* must do reference as well */ { reference = b4key( b4, 0 )->pointer ; b4insert( b4, bdata->value, bdata->num, b4key( b4temp, b4temp->nKeys )->pointer / 512 ) ; b4key( b4, 1 ) ->pointer = reference ; } else b4insert( b4, bdata->value, bdata->num, b4key( b4temp, b4temp->nKeys )->pointer / 512 ) ; for ( i = b4temp->nKeys - 1 ; i >= 0 ; i-- ) { b4->keyOn = 0 ; bdata = b4key( b4temp, i ) ; b4insert( b4, bdata->value, bdata->num, bdata->pointer / 512 ) ; } b4temp->changed = 0 ; tfile4shrink( t4, b4temp->fileBlock ) ; b4remove( parent ) ; if ( parent->nKeys < t4->header.keysHalf ) tfile4balanceBranch( t4, parent ) ; } else { for ( i = 0 ; i < b4->nKeys ; i++ ) { b4goEof( b4temp ) ; bdata = b4key( b4, i ) ; b4insert( b4temp, bdata->value, bdata->num, 0L ) ; } t4->codeBase->doIndexVerify = 0 ; /* avoid verify errors due to our partial removal */ do { tfile4down( t4 ) ; if ( t4->blocks.lastNode == 0 ) break ; } while( !b4leaf( (B4BLOCK *)t4->blocks.lastNode ) ) ; /* don't use tfile4block() due to borland problems */ t4->codeBase->doIndexVerify = 1 ; b4flush( b4temp ) ; if ( tfile4removeRef( t4 ) != 0 ) /* will delete b4 and will remove and re-add the reference if required */ return -1 ; } } else { /* put parent entry, then all but one child, then last child to parent */ b4goEof( b4 ) ; if ( balanceMode == 2 ) { b4temp->keyOn = 0 ; bdata = b4key( parent, parent->keyOn ) ; b4insert( b4temp, bdata->value, bdata->num, b4key( b4, b4->nKeys )->pointer / 512 ) ; for ( i = b4->nKeys - 1 ; i >= 0 ; i-- ) { b4temp->keyOn = 0 ; bdata = b4key( b4, i ) ; b4insert( b4temp, bdata->value, bdata->num, bdata->pointer / 512 ) ; } tfile4shrink( t4, b4->fileBlock ) ; l4pop( &t4->saved ) ; b4->changed = 0 ; b4free( b4 ) ; b4 = 0 ; b4flush( b4temp ) ; b4remove( parent ) ; if ( parent->nKeys < t4->header.keysHalf ) tfile4balanceBranch( t4, parent ) ; } else { bdata = b4key( parent, parent->keyOn ) ; if ( b4->keyOn == 0 && b4->nKeys == 0 ) /* must do reference as well */ { reference = b4key( b4, 0 )->pointer ; b4insert( b4, bdata->value, bdata->num, 0L ) ; b4key( b4, 1 ) ->pointer = reference ; } else b4insert( b4, bdata->value, bdata->num, 0L ) ; for ( i = 0 ; i < b4temp->nKeys - 1 ; i++ ) { b4goEof( b4 ) ; bdata = b4key( b4temp, i ) ; b4insert( b4, bdata->value, bdata->num, 0L ) ; } bdata = b4key( parent, parent->keyOn ) ; bdata2 = b4key( b4temp, b4temp->nKeys - 1 ) ; memcpy( bdata->value, bdata2->value, t4->header.keyLen ) ; memcpy( &( bdata->num ), &bdata2->num, sizeof( long ) ) ; parent->keyOn++ ; parent->changed = 1 ; t4->codeBase->doIndexVerify = 0 ; /* avoid verify errors due to our partial removal */ b4flush( b4 ) ; do { if ( tfile4down( t4 ) == 1 ) break ; } while( ((B4BLOCK *)t4->blocks.lastNode)->fileBlock != b4temp->fileBlock ) ; t4->codeBase->doIndexVerify = 1 ; if ( tfile4removeRef( t4 ) != 0 ) /* will delete b4temp and will remove and re-add the reference if required */ return -1 ; b4temp->changed = 0 ; } } } else /* will have to redistribute keys */ { avg = ( b4->nKeys + b4temp->nKeys + 1 ) / 2 ; if ( avg < t4->header.keysHalf ) avg = t4->header.keysHalf ; if ( isRight ) { b4->keyOn = 0 ; parent->keyOn-- ; bdata = b4key( parent, parent->keyOn ) ; if ( balanceMode == 2 ) { if ( b4->nKeys == 0 ) { tempFb = b4key( b4, 0 )->pointer ; b4insert( b4, bdata->value, bdata->num, b4key( b4temp, b4temp->nKeys )->pointer / 512 ) ; b4append( b4, tempFb / 512 ) ; } else b4insert( b4, bdata->value, bdata->num, b4key( b4temp, b4temp->nKeys )->pointer / 512 ) ; } else b4insert( b4, bdata->value, bdata->num, 0L ) ; while ( b4temp->nKeys > avg && b4->nKeys < avg ) { b4->keyOn = 0 ; b4temp->keyOn = b4temp->nKeys - 1 ; bdata = b4key( b4temp, b4temp->keyOn ) ; if ( balanceMode == 2 ) b4insert( b4, bdata->value, bdata->num, bdata->pointer / 512 ) ; else b4insert( b4, bdata->value, bdata->num, 0L ) ; b4remove( b4temp ) ; } b4temp->keyOn = b4temp->nKeys - 1 ; } else { b4goEof( b4 ) ; if ( balanceMode == 2 ) { bdata = b4key( b4, b4->nKeys ) ; bdata2 = b4key( parent, parent->keyOn ) ; memcpy( bdata->value, bdata2->value, t4->header.keyLen ) ; memcpy( &(bdata->num), &bdata2->num, sizeof( long ) ) ; b4->nKeys++ ; } else { bdata = b4key( parent, parent->keyOn ) ; b4insert( b4, bdata->value, bdata->num, 0L ) ; } while ( b4->nKeys - (balanceMode == 2) < avg ) { b4goEof( b4 ) ; b4temp->keyOn = 0 ; bdata = b4key( b4temp, b4temp->keyOn ) ; if ( balanceMode == 2 ) b4insert( b4, bdata->value, bdata->num, bdata->pointer / 512 ) ; else b4insert( b4, bdata->value, bdata->num, 0L ) ; b4remove( b4temp ) ; } if ( balanceMode == 2 ) { b4->nKeys-- ; b4->keyOn-- ; } b4temp->keyOn = 0 ; } /* and add a key back to the parent */ bdata = b4key( parent, parent->keyOn ) ; if ( balanceMode == 2 ) { if ( isRight ) bdata2 = b4key( b4temp, b4temp->nKeys - 1 ) ; else bdata2 = b4key( b4, b4->nKeys ) ; memcpy( bdata->value, bdata2->value, t4->header.keyLen ) ; memcpy( &(bdata->num), &bdata2->num, sizeof( long ) ) ; if ( isRight ) { b4temp->keyOn = b4temp->nKeys ; b4remove( b4temp ) ; } } else { bdata2 = b4key( b4temp, b4temp->keyOn ) ; memcpy( bdata->value, bdata2->value, t4->header.keyLen ) ; memcpy( &(bdata->num), &bdata2->num, sizeof( long ) ) ; b4remove( b4temp ) ; } parent->changed = 1 ; b4flush( b4temp ) ; } } return 0 ; } static int tfile4balanceBranchLeaf( TAG4FILE *t4, B4BLOCK *parent, B4BLOCK *branch, B4BLOCK *leaf ) { B4BLOCK *b4temp ; long newFileBlock ; B4KEY_DATA *key ; t4->header.version++ ; newFileBlock = tfile4extend( t4 ) ; b4temp = b4alloc( t4, newFileBlock ) ; if ( b4temp == 0 ) return -1 ; key = b4key( b4temp, 0 ) ; key->pointer = leaf->fileBlock * I4MULTIPLY ; b4temp->changed = 1 ; key = b4key( parent, parent->keyOn ) ; #ifdef E4ANALYZE_ALL if ( key->pointer != leaf->fileBlock * I4MULTIPLY ) return error4( t4->codeBase, e4index, E95407 ) ; #endif key->pointer = newFileBlock * I4MULTIPLY ; parent->changed = 1 ; l4add( &t4->blocks, b4temp ) ; return tfile4balanceBlock( t4, parent, b4temp, branch, 1, 2 ) ; } /* if doFull is true, the whole branch set will be balanced, not just the current leaf block and reqd. Also, this will balance top down instead of the other way. */ int tfile4balance( TAG4FILE *t4, B4BLOCK *b4, int doFull ) { B4BLOCK *b4temp, *parent ; long tempFb ; int rc, noKeys, doRightSpecial ; char isRight, balanceMode ; /* balanceMode = 0 for done, 1 for leaf, and 2 for branch */ B4KEY_DATA *myKeyData ; if ( !b4leaf( b4 ) ) return 0 ; b4temp = b4alloc( t4, 0L ) ; if ( b4temp == 0 ) return -1 ; if ( doFull ) { tfile4upToRoot( t4 ) ; balanceMode = 2 ; /* branch */ } else balanceMode = 1 ; /* leaf */ noKeys = 0 ; while ( balanceMode != 0 || doFull ) { if ( doFull ) { parent = (B4BLOCK *)t4->blocks.lastNode ; if ( noKeys == 0 ) parent->keyOn = parent->nKeys ; rc = tfile4down( t4 ) ; if ( rc == 1 ) /* make sure siblings are also leafs */ { tfile4up( t4 ) ; parent = (B4BLOCK *)t4->blocks.lastNode ; if ( parent != 0 ) { if ( parent->keyOn > 0 ) { tempFb = (long)b4key( parent, parent->keyOn - 1 )->pointer ; if ( i4readBlock( &t4->file, tempFb, 0, b4temp ) < 0 ) { b4free( b4temp ) ; return -1 ; } if ( b4leaf( b4temp ) == 0 && b4leaf( b4 ) == 1 ) { rc = tfile4balanceBranchLeaf( t4, parent, b4temp, b4 ) ; b4free( b4temp ) ; return rc ; } } } break ; } else if ( noKeys == 1 ) /* need to check the siblings to make sure also branches */ { tfile4up( t4 ) ; parent = (B4BLOCK *)t4->blocks.lastNode ; if ( parent != 0 ) { if ( parent->keyOn > 0 ) { tempFb = (long)b4key( parent, parent->keyOn - 1 )->pointer ; if ( i4readBlock( &t4->file, tempFb, 0, b4temp ) < 0 ) { b4free( b4temp ) ; return -1 ; } if ( b4leaf( b4temp ) == 0 && b4leaf( b4 ) == 1 ) { rc = tfile4balanceBranchLeaf( t4, parent, b4temp, b4 ) ; b4free( b4temp ) ; return rc ; } if ( b4leaf( b4temp ) == 1 && b4leaf( b4 ) == 0 ) { rc = tfile4balanceBranchLeaf( t4, parent, b4, b4temp ) ; b4free( b4temp ) ; return rc ; } } else { parent->keyOn = 0 ; tempFb = (long)b4key( parent, parent->keyOn + 1 )->pointer ; if ( i4readBlock( &t4->file, tempFb, 0, b4temp ) < 0 ) { b4free( b4temp ) ; return -1 ; } if ( b4leaf( b4temp ) == 0 && b4leaf( b4 ) == 1 ) { rc = tfile4balanceBranchLeaf( t4, parent, b4temp, b4 ) ; b4free( b4temp ) ; return rc ; } if ( b4leaf( b4temp ) == 1 && b4leaf( b4 ) == 0 ) { rc = tfile4balanceBranchLeaf( t4, parent, b4, b4temp ) ; b4free( b4temp ) ; return rc ; } } } if ( b4leaf( b4 ) ) break ; tfile4down( t4 ) ; tfile4down( t4 ) ; b4 = (B4BLOCK *)t4->blocks.lastNode ; tfile4up( t4 ) ; continue ; } if ( rc < 0 ) return -1 ; b4 = (B4BLOCK *)t4->blocks.lastNode ; b4->keyOn = b4->nKeys ; if ( b4->nKeys >= t4->header.keysHalf ) continue ; if ( b4leaf( b4 ) ) balanceMode = 1 ; tempFb = b4key( parent, parent->keyOn - 1 )->pointer ; isRight = 1 ; } else { if ( balanceMode == 2 ) /* branch */ { b4 = parent ; while( t4->blocks.lastNode != (LINK4 *)b4 ) /* re-align ourselves */ tfile4up( t4 ) ; } tfile4up( t4 ) ; parent = (B4BLOCK *)t4->blocks.lastNode ; if ( parent == 0 ) /* root block */ { tfile4down( t4 ) ; parent = (B4BLOCK *)t4->blocks.lastNode ; if ( !b4leaf( parent ) && parent->nKeys == 0 ) /* remove */ { tempFb = (long)b4key( parent, 0 )->pointer ; tfile4shrink( t4, parent->fileBlock ) ; tfile4up( t4 ) ; l4pop( &t4->saved ) ; parent->changed = 0 ; b4free( parent ) ; parent = 0 ; t4->header.root = tempFb ; tfile4down( t4 ) ; } break ; } if ( parent->nKeys == 0 ) /* try to replace parent with ourself */ return error4describe( t4->codeBase, e4index, E81601, tfile4alias( t4 ), 0, 0 ) ; if ( b4->nKeys >= t4->header.keysHalf ) { balanceMode = 0 ; continue ; } isRight = (parent->keyOn == parent->nKeys ) ; if ( isRight ) tempFb = b4key( parent, parent->keyOn - 1 )->pointer ; else tempFb = b4key( parent, parent->keyOn + 1 )->pointer ; } b4temp->fileBlock = tempFb ; for ( ;; ) { if ( i4readBlock( &t4->file, tempFb, 0, b4temp ) < 0 ) { b4free( b4temp ) ; return -1 ; } if ( balanceMode == 2 || b4leaf( b4temp ) ) /* if branch mode or leaf mode and leaf block found */ break ; if ( isRight ) tempFb = b4key( b4temp, b4temp->nKeys )->pointer ; else tempFb = b4key( b4temp, 0 )->pointer ; } /* save a key position for after-seek */ myKeyData = (B4KEY_DATA *)u4allocEr( t4->codeBase, t4->header.groupLen ) ; if ( myKeyData == 0 ) return e4memory ; doRightSpecial = 0 ; if ( b4->nKeys == 0 ) /* can't do a seek on, so after just go to far left */ { noKeys = 1 ; if ( isRight == 1 && parent != 0 ) /* get parents key, since performing a right-op */ { memcpy( &(myKeyData->num), &( b4key( parent, parent->nKeys - 1 )->num), sizeof(long) + t4->header.keyLen ) ; doRightSpecial = 1 ; } else memcpy( &(myKeyData->num), &( b4key( b4temp, 0 )->num), sizeof(long) + t4->header.keyLen ) ; } else { noKeys = 0 ; if ( b4->keyOn > 0 ) memcpy( &(myKeyData->num), &( b4key( b4, 0 )->num), sizeof(long) + t4->header.keyLen ) ; else memcpy( &(myKeyData->num), &( b4key( b4, 1 )->num), sizeof(long) + t4->header.keyLen ) ; } if ( tfile4balanceBlock( t4, parent, b4, b4temp, isRight, balanceMode ) < 0 ) { u4free( myKeyData ) ; return -1 ; } /* need to re-find position */ rc = tfile4go( t4, myKeyData->value, myKeyData->num, 0 ) ; /* returns -1 if error4code( codeBase ) < 0 */ if ( noKeys == 1 ) /* can't find position, so just go to far left */ { for ( ;; ) { b4 = tfile4block( t4 ) ; if ( b4->nKeys < t4->header.keysHalf || b4leaf( b4 ) ) { if ( doRightSpecial == 1 ) /* in this case, don't reset position, just go up */ while ( tfile4up( t4 ) != 1 ) ; else tfile4upToRoot( t4 ) ; balanceMode = 2 ; doFull = 1 ; break ; } if ( doRightSpecial == 1 ) { b4->keyOn = b4->nKeys ; tfile4down( t4 ) ; } else if ( tfile4skip( t4, 1L ) != 1L ) break ; } } b4 = tfile4block( t4 ) ; u4free( myKeyData ) ; if ( rc < 0 ) return rc ; if ( b4 == 0 ) /* in theory should never happen, but would maybe mean done */ return 0 ; parent = (B4BLOCK *)b4->link.p ; if ( parent == 0 || b4leaf( parent ) ) /* no parent */ break ; if ( parent == b4 ) /* move down one to avoid problems (b4 should never be root) */ { if ( tfile4down( t4 ) == 0 ) { b4 = (B4BLOCK *)t4->blocks.lastNode ; tfile4up( t4 ) ; } } if ( balanceMode != 2 || noKeys != 1 ) { if ( doFull ) { if ( b4leaf( b4 ) ) balanceMode = 0 ; } else { if ( parent->nKeys < t4->header.keysHalf ) /* do parent as well */ balanceMode = 2 ; else balanceMode = 0 ; } } } b4free( b4temp ) ; return 0 ; } int tfile4getReplaceEntry( TAG4FILE *t4, B4KEY_DATA *insertSpot, B4BLOCK *saveBlock ) { int rc ; B4BLOCK *blockOn ; blockOn = saveBlock ; if ( b4leaf( blockOn ) ) return 0 ; blockOn->keyOn = blockOn->keyOn + 1 ; while ( !b4leaf( blockOn ) ) { rc = tfile4down( t4 ) ; if ( rc < 0 || rc == 2 ) return -1 ; blockOn = (B4BLOCK *)t4->blocks.lastNode ; } memcpy( &insertSpot->num, &(b4key( blockOn, blockOn->keyOn )->num), sizeof(long) + t4->header.keyLen ) ; saveBlock->changed = 1 ; b4remove( blockOn ) ; if ( blockOn->nKeys < t4->header.keysHalf && blockOn->fileBlock != t4->header.root ) /* if not root may have to balance the tree */ if ( tfile4balance( t4, blockOn, 0 ) < 0 ) return -1 ; return 1 ; } int tfile4balanceBranch( TAG4FILE *t4, B4BLOCK *b4 ) { B4BLOCK *parent, *b4temp ; int rc ; char isRight ; long tempFb, reference ; #ifdef E4ANALYZE if ( b4leaf( b4 ) ) return error4describe( t4->codeBase, e4index, E95406, tfile4alias( t4 ), 0, 0 ) ; #endif if ( b4 == (B4BLOCK *)l4first( &t4->blocks ) ) { if ( b4->nKeys == 0 ) /* empty, so just remove */ { reference = (long)b4key( b4, 0 )->pointer ; b4->changed = 0 ; tfile4shrink( t4, b4->fileBlock ) ; tfile4up( t4 ) ; l4pop(&t4->saved ) ; b4free( b4 ) ; b4 = 0 ; t4->header.root = reference ; } return 0 ; } tfile4up( t4 ) ; parent = (B4BLOCK *)t4->blocks.lastNode ; #ifdef E4ANALYZE if ( b4key( parent, parent->keyOn )->pointer != I4MULTIPLY * b4->fileBlock ) return error4describe( t4->codeBase, e4index, E95406, tfile4alias( t4 ), 0, 0 ) ; #endif if ( parent->keyOn == parent->nKeys ) { tempFb = b4key( parent, parent->keyOn - 1 )->pointer ; isRight = 1 ; } else { tempFb = b4key( parent, parent->keyOn + 1 )->pointer ; isRight = 0 ; } b4temp = b4alloc( t4, 0L ) ; if ( b4temp == 0 ) return error4stack( t4->codeBase, e4memory, E95406 ) ; rc = i4readBlock( &t4->file, tempFb, 0, b4temp ) ; if ( rc < 0 ) { b4free( b4temp ) ; return error4stack( t4->codeBase, rc, E95406 ) ; } rc = tfile4balanceBlock( t4, parent, b4, b4temp, isRight, 2 ) ; b4free( b4temp ) ; if ( rc < 0 ) return error4stack( t4->codeBase, rc, E95406 ) ; return 0 ; } int tfile4removeCurrent( TAG4FILE *t4 ) { B4BLOCK *blockOn ; t4->header.version = (short)( t4->header.oldVersion + 1L ) ; blockOn = (B4BLOCK *)t4->blocks.lastNode ; #ifdef E4ANALYZE if ( b4lastpos( blockOn ) == -b4leaf( blockOn ) ) return error4describe( t4->codeBase, e4index, E85401, tfile4alias( t4 ), 0, 0 ) ; #endif switch( tfile4getReplaceEntry( t4, b4key( blockOn, blockOn->keyOn ), blockOn ) ) { case 0 : /* leaf delete */ b4remove( blockOn ) ; if ( blockOn->nKeys == 0 ) /* last entry deleted! -- remove upward reference */ { if ( tfile4removeRef( t4 ) != 0 ) return -1 ; } else { if ( blockOn->nKeys < t4->header.keysHalf && blockOn->fileBlock != t4->header.root ) /* if not root may have to balance the tree */ return tfile4balance( t4, blockOn, 0 ) ; } break ; case 1 : /* branch delete */ if( tfile4block( t4 )->nKeys == 0 ) /* removed the last key of */ if ( tfile4removeRef( t4 ) != 0 ) return -1 ; break ; default: return error4describe( t4->codeBase, e4index, E81601, tfile4alias( t4 ), 0, 0 ) ; } return 0 ; } #endif /* S4CLIPPER */ #endif /* S4INDEX_OFF */ #endif /* S4OFF_WRITE */ #endif