2 * Copyright (c) 1999, 2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: Implementation of public interface routines for B-tree manager.
30 Written by: Gordon Sheridan and Bill Bruffey
32 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
38 #include "BTreePrivate.h"
39 //#include "HFSInstrumentation.h"
42 extern Boolean
NodesAreContiguous(SFCB
*fcb
, UInt32 nodeSize
);
43 extern void fplog(FILE *stream
, const char *fmt
, ...);
45 /*-------------------------------------------------------------------------------
48 Function: Copy a BTree key. Sanity check the key length; if it is too large,
49 then set the copy's length to the BTree's maximum key length.
51 Inputs: btcb BTree whose key is being copied
52 srcKey Source key being copied
54 Output: destKey Destination where copy will be stored
57 -------------------------------------------------------------------------------*/
58 static void CopyKey(BTreeControlBlockPtr btcb
, const BTreeKey
*srcKey
, BTreeKey
*destKey
)
60 unsigned keySize
= CalcKeySize(btcb
, srcKey
);
61 unsigned maxKeySize
= MaxKeySize(btcb
);
65 * If the key length is too long (corrupted), then constrain the number
66 * of bytes to copy. Remember that we did this so we can also update
67 * the copy's length field later.
69 if (keySize
> maxKeySize
)
75 CopyMemory(srcKey
, destKey
, keySize
);
78 * If we had to constrain the key size above, then also update the
79 * key length in the copy. This will prevent the caller from dereferencing
80 * part of the key which we never actually copied.
84 if (btcb
->attributes
& kBTBigKeysMask
)
85 destKey
->length16
= btcb
->maxKeyLength
;
87 destKey
->length8
= btcb
->maxKeyLength
;
92 //////////////////////////////////// Globals ////////////////////////////////////
95 /////////////////////////// BTree Module Entry Points ///////////////////////////
98 /*-------------------------------------------------------------------------------
99 Routine: InitBTreeModule - Initialize BTree Module Global(s).
101 Function: Initialize BTree code, if necessary
107 Result: noErr - success
108 != noErr - can't happen
109 -------------------------------------------------------------------------------*/
111 OSStatus
InitBTreeModule(void)
117 /*-------------------------------------------------------------------------------
118 Routine: BTInitialize - Initialize a fork for access as a B*Tree.
120 Function: Write Header node and create any map nodes necessary to map the fork
121 as a B*Tree. If the fork is not large enough for the header node, the
122 FS Agent is called to extend the LEOF. Additional map nodes will be
123 allocated if necessary to represent the size of the fork. This allows
124 the FS Agent to specify the initial size of B*Tree files.
127 Input: pathPtr - pointer to path control block
128 maxKeyLength - maximum length that will be used for any key in this B*Tree
129 nodeSize - node size for B*Tree (must be 2^n, 9 >= n >= 15)
130 btreeType - type of B*Tree
131 keyDescPtr - pointer to key descriptor (optional if key compare proc is used)
135 Result: noErr - success
136 paramErr - mandatory parameter was missing
137 E_NoGetBlockProc - FS Agent CB has no GetNodeProcPtr
138 E_NoReleaseBlockProc - FS Agent CB has no ReleaseNodeProcPtr
139 E_NoSetEndOfForkProc - FS Agent CB has no SetEndOfForkProcPtr
140 E_NoSetBlockSizeProc - FS Agent CB has no SetBlockSizeProcPtr
141 fsBTrFileAlreadyOpenErr - fork is already open as a B*Tree
142 fsBTInvalidKeyLengthErr - maximum key length is out of range
143 E_BadNodeType - node size is an illegal value
144 fsBTUnknownVersionErr - the B*Tree type is unknown by this module
145 memFullErr - not enough memory to initialize B*Tree
147 -------------------------------------------------------------------------------*/
149 OSStatus
BTInitialize (FCB
*filePtr
,
153 KeyDescriptorPtr keyDescPtr
)
156 FSForkControlBlockPtr forkPtr
;
157 BTreeControlBlockPtr btreePtr
;
158 BlockDescriptor headerNode
;
161 FSSize minEOF
, maxEOF
;
162 SetEndOfForkProcPtr setEndOfForkProc
;
163 SetBlockSizeProcPtr setBlockSizeProc
;
165 ////////////////////// Preliminary Error Checking ///////////////////////////
167 headerNode
.buffer
= nil
;
169 if (pathPtr
== nil
) return paramErr
;
171 setEndOfForkProc
= pathPtr
->agentPtr
->agent
.setEndOfForkProc
;
172 setBlockSizeProc
= pathPtr
->agentPtr
->agent
.setBlockSizeProc
;
174 if (pathPtr
->agentPtr
->agent
.getBlockProc
== nil
) return E_NoGetBlockProc
;
175 if (pathPtr
->agentPtr
->agent
.releaseBlockProc
== nil
) return E_NoReleaseBlockProc
;
176 if (setEndOfForkProc
== nil
) return E_NoSetEndOfForkProc
;
177 if (setBlockSizeProc
== nil
) return E_NoSetBlockSizeProc
;
179 forkPtr
= pathPtr
->path
.forkPtr
;
181 if (forkPtr
->fork
.btreePtr
!= nil
) return fsBTrFileAlreadyOpenErr
;
183 if ((maxKeyLength
== 0) ||
184 (maxKeyLength
> kMaxKeyLength
)) return fsBTInvalidKeyLengthErr
;
186 if ( M_IsEven (maxKeyLength
)) ++maxKeyLength
; // len byte + even bytes + pad byte
188 switch (nodeSize
) // node size == 512*2^n
197 default: return E_BadNodeType
;
204 case kReservedBTreeType
: break;
206 default: return fsBTUnknownVersionErr
; //¥¥ right?
210 //////////////////////// Allocate Control Block /////////////////////////////
212 M_RESIDENT_ALLOCATE_FIXED_CLEAR( &btreePtr
, sizeof( BTreeControlBlock
), kFSBTreeControlBlockType
);
219 btreePtr
->version
= kBTreeVersion
; //¥¥ what is the version?
220 btreePtr
->reserved1
= 0;
222 btreePtr
->attributes
= 0;
223 btreePtr
->forkPtr
= forkPtr
;
224 btreePtr
->keyCompareProc
= nil
;
225 btreePtr
->keyDescPtr
= keyDescPtr
;
226 btreePtr
->btreeType
= btreeType
;
227 btreePtr
->treeDepth
= 0;
228 btreePtr
->rootNode
= 0;
229 btreePtr
->leafRecords
= 0;
230 btreePtr
->firstLeafNode
= 0;
231 btreePtr
->lastLeafNode
= 0;
232 btreePtr
->nodeSize
= nodeSize
;
233 btreePtr
->maxKeyLength
= maxKeyLength
;
234 btreePtr
->totalNodes
= 1; // so ExtendBTree adds maps nodes properly
235 btreePtr
->freeNodes
= 0;
236 btreePtr
->writeCount
= 1; // <CS10>, for BTree scanner
238 // set block size = nodeSize
239 err
= setBlockSizeProc (forkPtr
, nodeSize
);
242 ////////////////////////////// Check LEOF ///////////////////////////////////
245 if ( forkPtr
->fork
.logicalEOF
< minEOF
)
247 // allocate more space if necessary
250 err
= setEndOfForkProc (forkPtr
, minEOF
, maxEOF
);
255 //////////////////////// Initialize Header Node /////////////////////////////
257 err
= GetNewNode (btreePtr
, kHeaderNodeNum
, &headerNode
);
260 header
= headerNode
.buffer
;
262 header
->node
.type
= kHeaderNode
;
263 header
->node
.numRecords
= 3; // header rec, key desc, map rec
265 header
->nodeSize
= nodeSize
;
266 header
->maxKeyLength
= maxKeyLength
;
267 header
->btreeType
= btreeType
;
268 header
->totalNodes
= btreePtr
->totalNodes
;
269 header
->freeNodes
= btreePtr
->totalNodes
- 1;
270 // ignore header->clumpSize; //¥¥ rename this field?
272 // mark header node allocated in map record
273 pos
= ((Ptr
)headerNode
.buffer
) + kHeaderMapRecOffset
;
276 // set node offsets ( nodeSize-8, F8, 78, 0E)
277 pos
= ((Ptr
)headerNode
.buffer
) + nodeSize
;
278 pos
-= 2; *((UInt16
*)pos
) = kHeaderRecOffset
;
279 pos
-= 2; *((UInt16
*)pos
) = kKeyDescRecOffset
;
280 pos
-= 2; *((UInt16
*)pos
) = kHeaderMapRecOffset
;
281 pos
-= 2; *((UInt16
*)pos
) = nodeSize
- 8;
284 ///////////////////// Copy Key Descriptor To Header /////////////////////////
285 #if SupportsKeyDescriptors
286 if (keyDescPtr
!= nil
)
288 err
= CheckKeyDescriptor (keyDescPtr
, maxKeyLength
);
291 // copy to header node
292 pos
= ((Ptr
)headerNode
.buffer
) + kKeyDescRecOffset
;
293 CopyMemory (keyDescPtr
, pos
, keyDescPtr
->length
+ 1);
298 err
= UpdateNode (btreePtr
, &headerNode
);
302 ////////////////////////// Allocate Map Nodes ///////////////////////////////
304 err
= ExtendBTree (btreePtr
, forkPtr
->fork
.logicalEOF
.lo
/ nodeSize
); // sets totalNodes
308 ////////////////////////////// Close BTree //////////////////////////////////
310 err
= UpdateHeader (btreePtr
);
313 pathPtr
->path
.forkPtr
->fork
.btreePtr
= nil
;
314 M_RESIDENT_DEALLOCATE_FIXED( btreePtr
, sizeof( BTreeControlBlock
), kFSBTreeControlBlockType
);
319 /////////////////////// Error - Clean up and Exit ///////////////////////////
323 (void) ReleaseNode (btreePtr
, &headerNode
);
325 M_RESIDENT_DEALLOCATE_FIXED( btreePtr
, sizeof( BTreeControlBlock
), kFSBTreeControlBlockType
);
332 /*-------------------------------------------------------------------------------
333 Routine: BTOpenPath - Open a file for access as a B*Tree.
335 Function: Create BTree control block for a file, if necessary. Validates the
336 file to be sure it looks like a BTree file.
339 Input: filePtr - pointer to file to open as a B-tree
340 keyCompareProc - pointer to client's KeyCompare function
341 getBlockProc - pointer to client's GetBlock function
342 releaseBlockProc - pointer to client's ReleaseBlock function
343 setEndOfForkProc - pointer to client's SetEOF function
345 Result: noErr - success
346 paramErr - required ptr was nil
350 -------------------------------------------------------------------------------*/
352 OSStatus
BTOpenPath (SFCB
*filePtr
,
353 KeyCompareProcPtr keyCompareProc
,
354 GetBlockProcPtr getBlockProc
,
355 ReleaseBlockProcPtr releaseBlockProc
,
356 SetEndOfForkProcPtr setEndOfForkProc
,
357 SetBlockSizeProcPtr setBlockSizeProc
)
360 BTreeControlBlockPtr btreePtr
;
364 // LogStartTime(kTraceOpenBTree);
366 ////////////////////// Preliminary Error Checking ///////////////////////////
368 if ( filePtr
== nil
||
369 getBlockProc
== nil
||
370 releaseBlockProc
== nil
||
371 setEndOfForkProc
== nil
||
372 setBlockSizeProc
== nil
)
377 if ( filePtr
->fcbBtree
!= nil
) // already has a BTreeCB
380 // is file large enough to contain header node?
381 if ( filePtr
->fcbLogicalSize
< kMinNodeSize
)
382 return fsBTInvalidFileErr
; //¥¥ or E_BadHeader?
385 //////////////////////// Allocate Control Block /////////////////////////////
387 btreePtr
= (BTreeControlBlock
*) AllocateClearMemory( sizeof( BTreeControlBlock
) );
390 Panic ("\pBTOpen: no memory for btreePtr.");
394 btreePtr
->getBlockProc
= getBlockProc
;
395 btreePtr
->releaseBlockProc
= releaseBlockProc
;
396 btreePtr
->setEndOfForkProc
= setEndOfForkProc
;
397 btreePtr
->keyCompareProc
= keyCompareProc
;
399 /////////////////////////// Read Header Node ////////////////////////////////
401 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
403 btreePtr
->fcbPtr
= filePtr
;
404 filePtr
->fcbBtree
= (void *) btreePtr
; // attach btree cb to file
406 // it is now safe to call M_ExitOnError (err)
408 err
= setBlockSizeProc (btreePtr
->fcbPtr
, kMinNodeSize
);
412 err
= getBlockProc (btreePtr
->fcbPtr
,
417 PanicIf (err
!= noErr
, "\pBTOpen: getNodeProc returned error getting header node.");
420 header
= (BTHeaderRec
*) (nodeRec
.buffer
+ sizeof(BTNodeDescriptor
));
423 ///////////////////////////// verify header /////////////////////////////////
425 err
= VerifyHeader (filePtr
, header
);
429 ///////////////////// Initalize fields from header //////////////////////////
431 PanicIf ( (filePtr
->fcbVolume
->vcbSignature
!= 0x4244) && (btreePtr
->nodeSize
== 512), "\p BTOpenPath: wrong node size for HFS+ volume!");
433 btreePtr
->treeDepth
= header
->treeDepth
;
434 btreePtr
->rootNode
= header
->rootNode
;
435 btreePtr
->leafRecords
= header
->leafRecords
;
436 btreePtr
->firstLeafNode
= header
->firstLeafNode
;
437 btreePtr
->lastLeafNode
= header
->lastLeafNode
;
438 btreePtr
->nodeSize
= header
->nodeSize
;
439 btreePtr
->maxKeyLength
= header
->maxKeyLength
;
440 btreePtr
->totalNodes
= header
->totalNodes
;
441 btreePtr
->freeNodes
= header
->freeNodes
;
442 // ignore header->clumpSize; //¥¥ rename this field?
443 btreePtr
->btreeType
= header
->btreeType
;
445 btreePtr
->attributes
= header
->attributes
;
447 if ( btreePtr
->maxKeyLength
> 40 )
448 btreePtr
->attributes
|= (kBTBigKeysMask
+ kBTVariableIndexKeysMask
); //¥¥ we need a way to save these attributes
450 /////////////////////// Initialize dynamic fields ///////////////////////////
452 btreePtr
->version
= kBTreeVersion
;
454 btreePtr
->writeCount
= 1; // <CS10>, for BTree scanner
456 btreePtr
->numGetNodes
= 1; // for earlier call to getNodeProc
458 /////////////////////////// Check Header Node ///////////////////////////////
460 //¥¥ set kBadClose attribute bit, and UpdateNode
463 * If the actual node size is different than the amount we read,
464 * then release and trash this block, and re-read with the correct
467 if ( btreePtr
->nodeSize
!= kMinNodeSize
)
469 err
= setBlockSizeProc (btreePtr
->fcbPtr
, btreePtr
->nodeSize
);
474 * Need to use kTrashBlock option to force the
475 * buffer cache to re-read the entire node
477 err
= releaseBlockProc(btreePtr
->fcbPtr
, &nodeRec
, kTrashBlock
);
479 err
= ReleaseNode (btreePtr
, &nodeRec
);
482 err
= GetNode (btreePtr
, kHeaderNodeNum
, &nodeRec
); // calls CheckNode...
486 //¥¥ total nodes * node size <= LEOF?
489 ////////////////////////// Get Key Descriptor ///////////////////////////////
490 #if SupportsKeyDescriptors
491 if ( keyCompareProc
== nil
) // if no key compare proc then get key descriptor
493 err
= GetKeyDescriptor (btreePtr
, nodeRec
.buffer
); //¥¥ it should check amount of memory allocated...
496 err
= CheckKeyDescriptor (btreePtr
->keyDescPtr
, btreePtr
->maxKeyLength
);
503 btreePtr
->keyDescPtr
= nil
; // clear it so we don't dispose garbage later
506 err
= ReleaseNode (btreePtr
, &nodeRec
);
512 * Under Mac OS, b-tree nodes can be non-contiguous on disk when the
513 * allocation block size is smaller than the b-tree node size.
515 if ( !NodesAreContiguous(filePtr
, btreePtr
->nodeSize
) ) {
516 if (debug
) fplog(stderr
, "Nodes are not contiguous -- this is fatal\n");
517 return fsBTInvalidNodeErr
;
521 //////////////////////////////// Success ////////////////////////////////////
523 //¥¥ align LEOF to multiple of node size? - just on close
525 // LogEndTime(kTraceOpenBTree, noErr);
530 /////////////////////// Error - Clean up and Exit ///////////////////////////
534 filePtr
->fcbBtree
= nil
;
535 (void) ReleaseNode (btreePtr
, &nodeRec
);
536 DisposeMemory( btreePtr
);
538 // LogEndTime(kTraceOpenBTree, err);
545 /*-------------------------------------------------------------------------------
546 Routine: BTClosePath - Flush BTree Header and Deallocate Memory for BTree.
548 Function: Flush the BTreeControlBlock fields to header node, and delete BTree control
549 block and key descriptor associated with the file if filePtr is last
550 path of type kBTreeType ('btre').
553 Input: filePtr - pointer to file to delete BTree control block for.
555 Result: noErr - success
558 -------------------------------------------------------------------------------*/
560 OSStatus
BTClosePath (SFCB
*filePtr
)
563 BTreeControlBlockPtr btreePtr
;
565 FSPathControlBlockPtr tempPath
;
566 Boolean otherBTreePathsOpen
;
569 // LogStartTime(kTraceCloseBTree);
571 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
574 return fsBTInvalidFileErr
;
576 ////////////////////// Check for other BTree Paths //////////////////////////
579 //¥¥ Need replacement field for pathType
580 otherBTreePathsOpen
= false;
581 tempPath
= forkPtr
->fork
.pathList
.head
;
582 while ( (tempPath
!= (FSPathControlBlockPtr
) &forkPtr
->fork
.pathList
) &&
583 (otherBTreePathsOpen
== false) )
585 if ((tempPath
!= pathPtr
) && (tempPath
->path
.pathType
== kBTreeType
))
587 otherBTreePathsOpen
= true;
588 break; // done with loop check
591 tempPath
= tempPath
->next
;
594 ////////////////////////// Update Header Node ///////////////////////////////
597 if (otherBTreePathsOpen
== true)
599 err
= UpdateHeader (btreePtr
); // update header even if we aren't closing
600 return err
; // we only clean up after the last user...
604 btreePtr
->attributes
&= ~kBTBadCloseMask
; // clear "bad close" attribute bit
605 err
= UpdateHeader (btreePtr
);
608 #if SupportsKeyDescriptors
609 if (btreePtr
->keyDescPtr
!= nil
) // deallocate keyDescriptor, if any
611 DisposeMemory( btreePtr
->keyDescPtr
);
615 DisposeMemory( btreePtr
);
616 filePtr
->fcbBtree
= nil
;
618 // LogEndTime(kTraceCloseBTree, noErr);
622 /////////////////////// Error - Clean Up and Exit ///////////////////////////
626 // LogEndTime(kTraceCloseBTree, err);
633 /*-------------------------------------------------------------------------------
634 Routine: BTSearchRecord - Search BTree for a record with a matching key.
636 Function: Search for position in B*Tree indicated by searchKey. If a valid node hint
637 is provided, it will be searched first, then SearchTree will be called.
638 If a BTreeIterator is provided, it will be set to the position found as
639 a result of the search. If a record exists at that position, and a BufferDescriptor
640 is supplied, the record will be copied to the buffer (as much as will fit),
641 and recordLen will be set to the length of the record.
643 If an error other than fsBTRecordNotFoundErr occurs, the BTreeIterator, if any,
644 is invalidated, and recordLen is set to 0.
647 Input: pathPtr - pointer to path for BTree file.
648 searchKey - pointer to search key to match.
649 hintPtr - pointer to hint (may be nil)
651 Output: record - pointer to BufferDescriptor containing record
652 recordLen - length of data at recordPtr
653 iterator - pointer to BTreeIterator indicating position result of search
655 Result: noErr - success, record contains copy of record found
656 fsBTRecordNotFoundErr - record was not found, no data copied
657 fsBTInvalidFileErr - no BTreeControlBlock is allocated for the fork
658 fsBTInvalidKeyLengthErr -
660 -------------------------------------------------------------------------------*/
662 OSStatus
BTSearchRecord (SFCB
*filePtr
,
663 BTreeIterator
*searchIterator
,
664 UInt32 heuristicHint
,
665 FSBufferDescriptor
*record
,
667 BTreeIterator
*resultIterator
)
670 BTreeControlBlockPtr btreePtr
;
671 TreePathTable treePathTable
;
673 BlockDescriptor node
;
682 // LogStartTime(kTraceSearchBTree);
684 if (filePtr
== nil
) return paramErr
;
685 if (searchIterator
== nil
) return paramErr
;
687 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
688 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
690 #if SupportsKeyDescriptors
691 if (btreePtr
->keyCompareProc
== nil
) // CheckKey if we using Key Descriptor
693 err
= CheckKey (&searchIterator
->key
, btreePtr
->keyDescPtr
, btreePtr
->maxKeyLength
);
700 ////////////////////////////// Take A Hint //////////////////////////////////
702 err
= IsItAHint (btreePtr
, searchIterator
, &validHint
);
707 nodeNum
= searchIterator
->hint
.nodeNum
;
709 err
= GetNode (btreePtr
, nodeNum
, &node
);
712 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
713 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
715 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
717 //¥¥ if !foundRecord, we could still skip tree search if ( 0 < index < numRecords )
720 if (foundRecord
== false)
722 err
= ReleaseNode (btreePtr
, &node
);
727 ++btreePtr
->numValidHints
;
731 if( foundRecord
== false )
732 (void) BTInvalidateHint( searchIterator
);
735 ////////////////////////////// Try the heuristicHint //////////////////////////////////
737 if ( (foundRecord
== false) && (heuristicHint
!= kInvalidMRUCacheKey
) && (nodeNum
!= heuristicHint
) )
739 // LogStartTime(kHeuristicHint);
740 nodeNum
= heuristicHint
;
742 err
= GetNode (btreePtr
, nodeNum
, &node
);
745 if ( ((BTNodeDescriptor
*) node
.buffer
)->kind
== kBTLeafNode
&&
746 ((BTNodeDescriptor
*) node
.buffer
)->numRecords
> 0 )
748 foundRecord
= SearchNode (btreePtr
, node
.buffer
, &searchIterator
->key
, &index
);
751 if (foundRecord
== false)
753 err
= ReleaseNode (btreePtr
, &node
);
757 // LogEndTime(kHeuristicHint, (foundRecord == false));
760 //////////////////////////// Search The Tree ////////////////////////////////
762 if (foundRecord
== false)
764 err
= SearchTree ( btreePtr
, &searchIterator
->key
, treePathTable
, &nodeNum
, &node
, &index
);
767 case noErr
: foundRecord
= true; break;
768 case fsBTRecordNotFoundErr
: break;
769 default: goto ErrorExit
;
774 //////////////////////////// Get the Record /////////////////////////////////
776 if (foundRecord
== true)
778 //¥¥ Should check for errors! Or BlockMove could choke on recordPtr!!!
779 GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
781 if (recordLen
!= nil
) *recordLen
= len
;
785 ByteCount recordSize
;
787 recordSize
= record
->itemCount
* record
->itemSize
;
789 PanicIf(len
> recordSize
, "\pBTSearchRecord: truncating record!");
791 if (len
> recordSize
) len
= recordSize
;
793 CopyMemory (recordPtr
, record
->bufferAddress
, len
);
798 /////////////////////// Success - Update Iterator ///////////////////////////
800 if (resultIterator
!= nil
)
802 resultIterator
->hint
.writeCount
= btreePtr
->writeCount
;
803 resultIterator
->hint
.nodeNum
= nodeNum
;
804 resultIterator
->hint
.index
= index
;
805 resultIterator
->hint
.reserved1
= 0;
806 resultIterator
->hint
.reserved2
= 0;
808 resultIterator
->version
= 0;
809 resultIterator
->reserved
= 0;
811 // copy the key in the BTree when found rather than searchIterator->key to get proper case/diacriticals
812 if (foundRecord
== true)
813 CopyKey(btreePtr
, keyPtr
, &resultIterator
->key
);
815 CopyKey(btreePtr
, &searchIterator
->key
, &resultIterator
->key
);
818 err
= ReleaseNode (btreePtr
, &node
);
821 // LogEndTime(kTraceSearchBTree, (foundRecord == false));
823 if (foundRecord
== false) return fsBTRecordNotFoundErr
;
827 /////////////////////// Error - Clean Up and Exit ///////////////////////////
831 if (recordLen
!= nil
)
834 if (resultIterator
!= nil
)
836 resultIterator
->hint
.writeCount
= 0;
837 resultIterator
->hint
.nodeNum
= 0;
838 resultIterator
->hint
.index
= 0;
839 resultIterator
->hint
.reserved1
= 0;
840 resultIterator
->hint
.reserved2
= 0;
842 resultIterator
->version
= 0;
843 resultIterator
->reserved
= 0;
844 resultIterator
->key
.length16
= 0; // zero out two bytes to cover both types of keys
847 if ( err
== fsBTEmptyErr
)
848 err
= fsBTRecordNotFoundErr
;
850 // LogEndTime(kTraceSearchBTree, err);
857 /*-------------------------------------------------------------------------------
858 Routine: BTIterateRecord - Find the first, next, previous, or last record.
860 Function: Find the first, next, previous, or last record in the BTree
862 Input: pathPtr - pointer to path iterate records for.
863 operation - iteration operation (first,next,prev,last)
864 iterator - pointer to iterator indicating start position
866 Output: iterator - iterator is updated to indicate new position
867 newKeyPtr - pointer to buffer to copy key found by iteration
868 record - pointer to buffer to copy record found by iteration
869 recordLen - length of record
871 Result: noErr - success
873 -------------------------------------------------------------------------------*/
875 OSStatus
BTIterateRecord (SFCB
*filePtr
,
876 BTreeIterationOperation operation
,
877 BTreeIterator
*iterator
,
878 FSBufferDescriptor
*record
,
882 BTreeControlBlockPtr btreePtr
;
890 BlockDescriptor left
, node
, right
;
894 // LogStartTime(kTraceGetBTreeRecord);
896 ////////////////////////// Priliminary Checks ///////////////////////////////
908 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
911 return fsBTInvalidFileErr
; //¥¥ handle properly
914 if ((operation
!= kBTreeFirstRecord
) &&
915 (operation
!= kBTreeNextRecord
) &&
916 (operation
!= kBTreeCurrentRecord
) &&
917 (operation
!= kBTreePrevRecord
) &&
918 (operation
!= kBTreeLastRecord
))
920 err
= fsInvalidIterationMovmentErr
;
924 /////////////////////// Find First or Last Record ///////////////////////////
926 if ((operation
== kBTreeFirstRecord
) || (operation
== kBTreeLastRecord
))
928 if (operation
== kBTreeFirstRecord
) nodeNum
= btreePtr
->firstLeafNode
;
929 else nodeNum
= btreePtr
->lastLeafNode
;
937 err
= GetNode (btreePtr
, nodeNum
, &node
);
940 if ( ((NodeDescPtr
) node
.buffer
)->kind
!= kBTLeafNode
||
941 ((NodeDescPtr
) node
.buffer
)->numRecords
<= 0 )
943 err
= ReleaseNode (btreePtr
, &node
);
946 if (debug
) fprintf(stderr
, "%s(%d): returning fsBTInvalidNodeErr\n", __FUNCTION__
, __LINE__
);
947 err
= fsBTInvalidNodeErr
;
951 if (operation
== kBTreeFirstRecord
) index
= 0;
952 else index
= ((BTNodeDescriptor
*) node
.buffer
)->numRecords
- 1;
954 goto CopyData
; //¥¥ is there a cleaner way?
958 //////////////////////// Find Iterator Position /////////////////////////////
960 err
= FindIteratorPosition (btreePtr
, iterator
,
961 &left
, &node
, &right
, &nodeNum
, &index
, &foundRecord
);
965 ///////////////////// Find Next Or Previous Record //////////////////////////
967 if (operation
== kBTreePrevRecord
)
975 if (left
.buffer
== nil
)
977 nodeNum
= ((NodeDescPtr
) node
.buffer
)->bLink
;
980 err
= GetNode (btreePtr
, nodeNum
, &left
);
983 err
= fsBTStartOfIterationErr
;
987 // Before we stomp on "right", we'd better release it if needed
988 if (right
.buffer
!= nil
) {
989 err
= ReleaseNode(btreePtr
, &right
);
995 index
= ((NodeDescPtr
) node
.buffer
)->numRecords
-1;
998 else if (operation
== kBTreeNextRecord
)
1000 if ((foundRecord
!= true) &&
1001 (((NodeDescPtr
) node
.buffer
)->fLink
== 0) &&
1002 (index
== ((NodeDescPtr
) node
.buffer
)->numRecords
))
1004 err
= fsBTEndOfIterationErr
;
1008 // we did not find the record but the index is already positioned correctly
1009 if ((foundRecord
== false) && (index
!= ((NodeDescPtr
) node
.buffer
)->numRecords
))
1012 // we found the record OR we have to look in the next node
1013 if (index
< ((NodeDescPtr
) node
.buffer
)->numRecords
-1)
1019 if (right
.buffer
== nil
)
1021 nodeNum
= ((NodeDescPtr
) node
.buffer
)->fLink
;
1024 err
= GetNode (btreePtr
, nodeNum
, &right
);
1025 M_ExitOnError (err
);
1027 err
= fsBTEndOfIterationErr
;
1031 // Before we stomp on "left", we'd better release it if needed
1032 if (left
.buffer
!= nil
) {
1033 err
= ReleaseNode(btreePtr
, &left
);
1042 else // operation == kBTreeCurrentRecord
1044 // make sure we have something... <CS9>
1045 if ((foundRecord
!= true) &&
1046 (index
>= ((NodeDescPtr
) node
.buffer
)->numRecords
))
1048 err
= fsBTEndOfIterationErr
;
1053 //////////////////// Copy Record And Update Iterator ////////////////////////
1057 // added check for errors <CS9>
1058 err
= GetRecordByIndex (btreePtr
, node
.buffer
, index
, &keyPtr
, &recordPtr
, &len
);
1059 M_ExitOnError (err
);
1061 if (recordLen
!= nil
) *recordLen
= len
;
1065 ByteCount recordSize
;
1067 recordSize
= record
->itemCount
* record
->itemSize
;
1069 PanicIf(len
> recordSize
, "\pBTIterateRecord: truncating record!");
1071 if (len
> recordSize
) len
= recordSize
;
1073 CopyMemory (recordPtr
, record
->bufferAddress
, len
);
1076 if (iterator
!= nil
) // first & last do not require iterator
1078 iterator
->hint
.writeCount
= btreePtr
->writeCount
;
1079 iterator
->hint
.nodeNum
= nodeNum
;
1080 iterator
->hint
.index
= index
;
1081 iterator
->hint
.reserved1
= 0;
1082 iterator
->hint
.reserved2
= 0;
1084 iterator
->version
= 0;
1085 iterator
->reserved
= 0;
1087 CopyKey(btreePtr
, keyPtr
, &iterator
->key
);
1091 ///////////////////////////// Release Nodes /////////////////////////////////
1093 err
= ReleaseNode (btreePtr
, &node
);
1094 M_ExitOnError (err
);
1096 if (left
.buffer
!= nil
)
1098 err
= ReleaseNode (btreePtr
, &left
);
1099 M_ExitOnError (err
);
1102 if (right
.buffer
!= nil
)
1104 err
= ReleaseNode (btreePtr
, &right
);
1105 M_ExitOnError (err
);
1108 // LogEndTime(kTraceGetBTreeRecord, noErr);
1112 /////////////////////// Error - Clean Up and Exit ///////////////////////////
1116 (void) ReleaseNode (btreePtr
, &left
);
1117 (void) ReleaseNode (btreePtr
, &node
);
1118 (void) ReleaseNode (btreePtr
, &right
);
1120 if (recordLen
!= nil
)
1123 if (iterator
!= nil
)
1125 iterator
->hint
.writeCount
= 0;
1126 iterator
->hint
.nodeNum
= 0;
1127 iterator
->hint
.index
= 0;
1128 iterator
->hint
.reserved1
= 0;
1129 iterator
->hint
.reserved2
= 0;
1131 iterator
->version
= 0;
1132 iterator
->reserved
= 0;
1133 iterator
->key
.length16
= 0;
1136 if ( err
== fsBTEmptyErr
|| err
== fsBTEndOfIterationErr
)
1137 err
= fsBTRecordNotFoundErr
;
1139 // LogEndTime(kTraceGetBTreeRecord, err);
1145 //////////////////////////////// BTInsertRecord /////////////////////////////////
1147 OSStatus
BTInsertRecord (SFCB
*filePtr
,
1148 BTreeIterator
*iterator
,
1149 FSBufferDescriptor
*record
,
1153 BTreeControlBlockPtr btreePtr
;
1154 TreePathTable treePathTable
;
1156 BlockDescriptor nodeRec
;
1157 UInt32 insertNodeNum
;
1162 ////////////////////////// Priliminary Checks ///////////////////////////////
1164 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1166 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1170 // LogStartTime(kTraceInsertBTreeRecord);
1172 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
1174 ///////////////////////// Find Insert Position //////////////////////////////
1176 // always call SearchTree for Insert
1177 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1179 switch (err
) // set/replace/insert decision point
1181 case noErr
: err
= fsBTDuplicateRecordErr
;
1184 case fsBTRecordNotFoundErr
: break;
1186 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1188 if (btreePtr
->freeNodes
== 0)
1190 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1191 M_ExitOnError (err
);
1194 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1195 M_ExitOnError (err
);
1197 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1198 M_ExitOnError (err
);
1200 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1201 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1203 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1204 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1205 record
->bufferAddress
, recordLen
);
1206 if (recordFit
!= true)
1208 err
= fsBTRecordTooLargeErr
;
1212 err
= UpdateNode (btreePtr
, &nodeRec
);
1213 M_ExitOnError (err
);
1215 // update BTreeControlBlock
1216 btreePtr
->treeDepth
= 1;
1217 btreePtr
->rootNode
= insertNodeNum
;
1218 btreePtr
->firstLeafNode
= insertNodeNum
;
1219 btreePtr
->lastLeafNode
= insertNodeNum
;
1220 M_BTreeHeaderDirty (btreePtr
);
1230 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, index
,
1231 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1232 record
->bufferAddress
, recordLen
);
1233 if (recordFit
== true)
1235 err
= UpdateNode (btreePtr
, &nodeRec
);
1236 M_ExitOnError (err
);
1242 /////////////////////// Extend File If Necessary ////////////////////////////
1244 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //¥¥ math limit
1245 if (nodesNeeded
> 0)
1247 nodesNeeded
+= btreePtr
->totalNodes
;
1248 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1251 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1252 M_ExitOnError (err
);
1255 // no need to delete existing record
1257 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1258 recordLen
, &nodeRec
, index
, 1, kInsertRecord
, &insertNodeNum
);
1259 M_ExitOnError (err
);
1262 //////////////////////////////// Success ////////////////////////////////////
1265 ++btreePtr
->writeCount
; // <CS10>
1266 ++btreePtr
->leafRecords
;
1267 M_BTreeHeaderDirty (btreePtr
);
1270 iterator
->hint
.writeCount
= btreePtr
->writeCount
; // unused until <CS10>
1271 iterator
->hint
.nodeNum
= insertNodeNum
;
1272 iterator
->hint
.index
= 0; // unused
1273 iterator
->hint
.reserved1
= 0;
1274 iterator
->hint
.reserved2
= 0;
1276 // LogEndTime(kTraceInsertBTreeRecord, noErr);
1281 ////////////////////////////// Error Exit ///////////////////////////////////
1284 (void) ReleaseNode (btreePtr
, &nodeRec
);
1286 iterator
->hint
.writeCount
= 0;
1287 iterator
->hint
.nodeNum
= 0;
1288 iterator
->hint
.index
= 0;
1289 iterator
->hint
.reserved1
= 0;
1290 iterator
->hint
.reserved2
= 0;
1292 if (err
== fsBTEmptyErr
)
1293 err
= fsBTRecordNotFoundErr
;
1295 // LogEndTime(kTraceInsertBTreeRecord, err);
1302 ////////////////////////////////// BTSetRecord //////////////////////////////////
1304 OSStatus
BTSetRecord (SFCB
*filePtr
,
1305 BTreeIterator
*iterator
,
1306 FSBufferDescriptor
*record
,
1310 BTreeControlBlockPtr btreePtr
;
1311 TreePathTable treePathTable
;
1313 BlockDescriptor nodeRec
;
1314 UInt32 insertNodeNum
;
1316 Boolean recordFound
= false;
1322 ////////////////////////// Priliminary Checks ///////////////////////////////
1324 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1326 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1330 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
1333 ///////////////////////// Find Insert Position //////////////////////////////
1335 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1336 M_ExitOnError (err
);
1340 insertNodeNum
= iterator
->hint
.nodeNum
;
1342 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1345 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1346 M_ExitOnError (err
);
1350 err
= UpdateNode (btreePtr
, &nodeRec
);
1351 M_ExitOnError (err
);
1354 ++btreePtr
->numValidHints
;
1359 (void) BTInvalidateHint( iterator
);
1362 err
= ReleaseNode (btreePtr
, &nodeRec
);
1363 M_ExitOnError (err
);
1367 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1369 switch (err
) // set/replace/insert decision point
1371 case noErr
: recordFound
= true;
1374 case fsBTRecordNotFoundErr
: break;
1376 case fsBTEmptyErr
: // if tree empty add 1st leaf node
1378 if (btreePtr
->freeNodes
== 0)
1380 err
= ExtendBTree (btreePtr
, btreePtr
->totalNodes
+ 1);
1381 M_ExitOnError (err
);
1384 err
= AllocateNode (btreePtr
, &insertNodeNum
);
1385 M_ExitOnError (err
);
1387 err
= GetNewNode (btreePtr
, insertNodeNum
, &nodeRec
);
1388 M_ExitOnError (err
);
1390 ((NodeDescPtr
)nodeRec
.buffer
)->kind
= kBTLeafNode
;
1391 ((NodeDescPtr
)nodeRec
.buffer
)->height
= 1;
1393 recordFit
= InsertKeyRecord (btreePtr
, nodeRec
.buffer
, 0,
1394 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
1395 record
->bufferAddress
, recordLen
);
1396 if (recordFit
!= true)
1398 err
= fsBTRecordTooLargeErr
;
1402 err
= UpdateNode (btreePtr
, &nodeRec
);
1403 M_ExitOnError (err
);
1405 // update BTreeControlBlock
1406 btreePtr
->rootNode
= insertNodeNum
;
1407 btreePtr
->treeDepth
= 1;
1408 btreePtr
->flags
|= kBTHeaderDirty
;
1412 default: goto ErrorExit
;
1416 if (recordFound
== true) // Simple Replace - optimization avoids unecessary ExtendBTree
1418 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1419 M_ExitOnError (err
);
1423 err
= UpdateNode (btreePtr
, &nodeRec
);
1424 M_ExitOnError (err
);
1431 /////////////////////// Extend File If Necessary ////////////////////////////
1433 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //¥¥ math limit
1434 if (nodesNeeded
> 0)
1436 nodesNeeded
+= btreePtr
->totalNodes
;
1437 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1440 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1441 M_ExitOnError (err
);
1445 if (recordFound
== true) // Delete existing record
1447 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
);
1448 operation
= kReplaceRecord
;
1450 operation
= kInsertRecord
;
1453 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1454 recordLen
, &nodeRec
, index
, 1, operation
, &insertNodeNum
);
1455 M_ExitOnError (err
);
1457 ++btreePtr
->writeCount
; // <CS10> writeCount changes only if the tree structure changed
1460 if (recordFound
== false)
1462 ++btreePtr
->leafRecords
;
1463 M_BTreeHeaderDirty (btreePtr
);
1467 iterator
->hint
.writeCount
= btreePtr
->writeCount
; // unused until <CS10>
1468 iterator
->hint
.nodeNum
= insertNodeNum
;
1469 iterator
->hint
.index
= 0; // unused
1470 iterator
->hint
.reserved1
= 0;
1471 iterator
->hint
.reserved2
= 0;
1476 ////////////////////////////// Error Exit ///////////////////////////////////
1480 (void) ReleaseNode (btreePtr
, &nodeRec
);
1482 iterator
->hint
.writeCount
= 0;
1483 iterator
->hint
.nodeNum
= 0;
1484 iterator
->hint
.index
= 0;
1485 iterator
->hint
.reserved1
= 0;
1486 iterator
->hint
.reserved2
= 0;
1493 //////////////////////////////// BTReplaceRecord ////////////////////////////////
1495 OSStatus
BTReplaceRecord (SFCB
*filePtr
,
1496 BTreeIterator
*iterator
,
1497 FSBufferDescriptor
*record
,
1501 BTreeControlBlockPtr btreePtr
;
1502 TreePathTable treePathTable
;
1504 BlockDescriptor nodeRec
;
1505 UInt32 insertNodeNum
;
1511 ////////////////////////// Priliminary Checks ///////////////////////////////
1513 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1515 err
= CheckInsertParams (filePtr
, iterator
, record
, recordLen
);
1519 // LogStartTime(kTraceReplaceBTreeRecord);
1521 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
1523 ////////////////////////////// Take A Hint //////////////////////////////////
1525 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
1526 M_ExitOnError (err
);
1530 insertNodeNum
= iterator
->hint
.nodeNum
;
1532 err
= GetNode (btreePtr
, insertNodeNum
, &nodeRec
);
1535 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1536 M_ExitOnError (err
);
1540 err
= UpdateNode (btreePtr
, &nodeRec
);
1541 M_ExitOnError (err
);
1543 ++btreePtr
->numValidHints
;
1549 (void) BTInvalidateHint( iterator
);
1552 err
= ReleaseNode (btreePtr
, &nodeRec
);
1553 M_ExitOnError (err
);
1557 (void) BTInvalidateHint( iterator
);
1562 ////////////////////////////// Get A Clue ///////////////////////////////////
1564 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &insertNodeNum
, &nodeRec
, &index
);
1565 M_ExitOnError (err
); // record must exit for Replace
1567 // optimization - if simple replace will work then don't extend btree
1568 // ¥¥ if we tried this before, and failed because it wouldn't fit then we shouldn't try this again...
1570 err
= TrySimpleReplace (btreePtr
, nodeRec
.buffer
, iterator
, record
, recordLen
, &recordFit
);
1571 M_ExitOnError (err
);
1575 err
= UpdateNode (btreePtr
, &nodeRec
);
1576 M_ExitOnError (err
);
1582 //////////////////////////// Make Some Room /////////////////////////////////
1584 nodesNeeded
= btreePtr
->treeDepth
+ 1 - btreePtr
->freeNodes
; //¥¥ math limit
1585 if (nodesNeeded
> 0)
1587 nodesNeeded
+= btreePtr
->totalNodes
;
1588 if (nodesNeeded
> CalcMapBits (btreePtr
)) // we'll need to add a map node too!
1591 err
= ExtendBTree (btreePtr
, nodesNeeded
);
1592 M_ExitOnError (err
);
1596 DeleteRecord (btreePtr
, nodeRec
.buffer
, index
); // delete existing key/record
1598 err
= InsertTree (btreePtr
, treePathTable
, &iterator
->key
, record
->bufferAddress
,
1599 recordLen
, &nodeRec
, index
, 1, kReplaceRecord
, &insertNodeNum
);
1600 M_ExitOnError (err
);
1602 ++btreePtr
->writeCount
; // <CS10> writeCount changes only if the tree structure changed
1606 iterator
->hint
.writeCount
= btreePtr
->writeCount
; // unused until <CS10>
1607 iterator
->hint
.nodeNum
= insertNodeNum
;
1608 iterator
->hint
.index
= 0; // unused
1609 iterator
->hint
.reserved1
= 0;
1610 iterator
->hint
.reserved2
= 0;
1612 // LogEndTime(kTraceReplaceBTreeRecord, noErr);
1617 ////////////////////////////// Error Exit ///////////////////////////////////
1621 (void) ReleaseNode (btreePtr
, &nodeRec
);
1623 iterator
->hint
.writeCount
= 0;
1624 iterator
->hint
.nodeNum
= 0;
1625 iterator
->hint
.index
= 0;
1626 iterator
->hint
.reserved1
= 0;
1627 iterator
->hint
.reserved2
= 0;
1629 // LogEndTime(kTraceReplaceBTreeRecord, err);
1636 //////////////////////////////// BTDeleteRecord /////////////////////////////////
1638 OSStatus
BTDeleteRecord (SFCB
*filePtr
,
1639 BTreeIterator
*iterator
)
1642 BTreeControlBlockPtr btreePtr
;
1643 TreePathTable treePathTable
;
1644 BlockDescriptor nodeRec
;
1648 // LogStartTime(kTraceDeleteBTreeRecord);
1650 ////////////////////////// Priliminary Checks ///////////////////////////////
1652 nodeRec
.buffer
= nil
; // so we can call ReleaseNode
1654 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1655 M_ReturnErrorIf (iterator
== nil
, paramErr
);
1657 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
1658 if (btreePtr
== nil
)
1660 err
= fsBTInvalidFileErr
;
1664 #if SupportsKeyDescriptors
1665 if (btreePtr
->keyDescPtr
!= nil
)
1667 err
= CheckKey (&iterator
->key
, btreePtr
->keyDescPtr
, btreePtr
->maxKeyLength
);
1668 M_ExitOnError (err
);
1672 /////////////////////////////// Find Key ////////////////////////////////////
1674 //¥¥ check hint for simple delete case (index > 0, numRecords > 2)
1676 err
= SearchTree (btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, &nodeRec
, &index
);
1677 M_ExitOnError (err
); // record must exit for Delete
1680 ///////////////////////////// Delete Record /////////////////////////////////
1682 err
= DeleteTree (btreePtr
, treePathTable
, &nodeRec
, index
, 1);
1683 M_ExitOnError (err
);
1686 ++btreePtr
->writeCount
; // <CS10>
1687 --btreePtr
->leafRecords
;
1688 M_BTreeHeaderDirty (btreePtr
);
1690 iterator
->hint
.nodeNum
= 0;
1692 // LogEndTime(kTraceDeleteBTreeRecord, noErr);
1696 ////////////////////////////// Error Exit ///////////////////////////////////
1699 (void) ReleaseNode (btreePtr
, &nodeRec
);
1701 // LogEndTime(kTraceDeleteBTreeRecord, err);
1708 OSStatus
BTGetInformation (SFCB
*filePtr
,
1710 BTreeInfoRec
*info
)
1712 #pragma unused (version)
1714 BTreeControlBlockPtr btreePtr
;
1717 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1719 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
1721 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1722 M_ReturnErrorIf (info
== nil
, paramErr
);
1726 info
->nodeSize
= btreePtr
->nodeSize
;
1727 info
->maxKeyLength
= btreePtr
->maxKeyLength
;
1728 info
->treeDepth
= btreePtr
->treeDepth
;
1729 info
->numRecords
= btreePtr
->leafRecords
;
1730 info
->numNodes
= btreePtr
->totalNodes
;
1731 info
->numFreeNodes
= btreePtr
->freeNodes
;
1732 info
->keyDescriptor
= btreePtr
->keyDescPtr
; //¥¥ this won't do at all...
1735 if (btreePtr
->keyDescPtr
== nil
)
1736 info
->keyDescLength
= 0;
1738 info
->keyDescLength
= (UInt32
) btreePtr
->keyDescPtr
->length
;
1745 /*-------------------------------------------------------------------------------
1746 Routine: BTFlushPath - Flush BTreeControlBlock to Header Node.
1748 Function: Brief_description_of_the_function_and_any_side_effects
1751 Input: pathPtr - pointer to path control block for B*Tree file to flush
1755 Result: noErr - success
1757 -------------------------------------------------------------------------------*/
1759 OSStatus
BTFlushPath (SFCB
*filePtr
)
1762 BTreeControlBlockPtr btreePtr
;
1765 // LogStartTime(kTraceFlushBTree);
1767 M_ReturnErrorIf (filePtr
== nil
, paramErr
);
1769 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
1771 M_ReturnErrorIf (btreePtr
== nil
, fsBTInvalidFileErr
);
1773 err
= UpdateHeader (btreePtr
);
1775 // LogEndTime(kTraceFlushBTree, err);
1782 /*-------------------------------------------------------------------------------
1783 Routine: BTInvalidateHint - Invalidates the hint within a BTreeInterator.
1785 Function: Invalidates the hint within a BTreeInterator.
1788 Input: iterator - pointer to BTreeIterator
1790 Output: iterator - iterator with the hint.nodeNum cleared
1792 Result: noErr - success
1793 paramErr - iterator == nil
1794 -------------------------------------------------------------------------------*/
1797 OSStatus
BTInvalidateHint (BTreeIterator
*iterator
)
1799 if (iterator
== nil
)
1802 iterator
->hint
.nodeNum
= 0;