/* i4remove.c (c)Copyright Sequiter Software Inc., 1990-1994. All rights reserved. */ #include "d4all.h" #ifndef S4UNIX #ifdef __TURBOC__ #pragma hdrstop #endif #endif #ifndef S4OFF_WRITE #ifndef S4INDEX_OFF int S4FUNCTION t4remove( TAG4 *t4, char *ptr, long rec ) { int rc ; #ifdef S4DEBUG if ( t4 == 0 ) e4severe( e4parm, E4_T4REMOVE ) ; #endif rc = t4go( t4, ptr, rec ) ; /* returns -1 if code_base->error_code < 0 */ if ( rc < 0 ) return rc ; if ( rc ) return r4entry ; return t4remove_current( t4 ) ; } int S4FUNCTION t4remove_calc( TAG4 *t4, long rec ) { char *ptr ; #ifdef S4DEBUG if ( t4 == 0 ) e4severe( e4parm, E4_T4REM_CALC ) ; #endif if ( t4->code_base->error_code < 0 ) return -1 ; t4expr_key( t4, &ptr ) ; return t4remove( t4, ptr, rec ) ; } #ifndef N4OTHER #ifdef S4FOX /* remove the (current) branch block */ void S4FUNCTION t4remove_branch( TAG4 *t4, B4BLOCK *block_on ) { long l_node, r_node ; INDEX4 *i4 ; #ifdef S4BYTE_SWAP char *swap_ptr ; S4LONG long_val ; short short_val ; int i ; #endif i4 = t4->index ; if ( block_on == (B4BLOCK *)l4first( &t4->blocks ) ) { /* Root Block, do not delete */ #ifdef S4DEBUG if ( block_on->header.left_node != -1 || block_on->header.right_node != -1 || block_on->header.n_keys != 1 ) e4severe( e4info, E4_INFO_CIB ) ; #endif memset( block_on->data, 0, B4BLOCK_SIZE - sizeof(B4STD_HEADER) - sizeof(B4NODE_HEADER) ) ; if ( !b4leaf( block_on ) ) /* if not a leaf, then reset node_hdr too */ { memset( (void *)&block_on->node_hdr, 0, sizeof(B4NODE_HEADER) ) ; b4leaf_init( block_on ) ; } else block_on->node_hdr.free_space = B4BLOCK_SIZE - sizeof( B4STD_HEADER ) - sizeof( B4NODE_HEADER ) ; block_on->header.n_keys = 0 ; block_on->header.left_node = -1 ; block_on->header.right_node = -1 ; block_on->key_on = -1 ; block_on->built_on = -1 ; block_on->header.node_attribute = 3 ; /* root and leaf */ block_on->changed = 1 ; } else /* This block is to be deleted */ { l_node = block_on->header.left_node ; r_node = block_on->header.right_node ; l4remove( &t4->blocks, block_on ) ; if ( i4shrink( i4, block_on->file_block ) < 0 ) return ; block_on->changed = 0 ; if ( l_node != -1L ) { if ( file4read_all( &i4->file, I4MULTIPLY*l_node, &block_on->header, B4BLOCK_SIZE) < 0 ) return ; #ifdef S4BYTE_SWAP block_on->header.node_attribute = x4reverse_short( (void *)&block_on->header.node_attribute ) ; block_on->header.n_keys = x4reverse_short( (void *)&block_on->header.n_keys ) ; block_on->header.left_node = x4reverse_long( (void *)&block_on->header.left_node ) ; block_on->header.right_node = x4reverse_long( (void *)&block_on->header.right_node ) ; /* if block is a leaf */ if (block_on->header.node_attribute >= 2 ) { block_on->node_hdr.free_space = x4reverse_short( (void *)&block_on->node_hdr.free_space ) ; long_val = x4reverse_long( (void *)&block_on->node_hdr.rec_num_mask[0] ) ; memcpy( (void *)&block_on->node_hdr.rec_num_mask[0], (void *)&long_val, sizeof(S4LONG) ) ; } else /* if block is a branch */ { short_val = block_on->tag->header.key_len + sizeof(S4LONG) ; /* position swap_ptr to end of first key expression */ swap_ptr = (char *) &block_on->node_hdr.free_space + block_on->tag->header.key_len ; /* move through all B4KEY's to swap 'long's */ for ( i = 0 ; i < (int) block_on->header.n_keys ; i++ ) { long_val = x4reverse_long( (void *)swap_ptr ) ; memcpy( swap_ptr, (void *) &long_val, sizeof(S4LONG) ) ; swap_ptr += sizeof(S4LONG) ; long_val = x4reverse_long( (void *)swap_ptr ) ; memcpy( swap_ptr, (void *) &long_val, sizeof(S4LONG) ) ; swap_ptr += short_val ; } } #endif block_on->file_block = l_node ; block_on->header.right_node = r_node ; block_on->changed = 1 ; b4flush( block_on ) ; } if ( r_node != -1L ) { if ( file4read_all( &i4->file, I4MULTIPLY*r_node, &block_on->header, B4BLOCK_SIZE) < 0 ) return ; #ifdef S4BYTE_SWAP block_on->header.node_attribute = x4reverse_short( (void *)&block_on->header.node_attribute ) ; block_on->header.n_keys = x4reverse_short( (void *)&block_on->header.n_keys ) ; block_on->header.left_node = x4reverse_long( (void *)&block_on->header.left_node ) ; block_on->header.right_node = x4reverse_long( (void *)&block_on->header.right_node ) ; /* if block is a leaf */ if (block_on->header.node_attribute >= 2 ) { block_on->node_hdr.free_space = x4reverse_short( (void *)&block_on->node_hdr.free_space ) ; long_val = x4reverse_long( (void *)block_on->node_hdr.rec_num_mask ) ; memcpy( (void *)block_on->node_hdr.rec_num_mask, (void *)&long_val, sizeof(S4LONG) ) ; } else /* if block is a branch */ { short_val = block_on->tag->header.key_len + sizeof(S4LONG) ; /* position swap_ptr to end of first key expression */ swap_ptr = (char *) &block_on->node_hdr.free_space + block_on->tag->header.key_len ; /* move through all B4KEY's to swap 'long's */ for ( i = 0 ; i < (int) block_on->header.n_keys ; i++ ) { long_val = x4reverse_long( (void *)swap_ptr ) ; memcpy( swap_ptr, (void *) &long_val, sizeof(S4LONG) ) ; swap_ptr += sizeof(S4LONG) ; long_val = x4reverse_long( (void *)swap_ptr ) ; memcpy( swap_ptr, (void *) &long_val, sizeof(S4LONG) ) ; swap_ptr += short_val ; } } #endif block_on->file_block = r_node ; block_on->header.left_node = l_node ; block_on->changed = 1 ; b4flush( block_on ) ; } b4free( block_on ) ; } } #endif /* Remove the current key */ int S4FUNCTION t4remove_current( TAG4 *t4 ) { B4BLOCK *block_on ; INDEX4 *i4 ; #ifdef S4FOX void *new_key_info ; long rec ; int bl_removed, less_than_last ; #else int remove_done, update_reqd ; B4BLOCK *block_iterate ; #endif i4 = t4->index ; #ifdef S4FOX i4->tag_index->header.version = i4->version_old+1 ; new_key_info = 0 ; for ( block_on = (B4BLOCK *)t4->blocks.last_node ; block_on ; ) { bl_removed = 0 ; if ( new_key_info == 0 ) /* then delete entry */ { if ( b4lastpos( block_on ) == 0 ) { if ( block_on != (B4BLOCK *)l4first( &t4->blocks ) ) bl_removed = 1 ; t4remove_branch( t4, block_on ) ; block_on = (B4BLOCK *)t4->blocks.last_node ; } else { less_than_last = 0 ; if ( block_on->key_on < b4lastpos( block_on ) ) less_than_last = 1 ; b4remove( block_on ) ; if ( less_than_last ) return 0 ; /* On last entry */ b4go_eof( block_on ) ; block_on->key_on-- ; /* update key_on for goint to last spot */ new_key_info = b4key_key( block_on, block_on->key_on ) ; rec = b4recno( block_on, block_on->key_on ) ; } } else /* Adjust entry */ { b4br_replace( block_on, (char *)new_key_info, rec ) ; if ( block_on->key_on != b4lastpos( block_on ) ) /* not on end key, so exit, else continue */ return 0 ; } if ( !bl_removed ) { block_on = (B4BLOCK *)block_on->link.p ; if ( block_on == (B4BLOCK *)t4->blocks.last_node ) break ; } } #endif #ifdef S4MDX remove_done = 0 ; update_reqd = 0 ; i4->changed = 1 ; t4->changed = 1 ; t4->header.version++ ; block_iterate = (B4BLOCK *)l4last( &t4->blocks ) ; for ( ;; ) { block_on = block_iterate ; if ( block_on == 0 ) break ; block_iterate = (B4BLOCK *)l4prev( &t4->blocks, block_iterate ) ; /* Calculate the previous block while the current block exists. */ if ( !remove_done ) /* either removing or else updating */ { if ( b4lastpos( block_on ) == 0 ) /* delete block */ { if ( block_on == (B4BLOCK *)l4first( &t4->blocks ) ) /* root block, don't delete */ { block_on->changed = 1 ; block_on->key_on = 0 ; memset( (void *)&block_on->n_keys, 0, i4->header.block_rw ) ; if (t4->filter ) /* must modify the filter setting for dBASE IV compatibility */ { file4write(&t4->index->file, t4->header_offset+sizeof(t4->header)+222, (char *)"\0", (int)1 ) ; t4->has_keys = 0 ; #ifdef S4MDX t4->had_keys = 1 ; #endif } return 0 ; } else { l4remove( &t4->blocks, block_on ) ; if ( i4shrink( i4, block_on->file_block) < 0 ) return -1 ; b4free( block_on ) ; } } else /* just remove entry */ { if ( block_iterate && block_on->key_on == b4lastpos( block_on ) && b4lastpos( block_on ) > 0 ) /* save to become last key of the block for parent update */ { memcpy( t4->code_base->saved_key, b4key_key( block_on, block_on->n_keys - 1 - b4leaf(block_on) ), t4->header.key_len ) ; update_reqd = 1 ; } else update_reqd = 0 ; b4remove( block_on ) ; if ( !b4leaf( block_on ) && b4lastpos( block_on ) == 0 ) /* branch with only one entry, so have the entry take place of this block */ { if (block_on == (B4BLOCK *)l4first( &t4->blocks ) ) /* the entry becomes the new root */ { /* first update the tags root */ t4->header.root = b4key( block_on, 0 )->num ; #ifdef S4BYTE_SWAP t4->header.root = x4reverse_long( (void *)&t4->header.root ) ; #endif file4write( &t4->index->file, t4->header_offset, (void *)&t4->header.root, sizeof(t4->header.root) ) ; #ifdef S4BYTE_SWAP t4->header.root = x4reverse_long( (void *)&t4->header.root ) ; #endif update_reqd = 0 ; } else /* remove this branch block */ { block_iterate->changed = 1 ; b4key( block_iterate, block_iterate->key_on )->num = b4key( block_on, 0 )->num ; } l4remove( &t4->blocks, block_on ) ; if ( i4shrink( t4->index, block_on->file_block) < 0 ) return -1 ; block_on->changed = 0 ; b4free( block_on ) ; } else block_on->key_on = b4lastpos( block_on ) ; if ( !update_reqd ) return 0 ; remove_done = 1 ; } } else /* Adjust entry - at most one update will be required in MDX */ { if ( block_on->key_on < b4lastpos( block_on ) ) { block_on->changed = 1 ; memcpy( b4key_key( block_on, block_on->key_on), t4->code_base->saved_key, t4->header.key_len ) ; return 0 ; } } } #endif return 0 ; } #endif /* ifndef N4OTHER */ #ifdef N4OTHER #ifdef S4NDX int S4FUNCTION t4remove_current( TAG4 *t4 ) { B4BLOCK *block_on, *block_iterate ; int remove_done, update_reqd ; #ifdef S4USE_OLD void *new_key_info ; long shrink_br = 0 ; B4BLOCK *shrink_block = 0 ; new_key_info = 0 ; #endif t4->header.version = t4->header.old_version+1 ; remove_done = 0 ; update_reqd = 0 ; t4->header.version++ ; block_iterate = (B4BLOCK *)l4last( &t4->blocks ) ; for ( ;; ) { block_on = block_iterate ; if ( block_on == 0 ) break ; block_iterate = (B4BLOCK *)l4prev( &t4->blocks, block_iterate ) ; /* Calculate the previous block while the current block exists. */ if ( !remove_done ) /* either removing or else updating */ { if ( b4lastpos( block_on ) == 0 ) /* delete block */ { block_on->changed = 1 ; block_on->key_on = 0 ; memset( (void *)&block_on->n_keys, 0, B4BLOCK_SIZE ) ; if ( block_on == (B4BLOCK *)l4first( &t4->blocks ) ) /* root block, don't delete */ return 0 ; else { l4remove( &t4->blocks, block_on ) ; if ( b4flush( block_on ) < 0 ) return -1 ; b4free( block_on ) ; } } else /* just remove entry */ { if ( block_iterate && block_on->key_on == b4lastpos( block_on ) && b4lastpos( block_on ) > 0 ) /* save to become last key of the block for parent update */ { memcpy( t4->code_base->saved_key, b4key_key( block_on, block_on->n_keys - 1 - b4leaf(block_on) ), t4->header.key_len ) ; update_reqd = 1 ; } else update_reqd = 0 ; b4remove( block_on ) ; if ( !b4leaf( block_on ) && b4lastpos( block_on ) == 0 ) /* branch with only one entry, so have the entry take place of this block */ { if (block_on == (B4BLOCK *)l4first( &t4->blocks ) ) /* the entry becomes the new root */ { /* first update the tags root */ t4->header.root = b4key( block_on, 0 )->num ; #ifdef S4BYTE_SWAP t4->header.root = x4reverse_long( (void *)&t4->header.root ) ; #endif file4write( &t4->file, t4->header_offset, (void *)&t4->header.root, sizeof(t4->header.root) ) ; #ifdef S4BYTE_SWAP t4->header.root = x4reverse_long( (void *)&t4->header.root ) ; #endif update_reqd = 0 ; } else /* remove this branch block */ { block_iterate->changed = 1 ; b4key( block_iterate, block_iterate->key_on )->pointer = b4key( block_on, 0 )->pointer ; } l4remove( &t4->blocks, block_on ) ; block_on->changed = 1 ; memset( &block_on->n_keys, 0, B4BLOCK_SIZE ) ; if ( b4flush( block_on ) < 0 ) return -1 ; b4free( block_on ) ; } else block_on->key_on = b4lastpos( block_on ) ; if ( !update_reqd ) return 0 ; remove_done = 1 ; } } else /* Adjust entry - at most one update will be required in MDX */ { if ( block_on->key_on < b4lastpos( block_on ) ) { block_on->changed = 1 ; memcpy( b4key_key( block_on, block_on->key_on), t4->code_base->saved_key, t4->header.key_len ) ; return 0 ; } } } return 0 ; } #else #ifdef S4CLIPPER int S4FUNCTION t4balance_branch( TAG4 *, B4BLOCK * ) ; int S4FUNCTION t4remove_ref( TAG4 *t4 ) { B4KEY_DATA *my_key_data ; B4BLOCK *block_on, *block_up ; long reference ; int i ; int rc ; my_key_data = (B4KEY_DATA *)u4alloc_er( t4->code_base, t4->header.group_len ) ; if ( my_key_data == 0 ) return -1 ; block_on = (B4BLOCK *)t4->blocks.last_node ; /* no longer required... #ifdef S4DEBUG if ( block_on->n_keys ) e4severe( e4info, E4_INFO_DEL ) ; #endif */ t4up( t4 ) ; block_up = (B4BLOCK *)t4->blocks.last_node ; if ( block_up == 0 ) /* root block only, so reference doesn't exist */ { /* reset the block */ short offset = ( t4->header.keys_max + 2 + ( (t4->header.keys_max/2)*2 != t4->header.keys_max ) ) * sizeof(short) ; for ( i = 0 ; i <= t4->header.keys_max ; i++ ) block_on->pointers[i] = (short)( t4->header.group_len * i ) + offset ; return 0 ; } else /* delete the block */ { t4shrink( t4, block_on->file_block ) ; l4pop( &t4->saved ) ; block_on->changed = 0 ; b4free( block_on ) ; } block_on = block_up ; if ( block_on->key_on >= block_on->n_keys ) memcpy( &(my_key_data->num), &( b4key( block_on, block_on->key_on - 1 )->num), sizeof(long) + t4->header.key_len ) ; else memcpy( &(my_key_data->num), &( b4key( block_on, block_on->key_on )->num), sizeof(long) + t4->header.key_len ) ; if ( block_on->n_keys == 1 ) { /* take the branch, and put in place of me, then delete and add me */ if ( block_on->key_on >= block_on->n_keys ) reference = (long) b4key( block_on, 0 )->pointer ; else reference = (long) b4key( block_on, 1 )->pointer ; t4shrink( t4, block_on->file_block ) ; t4up( t4 ) ; l4pop(&t4->saved ) ; block_on->changed = 0 ; b4free( block_on ) ; block_on = (B4BLOCK *)t4->blocks.last_node ; if ( block_on == 0 ) /* just removed root, so make reference root! */ t4->header.root = reference ; else { memcpy( &(b4key( block_on, block_on->key_on )->pointer), &reference, sizeof( reference ) ) ; block_on->changed = 1 ; } } else /* just remove myself, add later */ { b4remove( block_on ) ; if ( block_on->n_keys < t4->header.keys_half && block_on->file_block != t4->header.root ) /* if not root may have to balance the tree */ { if ( t4balance_branch( t4, block_on ) < 0 ) return -1 ; } } /* now add the removed reference back into the index */ rc = t4add( t4, (unsigned char *)my_key_data->value, my_key_data->num ) ; u4free( my_key_data ) ; return rc ; } static int t4balance_block( TAG4 *t4, B4BLOCK *parent, B4BLOCK *b4, B4BLOCK *b4temp, char is_right, char balance_mode ) { B4KEY_DATA *bdata, *bdata2 ; int avg, i ; long temp_fb ; if ( balance_mode == 2 && b4leaf( b4temp ) ) { #ifdef S4DEBUG e4severe( e4info, E4_INFO_CIF ) ; #endif return -1 ; } else { if ( b4temp->n_keys + b4->n_keys < t4->header.keys_max ) /* join the two */ { if ( is_right ) { if ( balance_mode == 2 ) { b4->key_on = 0 ; parent->key_on-- ; bdata = b4key( parent, parent->key_on ) ; b4insert( b4, bdata->value, bdata->num, b4key( b4temp, b4temp->n_keys )->pointer / 512 ) ; for ( i = b4temp->n_keys - 1 ; i >= 0 ; i-- ) { b4->key_on = 0 ; bdata = b4key( b4temp, i ) ; b4insert( b4, bdata->value, bdata->num, bdata->pointer / 512 ) ; } b4temp->changed = 0 ; t4shrink( t4, b4temp->file_block ) ; b4remove( parent ) ; if ( parent->n_keys < t4->header.keys_half ) t4balance_branch( t4, parent ) ; } else { for ( i = 0 ; i < b4->n_keys ; i++ ) { b4go_eof( b4temp ) ; bdata = b4key( b4, i ) ; b4insert( b4temp, bdata->value, bdata->num, 0L ) ; } t4->index->code_base->do_index_verify = 0 ; /* avoid verify errors due to our partial removal */ do { t4down( t4 ) ; } while( !b4leaf( t4block( t4 ) ) ) ; t4->index->code_base->do_index_verify = 1 ; b4flush( b4temp ) ; if ( t4remove_ref( 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 */ b4go_eof( b4 ) ; if ( balance_mode == 2 ) { b4temp->key_on = 0 ; bdata = b4key( parent, parent->key_on ) ; b4insert( b4temp, bdata->value, bdata->num, b4key( b4, b4->n_keys )->pointer / 512 ) ; for ( i = b4->n_keys - 1 ; i >= 0 ; i-- ) { b4temp->key_on = 0 ; bdata = b4key( b4, i ) ; b4insert( b4temp, bdata->value, bdata->num, bdata->pointer / 512 ) ; } t4shrink( t4, b4->file_block ) ; l4pop( &t4->saved ) ; b4->changed = 0 ; b4free( b4 ) ; b4flush( b4temp ) ; b4remove( parent ) ; if ( parent->n_keys < t4->header.keys_half ) t4balance_branch( t4, parent ) ; } else { bdata = b4key( parent, parent->key_on ) ; b4insert( b4, bdata->value, bdata->num, 0L ) ; for ( i = 0 ; i < b4temp->n_keys - 1 ; i++ ) { b4go_eof( b4 ) ; bdata = b4key( b4temp, i ) ; b4insert( b4, bdata->value, bdata->num, 0L ) ; } bdata = b4key( parent, parent->key_on ) ; bdata2 = b4key( b4temp, b4temp->n_keys - 1 ) ; memcpy( bdata->value, bdata2->value, t4->header.key_len ) ; memcpy( &( bdata->num ), &bdata2->num, sizeof( long ) ) ; parent->key_on++ ; parent->changed = 1 ; t4->index->code_base->do_index_verify = 0 ; /* avoid verify errors due to our partial removal */ b4flush( b4 ) ; do { if ( t4down( t4 ) == 1 ) break ; } while( ((B4BLOCK *)t4->blocks.last_node)->file_block != b4temp->file_block ) ; t4->index->code_base->do_index_verify = 1 ; if ( t4remove_ref( 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->n_keys + b4temp->n_keys + 1 ) / 2 ; if ( avg < t4->header.keys_half ) avg = t4->header.keys_half ; if ( is_right ) { b4->key_on = 0 ; parent->key_on-- ; bdata = b4key( parent, parent->key_on ) ; if ( balance_mode == 2 ) { if ( b4->n_keys == 0 ) { temp_fb = b4key( b4, 0 )->pointer ; b4insert( b4, bdata->value, bdata->num, b4key( b4temp, b4temp->n_keys )->pointer / 512 ) ; b4append( b4, temp_fb / 512 ) ; } else b4insert( b4, bdata->value, bdata->num, b4key( b4temp, b4temp->n_keys )->pointer / 512 ) ; } else b4insert( b4, bdata->value, bdata->num, 0L ) ; while ( b4temp->n_keys > avg && b4->n_keys < avg ) { b4->key_on = 0 ; b4temp->key_on = b4temp->n_keys - 1 ; bdata = b4key( b4temp, b4temp->key_on ) ; if ( balance_mode == 2 ) b4insert( b4, bdata->value, bdata->num, bdata->pointer / 512 ) ; else b4insert( b4, bdata->value, bdata->num, 0L ) ; b4remove( b4temp ) ; } b4temp->key_on = b4temp->n_keys - 1 ; } else { b4go_eof( b4 ) ; if ( balance_mode == 2 ) { bdata = b4key( b4, b4->n_keys ) ; bdata2 = b4key( parent, parent->key_on ) ; memcpy( bdata->value, bdata2->value, t4->header.key_len ) ; memcpy( &(bdata->num), &bdata2->num, sizeof( long ) ) ; b4->n_keys++ ; } else { bdata = b4key( parent, parent->key_on ) ; b4insert( b4, bdata->value, bdata->num, 0L ) ; } while ( b4->n_keys - (balance_mode == 2) < avg ) { b4go_eof( b4 ) ; b4temp->key_on = 0 ; bdata = b4key( b4temp, b4temp->key_on ) ; if ( balance_mode == 2 ) b4insert( b4, bdata->value, bdata->num, bdata->pointer / 512 ) ; else b4insert( b4, bdata->value, bdata->num, 0L ) ; b4remove( b4temp ) ; } if ( balance_mode == 2 ) { b4->n_keys-- ; b4->key_on-- ; } b4temp->key_on = 0 ; } /* and add a key back to the parent */ bdata = b4key( parent, parent->key_on ) ; if ( balance_mode == 2 ) { if ( is_right ) bdata2 = b4key( b4temp, b4temp->n_keys - 1 ) ; else bdata2 = b4key( b4, b4->n_keys ) ; memcpy( bdata->value, bdata2->value, t4->header.key_len ) ; memcpy( &(bdata->num), &bdata2->num, sizeof( long ) ) ; if ( is_right ) { b4temp->key_on = b4temp->n_keys ; b4remove( b4temp ) ; } } else { bdata2 = b4key( b4temp, b4temp->key_on ) ; memcpy( bdata->value, bdata2->value, t4->header.key_len ) ; memcpy( &(bdata->num), &bdata2->num, sizeof( long ) ) ; b4remove( b4temp ) ; } parent->changed = 1 ; b4flush( b4temp ) ; } } return 0 ; } static int t4balance_branch_leaf( TAG4 *t4, B4BLOCK *parent, B4BLOCK *branch, B4BLOCK *leaf ) { B4BLOCK *b4temp ; long new_file_block ; B4KEY_DATA *key ; t4->header.version++ ; new_file_block = t4extend( t4 ) ; b4temp = b4alloc( t4, new_file_block ) ; if ( b4temp == 0 ) return -1 ; key = b4key( b4temp, 0 ) ; key->pointer = leaf->file_block * I4MULTIPLY ; b4temp->changed = 1 ; key = b4key( parent, parent->key_on ) ; #ifdef S4DEBUG_DEV if ( key->pointer != leaf->file_block * I4MULTIPLY ) e4severe( e4info, "t4balance_branch_leaf - corruption" ) ; #endif key->pointer = new_file_block * I4MULTIPLY ; parent->changed = 1 ; l4add( &t4->blocks, b4temp ) ; return t4balance_block( t4, parent, b4temp, branch, 1, 2 ) ; } /* if do_full 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 S4FUNCTION t4balance( TAG4 *t4, B4BLOCK *b4, int do_full ) { B4BLOCK *b4temp, *parent ; long temp_fb ; int rc, i ; char is_right, balance_mode ; /* balance_mode = 0 for done, 1 for leaf, and 2 for branch */ if ( !b4leaf( b4 ) ) return 0 ; b4temp = b4alloc( t4, 0L ) ; if ( b4temp == 0 ) return -1 ; if ( do_full ) { t4up_to_root( t4 ) ; balance_mode = 2 ; /* branch */ } else balance_mode = 1 ; /* leaf */ while ( balance_mode != 0 || do_full ) { if ( do_full ) { parent = (B4BLOCK *)t4->blocks.last_node ; parent->key_on = parent->n_keys ; rc = t4down( t4 ) ; if ( rc == 1 ) /* make sure siblings are also leafs */ { t4up( t4 ) ; parent = (B4BLOCK *)t4->blocks.last_node ; if ( parent != 0 ) { if ( parent->key_on > 0 ) { temp_fb = (long)b4key( parent, parent->key_on - 1 )->pointer ; if ( i4read_block( &t4->file, temp_fb, 0, b4temp ) < 0 ) { b4free( b4temp ) ; return -1 ; } if ( b4leaf( b4temp ) == 0 && b4leaf( b4 ) == 1 ) { rc = t4balance_branch_leaf( t4, parent, b4temp, b4 ) ; b4free( b4temp ) ; return rc ; } } } break ; } if ( rc < 0 ) return -1 ; b4 = (B4BLOCK *)t4->blocks.last_node ; b4->key_on = b4->n_keys ; if ( b4->n_keys >= t4->header.keys_half ) continue ; if ( b4leaf( b4 ) ) balance_mode = 1 ; temp_fb = b4key( parent, parent->key_on - 1 )->pointer ; is_right = 1 ; } else { if ( balance_mode == 2 ) /* branch */ { b4 = parent ; while( t4->blocks.last_node != (LINK4 *)b4 ) /* re-align ourselves */ t4up( t4 ) ; } t4up( t4 ) ; parent = (B4BLOCK *)t4->blocks.last_node ; if ( parent == 0 ) /* root block */ { t4down( t4 ) ; parent = (B4BLOCK *)t4->blocks.last_node ; if ( !b4leaf( parent ) && parent->n_keys == 0 ) /* remove */ { temp_fb = (long)b4key( parent, 0 )->pointer ; t4shrink( t4, parent->file_block ) ; t4up( t4 ) ; l4pop( &t4->saved ) ; parent->changed = 0 ; b4free( parent ) ; t4->header.root = temp_fb ; t4down( t4 ) ; } break ; } if ( parent->n_keys == 0 ) /* try to replace parent with ourself */ { #ifdef S4DEBUG e4severe( e4info, E4_INFO_CIF ) ; #endif return -1 ; } if ( b4->n_keys >= t4->header.keys_half ) { balance_mode = 0 ; continue ; } is_right = (parent->key_on == parent->n_keys ) ; if ( is_right ) temp_fb = b4key( parent, parent->key_on - 1 )->pointer ; else temp_fb = b4key( parent, parent->key_on + 1 )->pointer ; } b4temp->file_block = temp_fb ; for ( ;; ) { if ( i4read_block( &t4->file, temp_fb, 0, b4temp ) < 0 ) { b4free( b4temp ) ; return -1 ; } if ( balance_mode == 2 || b4leaf( b4temp ) ) /* if branch mode or leaf mode and leaf block found */ break ; if ( is_right ) temp_fb = b4key( b4temp, b4temp->n_keys )->pointer ; else temp_fb = b4key( b4temp, 0 )->pointer ; } if ( t4balance_block( t4, parent, b4, b4temp, is_right, balance_mode ) < 0 ) return -1 ; if ( do_full ) { if ( b4leaf( b4 ) ) balance_mode = 0 ; } else { if ( parent->n_keys < t4->header.keys_half ) /* do parent as well */ balance_mode = 2 ; else balance_mode = 0 ; } } b4free( b4temp ) ; return 0 ; } int S4FUNCTION t4get_replace_entry( TAG4 *t4, B4KEY_DATA *insert_spot, B4BLOCK *save_block ) { int rc ; B4BLOCK *block_on ; block_on = save_block ; if ( b4leaf( block_on ) ) return 0 ; block_on->key_on = block_on->key_on + 1 ; while ( !b4leaf( block_on ) ) { rc = t4down( t4 ) ; if ( rc < 0 || rc == 2 ) return -1 ; block_on = (B4BLOCK *)t4->blocks.last_node ; } memcpy( &insert_spot->num, &(b4key( block_on, block_on->key_on )->num), sizeof(long) + t4->header.key_len ) ; save_block->changed = 1 ; b4remove( block_on ) ; if ( block_on->n_keys < t4->header.keys_half && block_on->file_block != t4->header.root ) /* if not root may have to balance the tree */ if ( t4balance( t4, block_on, 0 ) < 0 ) return -1 ; return 1 ; } int S4FUNCTION t4balance_branch( TAG4 *t4, B4BLOCK *b4 ) { B4BLOCK *parent, *b4temp ; int is_right ; long temp_fb, reference ; #ifdef S4DEBUG if ( b4leaf( b4 ) ) e4severe( e4info, E4_T4BALANCE_BR ) ; #endif if ( b4 == (B4BLOCK *)l4first( &t4->blocks ) ) { if ( b4->n_keys == 0 ) /* empty, so just remove */ { reference = (long)b4key( b4, 0 )->pointer ; b4->changed = 0 ; t4shrink( t4, b4->file_block ) ; t4up( t4 ) ; l4pop(&t4->saved ) ; t4->header.root = reference ; } return 0 ; } #ifdef S4DEBUG if ( b4->n_keys < 2 ) e4severe( e4info, E4_T4BALANCE_BR ) ; #endif t4up( t4 ) ; parent = (B4BLOCK *)t4->blocks.last_node ; #ifdef S4DEBUG if ( b4key( parent, parent->key_on )->pointer != I4MULTIPLY * b4->file_block ) e4severe( e4info, E4_T4BALANCE_BR ) ; #endif if ( parent->key_on == parent->n_keys ) { temp_fb = b4key( parent, parent->key_on - 1 )->pointer ; is_right = 1 ; } else { temp_fb = b4key( parent, parent->key_on + 1 )->pointer ; is_right = 0 ; } b4temp = b4alloc( t4, 0L ) ; if ( b4temp == 0 ) return -1 ; if ( i4read_block( &t4->file, temp_fb, 0, b4temp ) < 0 ) { b4free( b4temp ) ; return -1 ; } if ( t4balance_block( t4, parent, b4, b4temp, is_right, 2 ) < 0 ) return -1 ; b4free( b4temp ) ; return 0 ; } int S4FUNCTION t4remove_current( TAG4 *t4 ) { B4BLOCK *block_on ; t4->header.version = (short)( t4->header.old_version + 1L ) ; block_on = (B4BLOCK *)t4->blocks.last_node ; #ifdef S4DEBUG if ( b4lastpos( block_on ) == -b4leaf( block_on ) ) e4severe( e4info, E4_INFO_UNE ) ; #endif switch( t4get_replace_entry( t4, b4key( block_on, block_on->key_on ), block_on ) ) { case 0 : /* leaf delete */ b4remove( block_on ) ; if ( block_on->n_keys == 0 ) /* last entry deleted! -- remove upward reference */ { if ( t4remove_ref( t4 ) != 0 ) return -1 ; } else { if ( block_on->n_keys < t4->header.keys_half && block_on->file_block != t4->header.root ) /* if not root may have to balance the tree */ return t4balance( t4, block_on, 0 ) ; } break ; case 1 : /* branch delete */ /* block_on->changed = 1 ;*/ /* mark as changed for all entries */ if( t4block( t4 )->n_keys == 0 ) /* removed the last key of */ if ( t4remove_ref( t4 ) != 0 ) return -1 ; break ; default: e4severe( e4info, E4_INFO_INT ) ; break ; } return 0 ; } #endif /* S4CLIPPER */ #endif /* S4NDX */ #endif /* N4OTHER */ #endif /* S4INDEX_OFF */ #endif /* S4OFF_WRITE */