2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
25 Contains: Single-node operations for the BTree Module.
27 Version: xxx put the technology version here xxx
29 Written by: Gordon Sheridan and Bill Bruffey
31 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
37 Other Contact: Mark Day
39 Technology: File Systems
46 Change History (most recent first):
48 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
49 <MOSXS> 4/113/99 djb Fix key size checking bug in CheckNode.
50 <MOSXS> 3/19/99 djb Added key size checking to CheckNode.
51 <MOSXS> 3/26/98 djb Added PrintNode for debugging.
52 <CS5> 9/4/97 djb Removed GetRightSiblingNode and GetLeftSiblingNode - they are
53 now macros. SearchNode is now in BTreeSearchNode.a.
54 <CS4> 8/22/97 djb Turn off debugging code in CheckKey.
55 <CS3> 7/24/97 djb Add summary traces for Get/Rel Node. Made GetRecordOffset into a
56 macro. Only call CheckNode if the node came from disk.
57 <CS2> 7/21/97 msd Make GetRecordByIndex check its record index input; it now
59 <CS1> 4/23/97 djb first checked in
61 <HFS3> 2/19/97 djb Changes to support big node cache.
62 <HFS2> 1/3/97 djb Added support for large keys.
63 <HFS1> 12/19/96 djb first checked in
66 History applicable to original Scarecrow Design:
68 <6> 10/25/96 ser Changing for new VFPI
69 <5> 9/17/96 dkh Add bounds checking to GetNode. Update GetNode to not assert
70 that CheckNode failed if the node is all zeroes. This can happen
71 if the hint case if the fetched node has been deallocated
72 <4> 3/7/96 dkh Change GetNewNode() to not use kGetEmptyBlock. Instead use
73 kGetBlock to fetch a block from the disk itself. \80\80\80 Why?
74 <3> 1/22/96 dkh Add #include Memory.h
75 <2> 1/10/96 msd Change 64-bit math to use real function names from Math64.i.
76 <1> 10/18/95 rst Moved from Scarecrow project.
78 <17> 7/18/95 mbb Change MoveData & ClearBytes to BlockMoveData & BlockZero.
79 <16> 1/31/95 prp GetBlockProc interface uses a 64 bit node number.
80 <15> 1/12/95 wjk Adopt Model FileSystem changes in D5.
81 <14> 9/30/94 prp Get in sync with D2 interface changes.
82 <13> 7/25/94 wjk Eliminate usage of BytePtr in favor of UInt8 *.
83 <12> 7/22/94 wjk Convert to the new set of header files.
84 <11> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
86 <10> 11/30/93 wjk Change some Ptr's to BytePtr's in function definitions so they
87 agree with their prototypes.
88 <9> 8/31/93 prp Use U64SetU instead of S64Set.
89 <8> 5/21/93 gs Maintain statistical counters on Get/Release node routines.
90 <7> 5/10/93 gs Change keySize parameter to keyLength for InsertKeyRecord
91 routine. Calculate number of bytes in key from keyLength to
92 account for length and pad bytes. Add GetChildNodeNum routine.
93 <6> 3/23/93 gs Add InsertKeyRecord routine.
94 <5> 2/8/93 gs Fix bug in SearchNode that caused "off by 1" error when final
95 compare was searchKey > trialKey. Add UpdateNode.
96 <4> 12/10/92 gs Change keyLength field of key to 'length'.
97 <3> 12/8/92 gs Incorporate suggestions from preliminary code review.
98 <2> 12/2/92 gs Implement routines.
99 <1> 11/15/92 gs Define routine interfaces.
103 #include "../headers/BTreesPrivate.h"
104 #include "../headers/HFSInstrumentation.h"
108 ///////////////////////// BTree Module Node Operations //////////////////////////
110 // GetNode - Call FS Agent to get node
111 // GetNewNode - Call FS Agent to get a new node
112 // ReleaseNode - Call FS Agent to release node obtained by GetNode.
113 // UpdateNode - Mark a node as dirty and call FS Agent to release it.
115 // CheckNode - Checks the validity of a node.
116 // ClearNode - Clear a node to all zeroes.
118 // InsertRecord - Inserts a record into a BTree node.
119 // InsertKeyRecord - Inserts a key and record pair into a BTree node.
120 // DeleteRecord - Deletes a record from a BTree node.
122 // SearchNode - Return index for record that matches key.
123 // LocateRecord - Return pointer to key and data, and size of data.
125 // GetNodeDataSize - Return the amount of space used for data in the node.
126 // GetNodeFreeSize - Return the amount of free space in the node.
128 // GetRecordOffset - Return the offset for record "index".
129 // GetRecordAddress - Return address of record "index".
130 // GetOffsetAddress - Return address of offset for record "index".
132 // InsertOffset - Inserts a new offset into a node.
133 // DeleteOffset - Deletes an offset from a node.
135 /////////////////////////////////////////////////////////////////////////////////
139 ////////////////////// Routines Internal To BTreeNodeOps.c //////////////////////
141 UInt16
GetRecordOffset (BTreeControlBlockPtr btree
,
145 UInt16
*GetOffsetAddress (BTreeControlBlockPtr btreePtr
,
149 void InsertOffset (BTreeControlBlockPtr btreePtr
,
154 void DeleteOffset (BTreeControlBlockPtr btreePtr
,
159 /////////////////////////////////////////////////////////////////////////////////
161 #define GetRecordOffset(btreePtr,node,index) (*(short *) ((UInt8 *)(node) + (btreePtr)->nodeSize - ((index) << 1) - kOffsetSize))
164 #include <sys/systm.h>
165 #define PRINTIT kprintf
166 #endif /* HFS_DIAGNOSTIC */
168 static void PrintNode(const NodeDescPtr node
, UInt16 nodeSize
, UInt32 nodeNumber
);
172 /*-------------------------------------------------------------------------------
174 Routine: GetNode - Call FS Agent to get node
176 Function: Gets an existing BTree node from FS Agent and verifies it.
178 Input: btreePtr - pointer to BTree control block
179 nodeNum - number of node to request
181 Output: nodePtr - pointer to beginning of node (nil if error)
186 -------------------------------------------------------------------------------*/
188 OSStatus
GetNode (BTreeControlBlockPtr btreePtr
,
193 GetBlockProcPtr getNodeProc
;
196 LogStartTime(kTraceGetNode
);
198 //\80\80 is nodeNum within proper range?
199 if( nodeNum
>= btreePtr
->totalNodes
)
201 Panic("\pGetNode:nodeNum >= totalNodes");
202 err
= fsBTInvalidNodeErr
;
206 nodePtr
->blockSize
= btreePtr
->nodeSize
; // indicate the size of a node
208 getNodeProc
= btreePtr
->getBlockProc
;
209 err
= getNodeProc (btreePtr
->fileRefNum
,
216 Panic ("\pGetNode: getNodeProc returned error.");
217 // nodePtr->buffer = nil;
220 ++btreePtr
->numGetNodes
;
224 // Only call CheckNode if the node came from disk.
225 // If it was in the cache, we'll assume its already a valid node.
228 if ( nodePtr
->blockReadFromDisk
) // if we read it from disk then check it
230 err
= CheckNode (btreePtr
, nodePtr
->buffer
);
235 VTOVCB(btreePtr
->fileRefNum
)->vcbFlags
|= kHFS_DamagedVolume
;
238 if (((NodeDescPtr
)nodePtr
->buffer
)->numRecords
!= 0)
239 PrintNode(nodePtr
->buffer
, btreePtr
->nodeSize
, nodeNum
);
244 // With the removal of bounds checking in IsItAHint(), it's possible that
245 // GetNode() will be called to fetch a clear (all zeroes) node. We want
246 // CheckNode() to fail in this case (it does), however we don't want to assert
247 // this case because it is not really an "error". Returning an error from GetNode()
248 // in this case will cause the hint checking code to ignore the hint and revert to
249 // the full search mode.
255 cur
= nodePtr
->buffer
;
256 lastPlusOne
= (UInt32
*) ((UInt8
*) cur
+ btreePtr
->nodeSize
);
258 while( cur
< lastPlusOne
)
262 Panic ("\pGetNode: CheckNode returned error.");
269 (void) TrashNode (btreePtr
, nodePtr
); // ignore error
274 LogEndTime(kTraceGetNode
, noErr
);
279 nodePtr
->buffer
= nil
;
280 nodePtr
->blockHeader
= nil
;
282 LogEndTime(kTraceGetNode
, err
);
289 /*-------------------------------------------------------------------------------
291 Routine: GetNewNode - Call FS Agent to get a new node
293 Function: Gets a new BTree node from FS Agent and initializes it to an empty
296 Input: btreePtr - pointer to BTree control block
297 nodeNum - number of node to request
299 Output: returnNodePtr - pointer to beginning of node (nil if error)
301 Result: noErr - success
303 -------------------------------------------------------------------------------*/
305 OSStatus
GetNewNode (BTreeControlBlockPtr btreePtr
,
307 NodeRec
*returnNodePtr
)
312 GetBlockProcPtr getNodeProc
;
315 //////////////////////// get buffer for new node ////////////////////////////
317 returnNodePtr
->blockSize
= btreePtr
->nodeSize
; // indicate the size of a node
319 getNodeProc
= btreePtr
->getBlockProc
;
320 err
= getNodeProc (btreePtr
->fileRefNum
,
322 kGetBlock
+kGetEmptyBlock
,
327 Panic ("\pGetNewNode: getNodeProc returned error.");
328 // returnNodePtr->buffer = nil;
331 ++btreePtr
->numGetNewNodes
;
334 ////////////////////////// initialize the node //////////////////////////////
336 node
= returnNodePtr
->buffer
;
338 ClearNode (btreePtr
, node
); // clear the node
340 pos
= (char *)node
+ btreePtr
->nodeSize
- 2; // find address of last offset
341 *(UInt16
*)pos
= sizeof (BTNodeDescriptor
); // set offset to beginning of free space
349 /*-------------------------------------------------------------------------------
351 Routine: ReleaseNode - Call FS Agent to release node obtained by GetNode.
353 Function: Informs the FS Agent that a BTree node may be released.
355 Input: btreePtr - pointer to BTree control block
356 nodeNum - number of node to release
358 Result: noErr - success
360 -------------------------------------------------------------------------------*/
362 OSStatus
ReleaseNode (BTreeControlBlockPtr btreePtr
,
366 ReleaseBlockProcPtr releaseNodeProc
;
369 LogStartTime(kTraceReleaseNode
);
373 if (nodePtr
->buffer
!= nil
)
375 releaseNodeProc
= btreePtr
->releaseBlockProc
;
376 err
= releaseNodeProc (btreePtr
->fileRefNum
,
379 PanicIf (err
, "\pReleaseNode: releaseNodeProc returned error.");
380 ++btreePtr
->numReleaseNodes
;
383 nodePtr
->buffer
= nil
;
384 nodePtr
->blockHeader
= nil
;
386 LogEndTime(kTraceReleaseNode
, err
);
394 /*-------------------------------------------------------------------------------
396 Routine: TrashNode - Call FS Agent to release node obtained by GetNode, and
397 not store it...mark it as bad.
399 Function: Informs the FS Agent that a BTree node may be released and thrown away.
401 Input: btreePtr - pointer to BTree control block
402 nodeNum - number of node to release
404 Result: noErr - success
406 -------------------------------------------------------------------------------*/
408 OSStatus
TrashNode (BTreeControlBlockPtr btreePtr
,
412 ReleaseBlockProcPtr releaseNodeProc
;
415 LogStartTime(kTraceReleaseNode
);
419 if (nodePtr
->buffer
!= nil
)
421 releaseNodeProc
= btreePtr
->releaseBlockProc
;
422 err
= releaseNodeProc (btreePtr
->fileRefNum
,
424 kReleaseBlock
| kTrashBlock
);
425 PanicIf (err
, "\TrashNode: releaseNodeProc returned error.");
426 ++btreePtr
->numReleaseNodes
;
429 nodePtr
->buffer
= nil
;
430 nodePtr
->blockHeader
= nil
;
432 LogEndTime(kTraceReleaseNode
, err
);
439 /*-------------------------------------------------------------------------------
441 Routine: UpdateNode - Mark a node as dirty and call FS Agent to release it.
443 Function: Marks a BTree node dirty and informs the FS Agent that it may be released.
445 //\80\80 have another routine that clears & writes a node, so we can call
446 CheckNode from this routine.
448 Input: btreePtr - pointer to BTree control block
449 nodeNum - number of node to release
450 transactionID - ID of transaction this node update is a part of
451 flags - special flags to pass to ReleaseNodeProc
453 Result: noErr - success
455 -------------------------------------------------------------------------------*/
457 OSStatus
UpdateNode (BTreeControlBlockPtr btreePtr
,
459 UInt32 transactionID
,
463 ReleaseBlockProcPtr releaseNodeProc
;
468 if (nodePtr
->buffer
!= nil
) //\80\80 why call UpdateNode if nil ?!?
472 if ( btreePtr
->attributes
& kBTVariableIndexKeysMask
)
473 (void) CheckNode (btreePtr
, nodePtr
->buffer
);
476 LogStartTime(kTraceReleaseNode
);
478 releaseNodeProc
= btreePtr
->releaseBlockProc
;
479 err
= releaseNodeProc (btreePtr
->fileRefNum
,
481 flags
| kMarkBlockDirty
);
483 LogEndTime(kTraceReleaseNode
, err
);
486 ++btreePtr
->numUpdateNodes
;
489 nodePtr
->buffer
= nil
;
490 nodePtr
->blockHeader
= nil
;
501 /*-------------------------------------------------------------------------------
503 Routine: CheckNode - Checks the validity of a node.
505 Function: Checks the validity of a node by verifying that the fLink and bLink fields
506 are within the forks EOF. The node type must be one of the four known
507 types. The node height must be less than or equal to the tree height. The
508 node must not have more than the maximum number of records, and the record
509 offsets must make sense.
511 Input: btreePtr - pointer to BTree control block
512 node - pointer to node to check
514 Result: noErr - success
515 fsBTInvalidNodeErr - failure
516 -------------------------------------------------------------------------------*/
518 OSStatus
CheckNode (BTreeControlBlockPtr btreePtr
, NodeDescPtr node
)
527 nodeSize
= btreePtr
->nodeSize
;
529 ///////////////////// are fLink and bLink within EOF ////////////////////////
531 maxNode
= (GetFileControlBlock(btreePtr
->fileRefNum
)->fcbEOF
/ nodeSize
) - 1;
533 if ( (node
->fLink
> maxNode
) || (node
->bLink
> maxNode
) )
534 return fsBTInvalidNodeErr
;
536 /////////////// check node type (leaf, index, header, map) //////////////////
538 if ( (node
->kind
< kBTLeafNode
) || (node
->kind
> kBTMapNode
) )
539 return fsBTInvalidNodeErr
;
541 ///////////////////// is node height > tree depth? //////////////////////////
543 if ( node
->height
> btreePtr
->treeDepth
)
544 return fsBTInvalidNodeErr
;
546 //////////////////////// check number of records ////////////////////////////
548 //XXX can we calculate a more accurate minimum record size?
549 maxRecords
= ( nodeSize
- sizeof (BTNodeDescriptor
) ) >> 3;
551 if (node
->numRecords
> maxRecords
)
552 return fsBTInvalidNodeErr
;
554 ////////////////////////// check record offsets /////////////////////////////
556 index
= node
->numRecords
; /* start index at free space */
557 prevOffset
= nodeSize
- (index
<< 1); /* use 2 bytes past end of free space */
560 offset
= GetRecordOffset (btreePtr
, node
, index
);
562 if (offset
& 1) // offset is odd
563 return fsBTInvalidNodeErr
;
565 if (offset
>= prevOffset
) // offset >= previous offset
566 return fsBTInvalidNodeErr
;
568 /* reject keys that overflow record slot */
569 if ((node
->kind
== kBTLeafNode
) &&
570 (index
< node
->numRecords
) && /* ignore free space record */
571 (CalcKeySize(btreePtr
, (KeyPtr
) ((Ptr
)node
+ offset
)) > (prevOffset
- offset
))) {
572 return fsBTInvalidNodeErr
;
576 } while ( --index
>= 0 );
578 if (offset
< sizeof (BTNodeDescriptor
) ) // first offset < minimum ?
579 return fsBTInvalidNodeErr
;
586 static void PrintNode(const NodeDescPtr node
, UInt16 nodeSize
, UInt32 nodeNumber
)
595 PRINTIT("Dump of B-tree node #%ld ($%08lX)\n", nodeNumber
, nodeNumber
);
602 PRINTIT("%04X: %08lX %08lX %08lX %08lX\n", (u_int
)offset
++, *lp
++, *lp
++, *lp
++, *lp
++);
607 /*-------------------------------------------------------------------------------
609 Routine: ClearNode - Clear a node to all zeroes.
611 Function: Writes zeroes from beginning of node for nodeSize bytes.
613 Input: btreePtr - pointer to BTree control block
614 node - pointer to node to clear
617 -------------------------------------------------------------------------------*/
619 void ClearNode (BTreeControlBlockPtr btreePtr
, NodeDescPtr node
)
621 ClearMemory( node
, btreePtr
->nodeSize
);
624 /*-------------------------------------------------------------------------------
626 Routine: InsertRecord - Inserts a record into a BTree node.
630 Note: Record size must be even!
632 Input: btreePtr - pointer to BTree control block
633 node - pointer to node to insert the record
634 index - position record is to be inserted
635 recPtr - pointer to record to insert
637 Result: noErr - success
638 fsBTFullErr - record larger than remaining free space.
639 -------------------------------------------------------------------------------*/
641 Boolean
InsertRecord (BTreeControlBlockPtr btreePtr
,
654 //// will new record fit in node?
656 freeSpace
= GetNodeFreeSize (btreePtr
, node
);
657 //\80\80 we could get freeOffset & calc freeSpace
658 if ( freeSpace
< recSize
+ 2)
664 //// make hole for new record
666 indexOffset
= GetRecordOffset (btreePtr
, node
, index
);
667 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
669 src
= ((Ptr
) node
) + indexOffset
;
670 dst
= ((Ptr
) src
) + recSize
;
671 bytesToMove
= freeOffset
- indexOffset
;
673 MoveRecordsRight (src
, dst
, bytesToMove
);
676 //// adjust offsets for moved records
678 InsertOffset (btreePtr
, node
, index
, recSize
);
681 //// move in the new record
683 dst
= ((Ptr
) node
) + indexOffset
;
684 MoveRecordsLeft (recPtr
, dst
, recSize
);
691 /*-------------------------------------------------------------------------------
693 Routine: InsertKeyRecord - Inserts a record into a BTree node.
697 Note: Record size must be even!
699 Input: btreePtr - pointer to BTree control block
700 node - pointer to node to insert the record
701 index - position record is to be inserted
702 keyPtr - pointer to key for record to insert
703 keyLength - length of key (or maxKeyLength)
704 recPtr - pointer to record to insert
705 recSize - number of bytes to copy for record
707 Result: noErr - success
708 fsBTFullErr - record larger than remaining free space.
709 -------------------------------------------------------------------------------*/
711 Boolean
InsertKeyRecord (BTreeControlBlockPtr btreePtr
,
729 //// calculate actual key size
731 if ( btreePtr
->attributes
& kBTBigKeysMask
)
732 keySize
= keyLength
+ sizeof(UInt16
);
734 keySize
= keyLength
+ sizeof(UInt8
);
736 if ( M_IsOdd (keySize
) )
737 ++keySize
; // add pad byte
740 //// will new record fit in node?
742 freeSpace
= GetNodeFreeSize (btreePtr
, node
);
743 //\80\80 we could get freeOffset & calc freeSpace
744 if ( freeSpace
< keySize
+ recSize
+ 2)
750 //// make hole for new record
752 indexOffset
= GetRecordOffset (btreePtr
, node
, index
);
753 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
755 src
= ((UInt8
*) node
) + indexOffset
;
756 dst
= ((UInt8
*) src
) + keySize
+ recSize
;
757 bytesToMove
= freeOffset
- indexOffset
;
759 MoveRecordsRight (src
, dst
, bytesToMove
);
762 //// adjust offsets for moved records
764 InsertOffset (btreePtr
, node
, index
, keySize
+ recSize
);
769 dst
= ((UInt8
*) node
) + indexOffset
;
771 if ( btreePtr
->attributes
& kBTBigKeysMask
)
773 *((UInt16
*) dst
)++ = keyLength
; // use keyLength rather than key.length
774 rawKeyLength
= keyPtr
->length16
;
779 *dst
++ = keyLength
; // use keyLength rather than key.length
780 rawKeyLength
= keyPtr
->length8
;
784 MoveRecordsLeft ( ((UInt8
*) keyPtr
) + sizeOfLength
, dst
, rawKeyLength
); // copy key
787 bytesToMove
= keySize
- rawKeyLength
;
789 ClearMemory (dst
+ rawKeyLength
, bytesToMove
); // clear pad bytes in index key
792 //// copy record data
794 dst
= ((UInt8
*) node
) + indexOffset
+ keySize
;
795 MoveRecordsLeft (recPtr
, dst
, recSize
);
802 /*-------------------------------------------------------------------------------
804 Routine: DeleteRecord - Deletes a record from a BTree node.
808 Input: btreePtr - pointer to BTree control block
809 node - pointer to node to insert the record
810 index - position record is to be inserted
813 -------------------------------------------------------------------------------*/
815 void DeleteRecord (BTreeControlBlockPtr btreePtr
,
826 //// compress records
827 indexOffset
= GetRecordOffset (btreePtr
, node
, index
);
828 nextOffset
= GetRecordOffset (btreePtr
, node
, index
+ 1);
829 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
831 src
= ((Ptr
) node
) + nextOffset
;
832 dst
= ((Ptr
) node
) + indexOffset
;
833 bytesToMove
= freeOffset
- nextOffset
;
835 MoveRecordsLeft (src
, dst
, bytesToMove
);
837 //// Adjust the offsets
838 DeleteOffset (btreePtr
, node
, index
);
840 /* clear out new free space */
841 bytesToMove
= nextOffset
- indexOffset
;
842 ClearMemory(GetRecordAddress(btreePtr
, node
, node
->numRecords
), bytesToMove
);
848 /*-------------------------------------------------------------------------------
850 Routine: SearchNode - Return index for record that matches key.
852 Function: Returns the record index for the record that matches the search key.
853 If no record was found that matches the search key, the "insert index"
854 of where the record should go is returned instead.
856 Algorithm: A binary search algorithm is used to find the specified key.
858 Input: btreePtr - pointer to BTree control block
859 node - pointer to node that contains the record
860 searchKey - pointer to the key to match
862 Output: index - pointer to beginning of key for record
864 Result: true - success (index = record index)
865 false - key did not match anything in node (index = insert index)
866 -------------------------------------------------------------------------------*/
868 SearchNode( BTreeControlBlockPtr btreePtr
,
871 UInt16
*returnIndex
)
879 KeyCompareProcPtr compareProc
= btreePtr
->keyCompareProc
;
882 upperBound
= node
->numRecords
- 1;
883 offset
= (UInt16
*) ((UInt8
*)(node
) + (btreePtr
)->nodeSize
- kOffsetSize
);
885 while (lowerBound
<= upperBound
) {
886 index
= (lowerBound
+ upperBound
) >> 1;
888 trialKey
= (KeyPtr
) ((UInt8
*)node
+ *(offset
- index
));
890 result
= compareProc(searchKey
, trialKey
);
893 upperBound
= index
- 1; /* search < trial */
894 } else if (result
> 0) {
895 lowerBound
= index
+ 1; /* search > trial */
897 *returnIndex
= index
; /* search == trial */
902 *returnIndex
= lowerBound
; /* lowerBound is insert index */
907 /*-------------------------------------------------------------------------------
909 Routine: GetRecordByIndex - Return pointer to key and data, and size of data.
911 Function: Returns a pointer to beginning of key for record, a pointer to the
912 beginning of the data for the record, and the size of the record data
913 (does not include the size of the key).
915 Input: btreePtr - pointer to BTree control block
916 node - pointer to node that contains the record
917 index - index of record to get
919 Output: keyPtr - pointer to beginning of key for record
920 dataPtr - pointer to beginning of data for record
921 dataSize - size of the data portion of the record
924 -------------------------------------------------------------------------------*/
926 OSStatus
GetRecordByIndex (BTreeControlBlockPtr btreePtr
,
938 // Make sure index is valid (in range 0..numRecords-1)
940 if (index
>= node
->numRecords
)
941 return fsBTRecordNotFoundErr
;
944 offset
= GetRecordOffset (btreePtr
, node
, index
);
945 *keyPtr
= (KeyPtr
) ((Ptr
)node
+ offset
);
948 keySize
= CalcKeySize(btreePtr
, *keyPtr
);
949 if ( M_IsOdd (keySize
) )
950 ++keySize
; // add pad byte
952 offset
+= keySize
; // add the key length to find data offset
953 *dataPtr
= (UInt8
*) node
+ offset
;
956 nextOffset
= GetRecordOffset (btreePtr
, node
, index
+ 1);
957 *dataSize
= nextOffset
- offset
;
964 /*-------------------------------------------------------------------------------
966 Routine: GetNodeDataSize - Return the amount of space used for data in the node.
968 Function: Gets the size of the data currently contained in a node, excluding
969 the node header. (record data + offset overhead)
971 Input: btreePtr - pointer to BTree control block
972 node - pointer to node that contains the record
974 Result: - number of bytes used for data and offsets in the node.
975 -------------------------------------------------------------------------------*/
977 UInt16
GetNodeDataSize (BTreeControlBlockPtr btreePtr
, NodeDescPtr node
)
981 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
983 return freeOffset
+ (node
->numRecords
<< 1) - sizeof (BTNodeDescriptor
);
988 /*-------------------------------------------------------------------------------
990 Routine: GetNodeFreeSize - Return the amount of free space in the node.
994 Input: btreePtr - pointer to BTree control block
995 node - pointer to node that contains the record
997 Result: - number of bytes of free space in the node.
998 -------------------------------------------------------------------------------*/
1000 UInt16
GetNodeFreeSize (BTreeControlBlockPtr btreePtr
, NodeDescPtr node
)
1004 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
); //\80\80 inline?
1006 return btreePtr
->nodeSize
- freeOffset
- (node
->numRecords
<< 1) - kOffsetSize
;
1011 /*-------------------------------------------------------------------------------
1013 Routine: GetRecordOffset - Return the offset for record "index".
1017 Input: btreePtr - pointer to BTree control block
1018 node - pointer to node that contains the record
1019 index - record to obtain offset for
1021 Result: - offset (in bytes) from beginning of node of record specified by index
1022 -------------------------------------------------------------------------------*/
1023 // make this a macro (for inlining)
1025 UInt16
GetRecordOffset (BTreeControlBlockPtr btreePtr
,
1032 pos
= (UInt8
*)node
+ btreePtr
->nodeSize
- (index
<< 1) - kOffsetSize
;
1034 return *(short *)pos
;
1040 /*-------------------------------------------------------------------------------
1042 Routine: GetRecordAddress - Return address of record "index".
1046 Input: btreePtr - pointer to BTree control block
1047 node - pointer to node that contains the record
1048 index - record to obtain offset address for
1050 Result: - pointer to record "index".
1051 -------------------------------------------------------------------------------*/
1052 // make this a macro (for inlining)
1054 UInt8
* GetRecordAddress (BTreeControlBlockPtr btreePtr
,
1060 pos
= (UInt8
*)node
+ GetRecordOffset (btreePtr
, node
, index
);
1068 /*-------------------------------------------------------------------------------
1070 Routine: GetRecordSize - Return size of record "index".
1074 Note: This does not work on the FreeSpace index!
1076 Input: btreePtr - pointer to BTree control block
1077 node - pointer to node that contains the record
1078 index - record to obtain record size for
1080 Result: - size of record "index".
1081 -------------------------------------------------------------------------------*/
1083 UInt16
GetRecordSize (BTreeControlBlockPtr btreePtr
,
1089 pos
= (UInt16
*) ((Ptr
)node
+ btreePtr
->nodeSize
- (index
<< 1) - kOffsetSize
);
1091 return *(pos
-1) - *pos
;
1096 /*-------------------------------------------------------------------------------
1097 Routine: GetOffsetAddress - Return address of offset for record "index".
1101 Input: btreePtr - pointer to BTree control block
1102 node - pointer to node that contains the record
1103 index - record to obtain offset address for
1105 Result: - pointer to offset for record "index".
1106 -------------------------------------------------------------------------------*/
1108 UInt16
*GetOffsetAddress (BTreeControlBlockPtr btreePtr
,
1114 pos
= (Ptr
)node
+ btreePtr
->nodeSize
- (index
<< 1) -2;
1116 return (UInt16
*)pos
;
1121 /*-------------------------------------------------------------------------------
1122 Routine: GetChildNodeNum - Return child node number from index record "index".
1124 Function: Returns the first UInt32 stored after the key for record "index".
1126 Assumes: The node is an Index Node.
1127 The key.length stored at record "index" is ODD. //\80\80 change for variable length index keys
1129 Input: btreePtr - pointer to BTree control block
1130 node - pointer to node that contains the record
1131 index - record to obtain child node number from
1133 Result: - child node number from record "index".
1134 -------------------------------------------------------------------------------*/
1136 UInt32
GetChildNodeNum (BTreeControlBlockPtr btreePtr
,
1137 NodeDescPtr nodePtr
,
1142 pos
= GetRecordAddress (btreePtr
, nodePtr
, index
);
1143 pos
+= CalcKeySize(btreePtr
, (BTreeKey
*) pos
); // key.length + size of length field
1145 return *(UInt32
*)pos
;
1150 /*-------------------------------------------------------------------------------
1151 Routine: InsertOffset - Add an offset and adjust existing offsets by delta.
1153 Function: Add an offset at 'index' by shifting 'index+1' through the last offset
1154 and adjusting them by 'delta', the size of the record to be inserted.
1155 The number of records contained in the node is also incremented.
1157 Input: btreePtr - pointer to BTree control block
1158 node - pointer to node
1159 index - index at which to insert record
1160 delta - size of record to be inserted
1163 -------------------------------------------------------------------------------*/
1165 void InsertOffset (BTreeControlBlockPtr btreePtr
,
1173 src
= GetOffsetAddress (btreePtr
, node
, node
->numRecords
); // point to free offset
1174 dst
= src
- 1; // point to new offset
1175 numOffsets
= node
->numRecords
++ - index
; // subtract index & postincrement
1178 *dst
++ = *src
++ + delta
; // to tricky?
1179 } while (numOffsets
--);
1184 /*-------------------------------------------------------------------------------
1186 Routine: DeleteOffset - Delete an offset.
1188 Function: Delete the offset at 'index' by shifting 'index+1' through the last offset
1189 and adjusting them by the size of the record 'index'.
1190 The number of records contained in the node is also decremented.
1192 Input: btreePtr - pointer to BTree control block
1193 node - pointer to node
1194 index - index at which to delete record
1197 -------------------------------------------------------------------------------*/
1199 void DeleteOffset (BTreeControlBlockPtr btreePtr
,
1207 dst
= GetOffsetAddress (btreePtr
, node
, index
);
1209 delta
= *src
- *dst
;
1210 numOffsets
= --node
->numRecords
- index
; // predecrement numRecords & subtract index
1212 while (numOffsets
--)
1214 *--dst
= *--src
- delta
; // work our way left