| // |
| // ACBTree.m |
| // ST4 |
| // |
| // Created by Alan Condit on 4/18/11. |
| // Copyright 2011 Alan Condit. All rights reserved. |
| // |
| |
| #import <Cocoa/Cocoa.h> |
| #import "ACBTree.h" |
| #import "AMutableDictionary.h" |
| #import "ANTLRRuntimeException.h" |
| |
| @class AMutableDictionary; |
| |
| @implementation ACBKey |
| |
| static NSInteger RECNUM = 0; |
| |
| @synthesize recnum; |
| @synthesize key; |
| |
| + (ACBKey *)newKey |
| { |
| return [[ACBKey alloc] init]; |
| } |
| |
| + (ACBKey *)newKeyWithKStr:(NSString *)aKey |
| { |
| return [[ACBKey alloc] initWithKStr:(NSString *)aKey]; |
| } |
| |
| - (id) init |
| { |
| self =[super init]; |
| if ( self != nil ) { |
| recnum = RECNUM++; |
| } |
| return self; |
| } |
| |
| - (id) initWithKStr:(NSString *)aKey |
| { |
| self =[super init]; |
| if ( self != nil ) { |
| NSInteger len; |
| recnum = RECNUM++; |
| key = aKey; |
| len = [aKey length]; |
| if ( len >= BTKeySize ) { |
| len = BTKeySize - 1; |
| } |
| strncpy( kstr, [aKey cStringUsingEncoding:NSASCIIStringEncoding], len); |
| kstr[len] = '\0'; |
| } |
| return self; |
| } |
| |
| @end |
| |
| @implementation ACBTree |
| |
| @synthesize dict; |
| @synthesize lnode; |
| @synthesize rnode; |
| @synthesize keys; |
| @synthesize btNodes; |
| @synthesize lnodeid; |
| @synthesize rnodeid; |
| @synthesize nodeid; |
| @synthesize nodeType; |
| @synthesize numkeys; |
| @synthesize numrecs; |
| @synthesize updtd; |
| @synthesize keylen; |
| @synthesize kidx; |
| |
| + (ACBTree *) newNodeWithDictionary:(AMutableDictionary *)theDict |
| { |
| return [[ACBTree alloc] initWithDictionary:theDict]; |
| } |
| |
| - (id)initWithDictionary:(AMutableDictionary *)theDict |
| { |
| self = [super init]; |
| if (self) { |
| // Initialization code here. |
| dict = theDict; |
| nodeid = theDict.nxt_nodeid++; |
| keys = keyArray; |
| btNodes = btNodeArray; |
| if ( nodeid == 0 ) { |
| numkeys = 0; |
| } |
| } |
| |
| return self; |
| } |
| |
| - (ACBTree *)createnode:(ACBKey *)kp |
| { |
| ACBTree *tmp; |
| |
| tmp = [ACBTree newNodeWithDictionary:dict]; |
| tmp.nodeType = nodeType; |
| tmp.lnode = self; |
| tmp.rnode = self.rnode; |
| self.rnode = tmp; |
| //tmp.btNodes[0] = self; |
| //tmp.keys[0] = kp; |
| tmp.updtd = YES; |
| tmp.numrecs = ((nodeType == LEAF)?1:numrecs); |
| updtd = YES; |
| tmp.numkeys = 1; |
| [tmp retain]; |
| return(tmp); |
| } |
| |
| - (ACBTree *)deletekey:(NSString *)dkey |
| { |
| ACBKey /* *del, */ *dkp; |
| ACBTree *told, *sNode; |
| BOOL mustRelease = NO; |
| |
| if ( [dkey isKindOfClass:[NSString class]] ) { |
| dkp = [ACBKey newKeyWithKStr:dkey]; |
| mustRelease = YES; |
| } |
| else if ( [dkey isKindOfClass:[ACBKey class]] ) |
| dkp = (ACBKey *)dkey; |
| else |
| @throw [ANTLRIllegalArgumentException newException:[NSString stringWithFormat:@"Don't understand this key:\"%@\"", dkey]]; |
| sNode = [self search:dkp.key]; |
| if ( sNode == nil || [sNode searchnode:dkp.key match:YES] == FAILURE ) { |
| if ( mustRelease ) [dkp release]; |
| return(self); |
| } |
| told = dict.root; |
| /* del = */[self internaldelete:dkp]; |
| |
| /* check for shrink at the root */ |
| if ( numkeys == 1 && nodeType != LEAF ) { |
| told = btNodes[0]; |
| told.nodeid = 1; |
| told.updtd = YES; |
| dict.root = told; |
| } |
| #ifdef DONTUSENOMO |
| if (debug == 'd') [self printtree]; |
| #endif |
| if ( mustRelease ) [dkp release]; |
| return(told); |
| } |
| |
| /** insertKey is the insertion entry point |
| * It determines if the key exists in the tree already |
| * it calls internalInsert to determine if the key already exists in the tree, |
| * and returns the node to be updated |
| */ |
| - (ACBTree *)insertkey:(ACBKey *)kp value:(id)value |
| { |
| ACBTree *tnew, *q; |
| NSInteger h, nodeNum; |
| |
| tnew = self; |
| q = [self internalinsert:kp value:value split:&h]; |
| /* check for growth at the root */ |
| if ( q != nil ) { |
| tnew = [[ACBTree newNodeWithDictionary:dict] retain]; |
| tnew.nodeType = BTNODE; |
| nodeNum = tnew.nodeid; |
| tnew.nodeid = 0; |
| self.nodeid = nodeNum; |
| [tnew insert:self.keys[numkeys-1] value:self index:0 split:&h]; |
| [tnew insert:q.keys[q.numkeys-1] value:q index:1 split:&h]; |
| tnew.numrecs = self.numrecs + q.numrecs; |
| tnew.lnodeid = self.nodeid; |
| tnew.rnodeid = self.rnodeid; |
| self.rnodeid = tnew.nodeid; |
| tnew.lnode = self; |
| tnew.rnode = self.rnode; |
| self.rnode = tnew; |
| /* affected by nodeid swap */ |
| // newnode.lnodeid = tnew.btNodes[0].nodeid; |
| } |
| //dict.root = t; |
| //l.reccnt++; |
| return(tnew); |
| } |
| |
| - (ACBTree *)search:(NSString *)kstr |
| { |
| NSInteger i, ret; |
| NSInteger srchlvl = 0; |
| ACBTree *t; |
| |
| t = self; |
| if ( self.numkeys == 0 && self.nodeType == LEAF ) |
| return nil; |
| while (t != nil) { |
| for (i = 0; i < t.numkeys; i++) { |
| ret = [t.keys[i].key compare:kstr]; |
| if ( ret >= 0 ) { |
| if ( t.nodeType == LEAF ) { |
| if ( ret == 0 ) return (t); /* node containing keyentry found */ |
| else return nil; |
| } |
| else { |
| break; |
| } |
| } |
| } |
| srchlvl++; |
| if ( t.nodeType == BTNODE ) t = t.btNodes[i]; |
| else { |
| t = nil; |
| } |
| } |
| return(nil); /* entry not found */ |
| } |
| |
| /** SEARCHNODE |
| * calling parameters -- |
| * BKEY PTR for key to search for. |
| * TYPE for exact match(YES) or position(NO) |
| * returns -- i |
| * i == FAILURE when match required but does not exist. |
| * i == t.numkeys if no existing insertion branch found. |
| * otherwise i == insertion branch. |
| */ |
| - (NSInteger)searchnode:(NSString *)kstr match:(BOOL)match |
| { |
| NSInteger i, ret; |
| for ( i = 0; i < numkeys; i++ ) { |
| ret = [keys[i].key compare:kstr]; |
| if ( ret >= 0 ) { /* key node found */ |
| if ( ret == 0 && match == NO ) { |
| return FAILURE; |
| } |
| else if ( ret > 0 && match == YES ) { |
| return FAILURE; |
| } |
| break; |
| } |
| } |
| if ( i == numkeys && match == YES ) { |
| i = FAILURE; |
| } |
| return(i); |
| } |
| |
| - (ACBKey *)internaldelete:(ACBKey *)dkp |
| { |
| NSInteger i, nkey; |
| __strong ACBKey *del = nil; |
| ACBTree *tsb; |
| NSInteger srchlvl = 0; |
| |
| /* find deletion branch */ |
| if ( self.nodeType != LEAF ) { |
| srchlvl++; |
| /* search for end of tree */ |
| i = [self searchnode:dkp.key match:NO]; |
| del = [btNodes[i] internaldelete:dkp]; |
| srchlvl--; |
| /* if not LEAF propagate back high key */ |
| tsb = btNodes[i]; |
| nkey = tsb.numkeys - 1; |
| } |
| /*** the bottom of the tree has been reached ***/ |
| else { /* set up deletion ptrs */ |
| if ( [self delfrmnode:dkp] == SUCCESS ) { |
| if ( numkeys < BTHNODESIZE+1 ) { |
| del = dkp; |
| } |
| else { |
| del = nil; |
| } |
| dkp.recnum = nodeid; |
| return(del); |
| } |
| } |
| /*** indicate deletion to be done ***/ |
| if ( del != nil ) { |
| /*** the key in "del" has to be deleted from in present node ***/ |
| if ( btNodes[i].numkeys >= BTHNODESIZE+1 ) { |
| /* node does not need balancing */ |
| del = nil; |
| self.keys[i] = tsb.keys[nkey]; |
| } |
| else { /* node requires balancing */ |
| if ( i == 0 ) { |
| [self rotateright:0]; |
| self.btNodes[0] = tsb; |
| } else if ( i < numkeys-1 ) { /* look to the right first */ |
| if ( self.btNodes[i+1].numkeys > BTHNODESIZE+1 ) { /* carry from right */ |
| [self borrowright:i]; |
| } |
| else { /* merge present node with right node */ |
| [self mergenode:i]; |
| } |
| } |
| else { /* look to the left */ |
| if ( i > 0 ) { /* carry or merge with left node */ |
| if ( self.btNodes[i-1].numkeys > BTHNODESIZE+1 ) { /* carry from left */ |
| [self borrowleft:i]; |
| } |
| else { /*** merge present node with left node ***/ |
| i--; |
| [self mergenode:i]; |
| tsb = self.btNodes[i]; |
| } |
| } |
| } |
| self.keys[i] = tsb.keys[nkey]; |
| } |
| } |
| numrecs--; |
| updtd = TRUE; |
| return(del); |
| } |
| |
| /** Search key kp on B-tree with root t; if found increment counter. |
| * otherwise insert an item with key kp in tree. If an ACBKey |
| * emerges to be passed to a lower level, then assign it to kp; |
| * h = "tree t has become higher" |
| */ |
| - (ACBTree *) internalinsert:(ACBKey *)kp value:(id)value split:(NSInteger *)h |
| { |
| /* search key ins on node t^; h = false */ |
| NSInteger i, ret; |
| ACBTree *q, *tmp; |
| |
| for (i = 0; i < numkeys; i++) { |
| ret = [keys[i].key compare:kp.key]; |
| if ( ret >= 0 ) { |
| if ( nodeType == LEAF && ret == 0 ) return (self); /* node containing keyentry found */ |
| break; |
| } |
| } |
| if ( nodeType == LEAF ) { /* key goes in this node */ |
| q = [self insert:kp value:value index:i split:h]; |
| } |
| else { /* nodeType == BTNODE */ |
| /* key is not on this node */ |
| q = [self.btNodes[i] internalinsert:kp value:value split:h]; |
| if ( *h ) { |
| [self insert:kp value:q index:i split:h]; |
| } |
| else { |
| self.numrecs++; |
| } |
| tmp = self.btNodes[numkeys-1]; |
| keys[numkeys-1] = tmp.keys[tmp.numkeys-1]; |
| if ( i != numkeys-1 ) { |
| tmp = self.btNodes[i]; |
| keys[i] = tmp.keys[tmp.numkeys-1]; |
| } |
| updtd = YES; |
| } /* search */ |
| return q; |
| } |
| |
| /** Do the actual insertion or split and insert |
| * insert key to the right of t.keys[hi] |
| */ |
| - (ACBTree *) insert:(ACBKey *)kp value:(id)value index:(NSInteger)hi split:(NSInteger *)h |
| { |
| ACBTree *b; |
| |
| if ( numkeys < BTNODESIZE ) { |
| *h = NO; |
| [self rotateright:hi]; |
| keys[hi] = kp; |
| btNodes[hi] = value; |
| numrecs++; |
| numkeys++; |
| updtd = YES; |
| //[kp retain]; |
| return nil; |
| } |
| else { /* node t is full; split it and assign the emerging ACBKey to olditem */ |
| b = [self splitnode:hi]; |
| if ( hi <= BTHNODESIZE ) { /* insert key in left page */ |
| [self rotateright:hi]; |
| keys[hi] = kp; |
| btNodes[hi] = value; |
| numrecs++; |
| numkeys++; |
| } |
| else { /* insert key in right page */ |
| hi -= BTHNODESIZE; |
| if ( b.rnode == nil ) hi--; |
| [b rotateright:hi]; |
| b.keys[hi] = kp; |
| b.btNodes[hi] = value; |
| b.numrecs++; |
| b.numkeys++; |
| } |
| numkeys = b.numkeys = BTHNODESIZE+1; |
| b.updtd = updtd = YES; |
| } |
| return b; |
| } /* insert */ |
| |
| - (void)borrowleft:(NSInteger)i |
| { |
| ACBTree *t0, *t1; |
| NSInteger nkey; |
| |
| t0 = btNodes[i]; |
| t1 = btNodes[i-1]; |
| nkey = t1.numkeys-1; |
| [t0 insinnode:t1.keys[nkey] value:t1.btNodes[nkey]]; |
| [t1 delfrmnode:t1.keys[nkey]]; |
| nkey--; |
| keys[i-1] = t1.keys[nkey]; |
| keys[i-1].recnum = t1.nodeid; |
| } |
| |
| - (void)borrowright:(NSInteger)i |
| { |
| ACBTree *t0, *t1; |
| NSInteger nkey; |
| |
| t0 = btNodes[i]; |
| t1 = btNodes[i+1]; |
| [t0 insinnode:t1.keys[0] value:t1.btNodes[0]]; |
| [t1 delfrmnode:t1.keys[0]]; |
| nkey = t0.numkeys - 1; |
| keys[i] = t0.keys[nkey]; |
| keys[i].recnum = t0.nodeid; |
| } |
| |
| - (NSInteger)delfrmnode:(ACBKey *)ikp |
| { |
| NSInteger j; |
| |
| j = [self searchnode:ikp.key match:YES]; |
| if (j == FAILURE) { |
| return(FAILURE); |
| } |
| ACBKey *k0 = nil; |
| ACBTree *n0 = nil; |
| if ( self.nodeType == LEAF ) { |
| k0 = self.keys[j]; |
| n0 = self.btNodes[j]; |
| } |
| [self rotateleft:j]; |
| self.numkeys--; |
| numrecs -= ((self.nodeType == LEAF)?1:btNodes[j].numrecs); |
| if ( k0 ) [k0 release]; |
| if ( n0 ) [n0 release]; |
| updtd = TRUE; |
| return(SUCCESS); |
| } |
| |
| - (NSInteger)insinnode:(ACBKey *)ikp value:(id)value |
| { |
| NSInteger j; |
| |
| j = [self searchnode:ikp.key match:NO]; |
| [self rotateright:j]; |
| keys[j] = ikp; |
| btNodes[j] = value; |
| numkeys++; |
| if ( nodeType == LEAF ) { |
| numrecs++; |
| } |
| else { |
| numrecs += btNodes[j].numrecs; |
| } |
| updtd = TRUE; |
| return(j); |
| } |
| |
| - (void)mergenode:(NSInteger)i |
| { |
| ACBTree *t0, *t1, *tr; |
| NSInteger j, k, nkeys; |
| |
| t0 = btNodes[i]; |
| t1 = btNodes[i+1]; |
| /*** move keys and pointers from |
| t1 node to t0 node ***/ |
| for (j=t0.numkeys, k=0; j < BTNODESIZE && k < t1.numkeys; j++, k++) { |
| t0.keys[j] = t1.keys[k]; |
| t0.btNodes[j] = t1.btNodes[k]; |
| t0.numkeys++; |
| } |
| t0.numrecs += t1.numrecs; |
| t0.rnode = t1.rnode; |
| t0.rnodeid = t1.rnodeid; |
| t0.updtd = YES; |
| nkeys = t0.numkeys - 1; |
| keys[i] = t0.keys[nkeys]; /* update key to point to new high key */ |
| [self rotateleft:i+1]; /* copy over the keys and nodes */ |
| |
| t1.nodeType = -1; |
| if (t1.rnodeid != 0xffff && i < numkeys - 2) { |
| tr = btNodes[i+1]; |
| tr.lnodeid = t0.nodeid; |
| tr.lnode = t0; |
| tr.updtd = YES; |
| } |
| self.numkeys--; |
| updtd = YES; |
| } |
| |
| - (ACBTree *)splitnode:(NSInteger)idx |
| { |
| ACBTree *t1; |
| NSInteger j, k; |
| |
| k = (idx <= BTHNODESIZE) ? BTHNODESIZE : BTHNODESIZE+1; |
| /*** create new node ***/ |
| // checknode(l, t, k); |
| t1 = [ACBTree newNodeWithDictionary:dict]; |
| t1.nodeType = nodeType; |
| t1.rnode = self.rnode; |
| self.rnode = t1; |
| t1.lnode = self; |
| self.updtd = t1.updtd = YES; |
| /*** move keys and pointers ***/ |
| NSInteger i = 0; |
| for (j = k; j < BTNODESIZE; j++, i++ ) { |
| t1.keys[i] = keys[j]; |
| t1.btNodes[i] = btNodes[j]; |
| t1.numrecs += ((nodeType == LEAF) ? 1 : btNodes[j].numrecs); |
| numrecs -= ((nodeType == LEAF) ? 1 : btNodes[j].numrecs); |
| keys[j] = nil; |
| btNodes[j] = nil; |
| } |
| t1.numkeys = BTNODESIZE-k; |
| self.numkeys = k; |
| return(t1); |
| } |
| |
| #ifdef DONTUSENOMO |
| freetree(l, t) |
| FIDB *l; |
| ACBTree *t; |
| { |
| ACBTree *tmp; |
| NSInteger i; |
| |
| if (dict.root == nil) return(SUCCESS); |
| if (t.nodeid == 1) { |
| srchlvl = 0; |
| } |
| else srchlvl++; |
| for (i = 0; i < t.numkeys; i++) { |
| tmp = t.btNodes[i]; |
| if (tmp != nil) { |
| if (tmp.nodeType == LEAF) { |
| free(tmp); /* free the leaf */ |
| if (tmp == l.rrnode) { |
| l.rrnode = nil; |
| } |
| t.btNodes[i] = nil; |
| l.chknode.nods_inuse--; |
| /* putpage(l, l.chknode, 0); |
| */ |
| } |
| else { |
| freetree(l, tmp); /* continue up the tree */ |
| srchlvl--; /* decrement the srchlvl on return */ |
| } |
| } |
| } |
| free(t); /* free the node entered with */ |
| if (t == l.rrnode) { |
| l.rrnode = nil; |
| } |
| l.chknode.nods_inuse--; |
| /* putpage(l, l.chknode, 0); |
| */ |
| t = nil; |
| } |
| |
| - (void) notfound:(ACBKey *)kp |
| { |
| /* error routine to perform if entry was expected and not found */ |
| } |
| |
| - (void)printtree:(ACBTree *)t |
| { |
| BYTE *str; |
| NSInteger i, j; |
| NSUInteger *pdate, *ptime; |
| |
| syslst = stdprn; |
| if ( t.nodeid == 1 ) { |
| srchlvl = 0; |
| } |
| else srchlvl++; |
| for (j = 0; j < t.numkeys; j++) { |
| checknode(l, t, j); |
| if ( t.btNodes[j] != nil ) [self printtree:t.btNodes[j]]; |
| } |
| NSLog(@"Nodeid = %d, nodeType = %s, numkeys = %d, numrecs = %d\n", |
| t.nodeid, (t.nodeType == BTNODE)?@"NODE":@"LEAF", t.numkeys, t.numrecs); |
| NSLog(@"Left nodeid = %d, Right nodeid = %d\n", t.lnodeid, t.rnodeid); |
| for (i = 0; i < t.numkeys; i++) { |
| NSLog(@" t.keys[%d] recnum = %d, keyval = %@", |
| i, t.keys[i].recnum, t.keys[i]); |
| str = t.keys[i].kstr; |
| pdate = (NSUInteger *) (str + 6); |
| ptime = (NSUInteger *) (str + 8); |
| NSLog(@" date = %04.4x, time = %04.4x\n", |
| *pdate, *ptime); |
| } |
| } |
| |
| - (BOOL)puttree:(ACBTree *)t |
| { |
| NSInteger i; |
| if (t.nodeType != LEAF) { |
| for (i = 0; i < t.numkeys; i++) { |
| if ( t.btNodes[i] != nil ) puttree(l, t.btNodes[i]); |
| } |
| } |
| if ( t.updtd ) { |
| putnode(l, t, t.nodeid); |
| return(YES); |
| } |
| return(NO); |
| } |
| |
| #endif |
| |
| /** ROTATELEFT -- rotate keys from right to the left |
| * starting at position j |
| */ |
| - (void)rotateleft:(NSInteger)j |
| { |
| while ( j+1 < numkeys ) { |
| keys[j] = keys[j+1]; |
| btNodes[j] = btNodes[j+1]; |
| j++; |
| } |
| } |
| |
| /** ROTATERIGHT -- rotate keys to the right by 1 position |
| * starting at the last key down to position j. |
| */ |
| - (void)rotateright:(NSInteger)j |
| { |
| NSInteger k; |
| |
| for ( k = numkeys; k > j; k-- ) { |
| keys[k] = keys[k-1]; |
| btNodes[k] = btNodes[k-1]; |
| } |
| keys[j] = nil; |
| btNodes[j] = nil; |
| } |
| |
| - (NSInteger) keyWalkLeaves |
| { |
| NSInteger i, idx = 0; |
| NSInteger keycnt; |
| ACBTree *t; |
| |
| if ( self != dict.root ) { |
| return 0; // maybe I need to throw an exception here |
| } |
| t = self; |
| self.dict.data = [[NSMutableData dataWithLength:(numkeys * sizeof(id))] retain]; |
| self.dict.ptrBuffer = [self.dict.data mutableBytes]; |
| while ( t != nil && t.nodeType != LEAF ) { |
| t = t.btNodes[0]; |
| } |
| do { |
| keycnt = t.numkeys; |
| for ( i = 0; i < keycnt; i++ ) { |
| if ( t.btNodes[i] != nil ) { |
| dict.ptrBuffer[idx++] = (id) t.keys[i].key; |
| } |
| } |
| t = t.rnode; |
| } while ( t != nil ); |
| return( idx ); |
| } |
| |
| - (NSInteger) objectWalkLeaves |
| { |
| NSInteger i, idx = 0; |
| NSInteger keycnt; |
| ACBTree *t; |
| |
| if ( self != dict.root ) { |
| return 0; // maybe I need to throw an exception here |
| } |
| t = self; |
| self.dict.data = [[NSMutableData dataWithLength:(numrecs * sizeof(id))] retain]; |
| self.dict.ptrBuffer = [self.dict.data mutableBytes]; |
| while ( t != nil && t.nodeType != LEAF ) { |
| t = t.btNodes[0]; |
| } |
| do { |
| keycnt = t.numkeys; |
| for ( i = 0; i < keycnt; i++ ) { |
| if ( t.btNodes[i] != nil ) { |
| dict.ptrBuffer[idx++] = (id) t.btNodes[i]; |
| } |
| } |
| t = t.rnode; |
| } while ( t != nil ); |
| return( idx ); |
| } |
| |
| - (void)dealloc |
| { |
| #ifdef DEBUG_DEALLOC |
| NSLog( @"called dealloc in ACBTree" ); |
| #endif |
| [super dealloc]; |
| } |
| |
| @end |