TOC | PREV | NEXT

*************************** SDBTreeCursor.h ********************************

@interface SDBTreeCursor:NSObject
{
SDBTree *btree;            //implemented as an NSDictionary and NSArray
unsigned int index;            //current position in array
NSData *currentKey;
BOOL currentKeyFoundInBTree;
BOOL validIndex;
BOOL isRegistered;
}

- (id)initWithBTree: (SDBTree *)aBTree ;
- (void) setKeyForIndex:(unsigned int) newIndex;
- (BOOL) writeValue: (void *)aValue andLength: (unsigned int)length;
- (void) writeData:(NSData *)data;
- (void) writeRange:(const void *)aRange ofLength:(unsigned int)aLength atOffset:(unsigned int)anOffset;
- (void) removeValue;
- (NSData *)readData;
- (unsigned int) readValue: (void **)buffer;
- (BOOL) setLast;
- (BOOL) setFirst;
- (BOOL) setNext;
- (BOOL) setPrevious;
- (BOOL) setKey: (NSData *)aKey;
- (void) resetKey;
- (BOOL) setKey:(const void *)aKey andLength:(unsigned int) length;
- (BOOL)setKey:(const void *)aKey andLength:(unsigned int)aLength withHint:(unsigned int)aHint;
- (BOOL) setKey:(NSData *)newKey withHint:(HINT_TYPE)aHint;
- (NSData *) getKey;
- (BOOL) getKeyData:(NSData **)aKey withHint:(HINT_TYPE *)aHint;
- (BOOL) getKey:(const void **)aKey andLength:(unsigned int *) length;
- (BOOL)getKey:(const void **)aKey andLength:(unsigned int *)aLength withHint:(HINT_TYPE *)aHint;
- (BOOL) isMatch;
- (unsigned long) currentPosition;

- (void)invalidateIndex:(NSNotification *)aNotification;
- (SDBTree *)btree;

@end


*************************** SDBTreeCursor.m ********************************

#import "ixCover.h"

@implementation SDBTreeCursor:NSObject

- (void) registerCursor
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(invalidateIndex:) name: SDBTreeIndexDidBecomeInvalid object:btree];
isRegistered = YES;
}

- (void) unregisterCursor
{
isRegistered = NO;
[[NSNotificationCenter defaultCenter]removeObserver:self name:SDBTreeIndexDidBecomeInvalid object:btree];
}

// bug with NSData; data passed to it must be freeable (see Release Notes bug #58127)
- (NSData *) makeData:(const void *)aValue length:(int)length
{
void *freeableData;
freeableData = NSZoneMalloc([self zone],length);
bcopy(aValue,freeableData,length);
return [NSData dataWithBytes:freeableData length:length];
}

- (id) initWithBTree: (SDBTree *)aBTree
{
btree = aBTree;
[self registerCursor];      // so btree can invalidate index
index = NSNotFound;                  // uninitialized;
validIndex = NO;
return self;
}

- (void) setKeyForNewKey:(NSData *)newKey andIndex:(unsigned int) newIndex
{
NSData *oldKey = currentKey;
currentKey = [newKey retain];
[oldKey release];
index = newIndex;
validIndex = YES;
currentKeyFoundInBTree = YES;
}

- (void) setKeyForIndex:(unsigned int) newIndex
{
NSData *newKey = [btree keyAtIndex:newIndex];
[self setKeyForNewKey:newKey andIndex:newIndex];
}

///////////////////////////////////////////////////////////////////////////
//
//      writeValue:andLength:
//      
//      Writes aLength bytes from aValue as the value in the IXBTree
//      at the IXBTreeCursor's position, possibly overwriting a
//      previously stored value. Returns YES if the write resulted
//      in an insertion, and NO if the write overwrote a previously
//      stored value.
//
///////////////////////////////////////////////////////////////////////////


- (BOOL) writeValue: (void *)aValue andLength: (unsigned int)length
{
// key is currentKey
// make NSData out of aValue
NSData *value;

if (length == 0) {
// empty value
value = [NSData data];
} else {
value = [self makeData:aValue length:length];
}
return [btree insertValue:value andKey:currentKey atIndex:index];
}


- (void) writeData:(NSData *)data
{
[btree insertValue:data andKey:currentKey atIndex:index];
}

///////////////////////////////////////////////////////////////////////////
//
//      writeRange
//      
//      Writes aRange over a portion of the value in the IXBTree at the IXBTreeCursor's
//      position. Data starting at anOffset within the IXBTree's value
//      is overwritten for aLength bytes. If the range would extend past
//      the end of the value, the value is enlarged to hold the new amount.
//      
//      If there is no key/value pair at the IXBTreeCursor's position,
//      this method raises IX_NotFoundError.
//
///////////////////////////////////////////////////////////////////////////


- (void) writeRange:(const void *)aRange ofLength:(unsigned int)aLength atOffset:(unsigned int)anOffset
{
NSData *theData;
NSMutableData *changeData;
if (!currentKeyFoundInBTree) {
return;
// raise an exception or something
}
// retrieve the data
theData = [btree valueAtIndex:index];
changeData = [[theData mutableCopyWithZone:[self zone]]autorelease];
[changeData replaceBytesInRange:(NSMakeRange(anOffset,aLength))withBytes:aRange];
[btree insertValue:changeData andKey:currentKey atIndex:index];
}

///////////////////////////////////////////////////////////////////////////
//
//      removeValue
//      
//      Removes the key and the associated value at the IXBTreeCursor's
//      position. This method raises IX_NotFoundError if there is no key
//      at the IXBTreeCursor's position. Returns self.
//
///////////////////////////////////////////////////////////////////////////


- (void) removeValue
{
if (!validIndex) [self resetKey];

if (!currentKeyFoundInBTree) {
      // raise exception
      return;
}
[btree removeValueAtIndex:index withKey:currentKey];
}

- (NSData *)readData
{
if (!validIndex) [self resetKey];

if (!currentKeyFoundInBTree) {
      if (![btree indexBeyondLastKey:index]) {
       // we need to update the key to reflect the current position
       [self setKeyForIndex:index];
      } else {
       // we need to raise an exception
       return nil;
      }
}

return [btree valueAtIndex:index];
}

///////////////////////////////////////////////////////////////////////////
//
//      readValue:
//      
//      Copies the value in the IXBTree at the IXBTreeCursor's position,
//      and returns the length of the value. If there is no key/value pair
//      at the IXBTreeCursor's position, the IXBTreeCursor moves to the
//      next higher position if possible. If the IXBTreeCursor is at
//      the end of the key space, IX_NotFoundError is raised.
//
//      If *aValue is NULL, then a buffer will be allocated from the
//      IXBTreeCursor's zone, and the data will be copied into that buffer.
//      Your code is responsible for freeing the memory allocated.
//      If a non-NULL value is provided in *aValue,
//      then it is assumed to be the address of a valid buffer,
//      and the value stored in the IXBTree will be copied into it.
//      Your code is responsible for making sure the buffer is
//      large enough to hold the value. This is useful for
//      fixed-length values, or values with a known maximum length.
//      
//      Important: Using the address of an uninitialized pointer variable
//      as aValue is incorrect, and will result in data being copied
//      into a random location in the application's address space.
//      Your code should always allocate memory for the pointer variable
//      or set it to NULL before passing its address to this method.
//      
//      This method may be used to determine the size of the buffer needed:
//      invoke it first with NULL as aValue to get the buffer length without
//      copying the data, then a second time with the length to copy the
//      data.
///////////////////////////////////////////////////////////////////////////


- (unsigned int) readValue: (void **)buffer
// if buffer is nil, return length
// if *buffer is nil, allocate space here
{
NSData *tmpData;
void *tmpbuffer;
unsigned int length;

tmpData = [self readData];
length = [tmpData length];
if (buffer == NULL) return length;
if (*buffer == NULL) {
      tmpbuffer = NSZoneMalloc([self zone],length);
      if (!tmpbuffer) ; // raise an exception or something
      [tmpData getBytes:tmpbuffer];
      *buffer = tmpbuffer;
      return length;
}
[tmpData getBytes:*buffer];
return length;
}

///////////////////////////////////////////////////////////////////////////
//
//      setLast
//      
//      If there is at least one value associated with a key,
//      this method positions the cursor at the last element's
//      key and returns YES. Otherwise it returns NO, and any
//      attempt to remove or read a value at the cursor's position
//      will raise IX_ArgumentError.
//
///////////////////////////////////////////////////////////////////////////


- (BOOL) setLast
{
if ([btree isEmpty]) return NO;

[self setKeyForIndex:[btree lastIndex]];
return YES;
}

///////////////////////////////////////////////////////////////////////////
//
//      setFirst
//
//      If there is at least one value associated with a key,
//      this method positions the cursor at the first element's
//      key and returns YES. Otherwise it returns NO, and
//      any attempt to remove or read a value at the cursor's
//      position will raise IX_ArgumentError.
//
///////////////////////////////////////////////////////////////////////////


- (BOOL) setFirst
{
if ([btree isEmpty]) return NO;
[self setKeyForIndex:0];
return YES;
}

///////////////////////////////////////////////////////////////////////////
//
//      setNext
//      
//      Sets the cursor's position to the next key with an
//      associated value. Returns YES if there is a next element,
//      and NO if the cursor is already positioned at the end of
//      the key space. If this method returns NO, then
//      any attempt to remove or read a value at the cursor's
//      position will raise IX_ArgumentError.
//
///////////////////////////////////////////////////////////////////////////


- (BOOL) setNext
{
if ([btree isEmpty]) return NO;
if (index > [btree lastIndex]) return NO;
index = index + 1;
if (index > [btree lastIndex]) return NO;
[self setKeyForIndex:index];
return YES;
}

///////////////////////////////////////////////////////////////////////////
//
//      setPrevious
//      
//      Sets the cursor's position to the previous key with an
//      associated value. Returns YES if there is a previous
//      element, and NO if the cursor was positioned at the beginning
//      of the key space and has moved to a position before the first key.
//      If this method returns NO, then any attempt to read a value
//      will cause the cursor to move to the next key with a value,
//      or raise IX_ArgumentError if the cursor can't move
//      (because it's at the end of the key space).
//
///////////////////////////////////////////////////////////////////////////


- (BOOL) setPrevious
{
if ([btree isEmpty]) return NO;
if (index == 0) return NO;
index = index - 1;
[self setKeyForIndex:index];
return YES;
}

- (BOOL) setToPositionNumber:(unsigned long) position
{
if ([btree isEmpty]) return NO;
if (position > [btree lastIndex]) return NO;
index = position;
[self setKeyForIndex:index];
return YES;
}

- (BOOL) setKey: (NSData *)aKey
{
BOOL exactMatch;
NSData *tmp;
tmp = currentKey;
currentKey = [aKey retain];
[tmp release];
index = [btree indexForKey:aKey exactMatch:&exactMatch];
currentKeyFoundInBTree = exactMatch;
validIndex = YES;
return currentKeyFoundInBTree;
}

// uses currentKey
- (void) resetKey
{
BOOL exactMatch;
index = [btree indexForKey:currentKey exactMatch:&exactMatch];
currentKeyFoundInBTree = exactMatch;
validIndex = YES;
}

///////////////////////////////////////////////////////////////////////////
//
//      setKey:andLength:
//      
//      Sets the current position of the cursor to that specified
//      by aKey and aLength.
//      
//      If a value is associated with aKey, returns YES. Otherwise
//      returns NO. If there is no value with a key before aKey,
//      this method positions the cursor before the first value.
//      If there is no value with a key after aKey,
//      his method positions the cursor beyond the last values.

//      If this method returns NO, then any attempt to write into
//      or remove a value at the cursor's position will raise
//      IX_ArgumentError, and any attempt to read a key or value
//      will cause the cursor to move to the key for the next value
//      before reading the key or value, or raise IX_ArgumentError
//      if the cursor can't move (because it's at the end
//      of the key space).
//      
///////////////////////////////////////////////////////////////////////////


- (BOOL) setKey:(const void *)aKey andLength:(unsigned int) length
{
// make an NSData
NSData *tmpKey = [self makeData:aKey length:length];
return [self setKey:tmpKey];
}


///////////////////////////////////////////////////////////////////////////
//
//      setKey:andLength:withHint:
//      
//      Sets the current position of the cursor to that specified
//      by aKey and aLength.
//      
//      If a value is associated with aKey, returns YES. Otherwise
//      returns NO. If there is no value with a key before aKey,
//      this method positions the cursor before the first value.
//      If there is no value with a key after aKey,
//      his method positions the cursor beyond the last values.

//      If this method returns NO, then any attempt to write into
//      or remove a value at the cursor's position will raise
//      IX_ArgumentError, and any attempt to read a key or value
//      will cause the cursor to move to the key for the next value
//      before reading the key or value, or raise IX_ArgumentError
//      if the cursor can't move (because it's at the end
//      of the key space).
//      
//      This method uses a hint as returned by getKey:andLength:withHint:
//      to find aKey quickly. A hint is like a bookmark; it defines a
//      physical position in the IXBTree, so the IXBTreeCursor can just
//      go there and check if aKey is still there.
//      
///////////////////////////////////////////////////////////////////////////

- (BOOL) setKey:(NSData *)newKey withHint:(HINT_TYPE)aHint
{
NSData *tmpKey = [btree keyAtIndex:aHint];

if ([tmpKey isEqualToData:newKey]) {
[self setKeyForNewKey:newKey andIndex:aHint];
return YES;
} else {
return [self setKey:newKey];
}
}


- (BOOL)setKey:(const void *)aKey andLength:(unsigned int)aLength withHint:(unsigned int)aHint
{
NSData *newKey = [self makeData:aKey length:aLength];
return [self setKey:newKey withHint:aHint];
}


///////////////////////////////////////////////////////////////////////////
//
//      getKey:andLength:
//
//      // kaj 7/4/97 use NSData instead of void **
//      
//      Returns by reference the key defining the cursor's position
//      in its key space, along with the key's length.
//
//      If the cursor is at a key which has a value associated with it,
//      this method returns YES. If the cursor is between two values
//      or before the first one, this method advances the cursor to
//      the key for the next value, returns that key by reference,
//      and returns YES. If the cursor is beyond the last key,
//      this method returns NO, and the contents of aKey and aLength
//      aren't set.
//
///////////////////////////////////////////////////////////////////////////

- (NSData *) getKey
{
BOOL beyondLastKey = [btree indexBeyondLastKey:index];
NSData *tmpKey;
if (beyondLastKey) return nil;

tmpKey = [btree keyAtIndex:index];

// move cursor to key for next value and make that key our current key
if (![tmpKey isEqual:currentKey]) [self setKey:tmpKey];
return currentKey;
}

- (BOOL) getKeyData:(NSData **)aKey withHint:(HINT_TYPE *)aHint
{
*aKey = [self getKey];
if (!*aKey) return NO;

*aHint = index;
return YES;
}


- (BOOL) getKey:(const void **)aKey andLength:(unsigned int *) length
{
NSData *theKey = [self getKey]; // this is also the currentKey

if (!theKey) return NO;

*aKey = (void *)[theKey bytes];
*length = [theKey length];
return YES;
}


///////////////////////////////////////////////////////////////////////////
//
//      getKey:andLength:withHint:
//
//      kaj 7/4/97 replaced with getKey: (NSData **)aKey withHint:
//      
//      Returns by reference the key defining the cursor's position
//      in its key space, along with the key's length and a hint that
//      your code can use to speed up a subsequent key search for
//      the same key using setKey:andLength:withHint:.
//      The hint is guaranteed to remain useful as long as no insertions
//      or removals are performed; however, the more the IXBTree changes,
//      the less useful the hint becomes.
//
//      If the cursor is at a key which has a value associated with it,
//      this method returns YES. If the cursor is between two values
//      or before the first one, this method advances the cursor to
//      the key for the next value, returns that key by reference,
//      and returns YES. If the cursor is beyond the last key,
//      this method returns NO, and the contents of aKey, aLength, and aHint
//      aren't set.
//
///////////////////////////////////////////////////////////////////////////



- (BOOL)getKey:(const void **)aKey andLength:(unsigned int *)aLength withHint:(HINT_TYPE *)aHint
{
BOOL result = [self getKey:aKey andLength:aLength];
*aHint = index;
return result;
}


///////////////////////////////////////////////////////////////////////////
//
//      isMatch
//
//      Returns YES if the cursor is on a key with an associated value,
//      NO if the cursor is between two values or past
//      either end of the set of values.
//
//      If the cursor isn't on a key with a value, then trying
//      to get a key or read a value can cause the cursor to move
//      forward to the next key with a value before reading the key
//      or value, or raise IX_ArgumentError if the cursor can't
//      move (because it's at the end of the key space).
//      Any attempt to write into or remove a nonexistent value will raise
//      IX_ArgumentError.
//
///////////////////////////////////////////////////////////////////////////

- (BOOL) isMatch
{
      return currentKeyFoundInBTree;
}

- (unsigned long) currentPosition
{
return index;
}

- (SDBTree *)btree
{
return btree;
}

- (void)invalidateIndex:(NSNotification *)aNotification
{
if ([aNotification object] == btree)
validIndex = NO;
}

- (void) dealloc
{
[self unregisterCursor];
[currentKey release];
[super dealloc];
}


@end


TOC | PREV | NEXT
Created by Stone Design's Create on 3/12/1998