2 * Copyright (c) 2000-2003 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: Miscellaneous 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
47 Change History (most recent first):
49 <MOSXS> 6/1/99 djb Sync up with Mac OS 8.6.
50 <CS2> 9/4/97 djb Optimize TrySimpleReplace for the case where record size is not
52 <CS1> 4/23/97 djb first checked in
54 <HFS7> 3/31/97 djb Move ClearMemory to Utilities.c.
55 <HFS6> 3/17/97 DSH Casting for DFA
56 <HFS5> 2/27/97 msd Remove temporary fix from last revision. BTree EOF's should be
57 correct now, so check for strict equality.
58 <HFS4> 2/26/97 msd Fix a casting problem in ClearMemory. TEMPORARY FIX: Made
59 VerifyHeader more lenient, allowing the EOF to be greater than
60 the amount actually used by nodes; this should really be fixed
61 in the formatting code (which needs to compute the real BTree
62 sizes before writing the volume header).
63 <HFS3> 2/19/97 djb Added ClearMemory. Changed CalcKeyLength to KeyLength.
64 <HFS2> 1/3/97 djb Added support for large keys.
65 <HFS1> 12/19/96 djb first checked in
67 History applicable to original Scarecrow Design:
69 <9> 10/25/96 ser Changing for new VFPI
70 <8> 10/18/96 ser Converting over VFPI changes
71 <7> 9/17/96 dkh More BTree statistics. Change IsItAHint to not always check to
72 see if the hint node is allocated.
73 <6> 9/16/96 dkh Revised BTree statistics.
74 <5> 6/20/96 dkh Radar #1358740. Change from using Pools to debug MemAllocators.
75 <4> 1/22/96 dkh Change Pools.i inclusion to PoolsPriv.i
76 <3> 1/10/96 msd Change 64-bit math to use real function names from Math64.i.
77 <2> 12/7/95 dkh D10E2 build. Changed usage of Ref data type to LogicalAddress.
78 <1> 10/18/95 rst Moved from Scarecrow project.
80 <19> 4/26/95 prp In UpdateHeader, clear the dirty flag after the BTree is updated.
81 <18> 1/12/95 wjk Adopt Model FileSystem changes in D5.
82 <17> 11/16/94 prp Add IsItAHint routine and use it whenever hint's node number was
84 <16> 10/5/94 bk add pools.h include file
85 <15> 9/30/94 prp Get in sync with D2 interface changes.
86 <14> 7/22/94 wjk Convert to the new set of header files.
87 <13> 12/2/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
89 <12> 11/30/93 wjk Move from Makefiles to BuildFiles. Fit into the ModernOS and
91 <11> 11/23/93 wjk Changes required to compile on the RS6000.
92 <10> 8/31/93 prp Use U64SetU instead of S64Set.
93 <9> 6/2/93 gs Update for changes to FSErrors.h and add some comments.
94 <8> 5/21/93 gs Modify UpdateHeader to write out attributes. Remove
95 Get/UpdateNode from TrySimpleReplace.
96 <7> 5/10/93 gs Add TrySimpleReplace routine.
97 <6> 3/23/93 gs Change MoveData to take void * instead of Ptr. Add UpdateHeader
98 and ClearBytes routines.
99 <5> 2/8/93 gs Add FindIteratorPosition.
100 <4> 12/10/92 gs Implement CheckKeyDescriptor and the KeyDescriptor interpreter.
101 <3> 12/8/92 gs Add GetKeyDescriptor, VerifyHeader, and Alloc/Dealloc memory
103 <2> 12/2/92 gs Add CompareKeys routine.
104 <1> 11/15/92 gs first checked in
108 #include "../headers/BTreesPrivate.h"
111 ////////////////////////////// Routine Definitions //////////////////////////////
113 /*-------------------------------------------------------------------------------
114 Routine: CalcKeyRecordSize - Return size of combined key/record structure.
116 Function: Rounds keySize and recSize so they will end on word boundaries.
117 Does NOT add size of offset.
119 Input: keySize - length of key (including length field)
120 recSize - length of record data
124 Result: UInt16 - size of combined key/record that will be inserted in btree
125 -------------------------------------------------------------------------------*/
127 UInt16
CalcKeyRecordSize (UInt16 keySize
,
130 if ( M_IsOdd (keySize
) ) keySize
+= 1; // pad byte
132 if (M_IsOdd (recSize
) ) recSize
+= 1; // pad byte
134 return (keySize
+ recSize
);
139 /*-------------------------------------------------------------------------------
140 Routine: VerifyHeader - Validate fields of the BTree header record.
142 Function: Examines the fields of the BTree header record to determine if the
143 fork appears to contain a valid BTree.
145 Input: forkPtr - pointer to fork control block
146 header - pointer to BTree header
149 Result: noErr - success
151 -------------------------------------------------------------------------------*/
153 OSStatus
VerifyHeader (FCB
*filePtr
,
154 BTHeaderRec
*header
)
160 switch (header
->nodeSize
) // node size == 512*2^n
169 default: return fsBTInvalidHeaderErr
; //\80\80 E_BadNodeType
172 totalNodes
= header
->totalNodes
;
174 forkSize
= (UInt64
)totalNodes
* (UInt64
)header
->nodeSize
;
176 if ( forkSize
> filePtr
->fcbEOF
)
177 return fsBTInvalidHeaderErr
;
179 if ( header
->freeNodes
>= totalNodes
)
180 return fsBTInvalidHeaderErr
;
182 if ( header
->rootNode
>= totalNodes
)
183 return fsBTInvalidHeaderErr
;
185 if ( header
->firstLeafNode
>= totalNodes
)
186 return fsBTInvalidHeaderErr
;
188 if ( header
->lastLeafNode
>= totalNodes
)
189 return fsBTInvalidHeaderErr
;
191 if ( header
->treeDepth
> kMaxTreeDepth
)
192 return fsBTInvalidHeaderErr
;
195 /////////////////////////// Check BTree Type ////////////////////////////////
197 switch (header
->btreeType
)
199 case 0: // HFS Type - no Key Descriptor
200 case kUserBTreeType
: // with Key Descriptors etc.
201 case kReservedBTreeType
: // Desktop Mgr BTree ?
204 default: return fsBTUnknownVersionErr
;
213 OSStatus
TreeIsDirty(BTreeControlBlockPtr btreePtr
)
215 return (btreePtr
->flags
& kBTHeaderDirty
);
220 /*-------------------------------------------------------------------------------
221 Routine: UpdateHeader - Write BTreeInfoRec fields to Header node.
223 Function: Checks the kBTHeaderDirty flag in the BTreeInfoRec and updates the
224 header node if necessary.
226 Input: btreePtr - pointer to BTreeInfoRec
229 Result: noErr - success
231 -------------------------------------------------------------------------------*/
233 OSStatus
UpdateHeader(BTreeControlBlockPtr btreePtr
, Boolean forceWrite
)
236 BlockDescriptor node
;
240 if ((btreePtr
->flags
& kBTHeaderDirty
) == 0) // btree info already flushed
244 err
= GetNode (btreePtr
, kHeaderNodeNum
, &node
);
250 ModifyBlockStart(btreePtr
->fileRefNum
, &node
);
252 header
= (BTHeaderRec
*) ((char *)node
.buffer
+ sizeof(BTNodeDescriptor
));
254 header
->treeDepth
= btreePtr
->treeDepth
;
255 header
->rootNode
= btreePtr
->rootNode
;
256 header
->leafRecords
= btreePtr
->leafRecords
;
257 header
->firstLeafNode
= btreePtr
->firstLeafNode
;
258 header
->lastLeafNode
= btreePtr
->lastLeafNode
;
259 header
->nodeSize
= btreePtr
->nodeSize
; //\80\80 this shouldn't change
260 header
->maxKeyLength
= btreePtr
->maxKeyLength
; //\80\80 neither should this
261 header
->totalNodes
= btreePtr
->totalNodes
;
262 header
->freeNodes
= btreePtr
->freeNodes
;
263 header
->btreeType
= btreePtr
->btreeType
;
265 // ignore header->clumpSize; //\80\80 rename this field?
268 options
= kForceWriteBlock
;
270 options
= kLockTransaction
;
272 err
= UpdateNode (btreePtr
, &node
, 0, options
);
274 btreePtr
->flags
&= (~kBTHeaderDirty
);
281 /*-------------------------------------------------------------------------------
282 Routine: FindIteratorPosition - One_line_description.
284 Function: Brief_description_of_the_function_and_any_side_effects
286 Algorithm: see FSC.BT.BTIterateRecord.PICT
288 Note: //\80\80 document side-effects of bad node hints
290 Input: btreePtr - description
291 iterator - description
294 Output: iterator - description
298 nodeNum - description
299 returnIndex - description
300 foundRecord - description
303 Result: noErr - success
305 -------------------------------------------------------------------------------*/
307 OSStatus
FindIteratorPosition (BTreeControlBlockPtr btreePtr
,
308 BTreeIteratorPtr iterator
,
309 BlockDescriptor
*left
,
310 BlockDescriptor
*middle
,
311 BlockDescriptor
*right
,
312 UInt32
*returnNodeNum
,
314 Boolean
*foundRecord
)
319 UInt16 leftIndex
, index
, rightIndex
;
322 // assume btreePtr valid
323 // assume left, middle, right point to BlockDescriptors
324 // assume nodeNum points to UInt32
325 // assume index points to UInt16
326 // assume foundRecord points to Boolean
329 left
->blockHeader
= nil
;
330 middle
->buffer
= nil
;
331 middle
->blockHeader
= nil
;
333 right
->blockHeader
= nil
;
337 if (iterator
== nil
) // do we have an iterator?
339 err
= fsBTInvalidIteratorErr
;
343 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
346 nodeNum
= iterator
->hint
.nodeNum
;
347 if (! validHint
) // does the hint appear to be valid?
352 err
= GetNode (btreePtr
, nodeNum
, middle
);
353 if( err
== fsBTInvalidNodeErr
) // returned if nodeNum is out of range
358 if ( ((NodeDescPtr
) middle
->buffer
)->kind
!= kBTLeafNode
||
359 ((NodeDescPtr
) middle
->buffer
)->numRecords
<= 0 )
364 ++btreePtr
->numValidHints
;
366 foundIt
= SearchNode (btreePtr
, middle
->buffer
, &iterator
->key
, &index
);
374 if (((NodeDescPtr
) middle
->buffer
)->bLink
== 0) // before 1st btree record
379 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->bLink
;
381 err
= GetLeftSiblingNode (btreePtr
, middle
->buffer
, left
);
384 if ( ((NodeDescPtr
) left
->buffer
)->kind
!= kBTLeafNode
||
385 ((NodeDescPtr
) left
->buffer
)->numRecords
<= 0 )
390 foundIt
= SearchNode (btreePtr
, left
->buffer
, &iterator
->key
, &leftIndex
);
401 if (leftIndex
== 0) // we're lost!
405 else if (leftIndex
>= ((NodeDescPtr
) left
->buffer
)->numRecords
)
407 nodeNum
= ((NodeDescPtr
) left
->buffer
)->fLink
;
409 PanicIf (index
!= 0, "\pFindIteratorPosition: index != 0"); //\80\80 just checking...
422 else if (index
>= ((NodeDescPtr
) middle
->buffer
)->numRecords
)
424 if (((NodeDescPtr
) middle
->buffer
)->fLink
== 0) // beyond last record
429 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->fLink
;
431 err
= GetRightSiblingNode (btreePtr
, middle
->buffer
, right
);
434 if ( ((NodeDescPtr
) right
->buffer
)->kind
!= kBTLeafNode
||
435 ((NodeDescPtr
) right
->buffer
)->numRecords
<= 0 )
440 foundIt
= SearchNode (btreePtr
, right
->buffer
, &iterator
->key
, &rightIndex
);
441 if (rightIndex
>= ((NodeDescPtr
) right
->buffer
)->numRecords
) // we're lost
445 else // we found it, or rightIndex==0, or rightIndex<numRecs
457 //////////////////////////// Search The Tree ////////////////////////////////
461 TreePathTable treePathTable
; // so we only use stack space if we need to
463 err
= ReleaseNode (btreePtr
, left
); M_ExitOnError (err
);
464 err
= ReleaseNode (btreePtr
, middle
); M_ExitOnError (err
);
465 err
= ReleaseNode (btreePtr
, right
); M_ExitOnError (err
);
467 err
= SearchTree ( btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, middle
, &index
);
468 switch (err
) //\80\80 separate find condition from exceptions
470 case noErr
: foundIt
= true; break;
471 case fsBTRecordNotFoundErr
: break;
472 default: goto ErrorExit
;
476 /////////////////////////////// Success! ////////////////////////////////////
480 *returnNodeNum
= nodeNum
;
481 *returnIndex
= index
;
482 *foundRecord
= foundIt
;
487 ////////////////////////////// Error Exit ///////////////////////////////////
491 (void) ReleaseNode (btreePtr
, left
);
492 (void) ReleaseNode (btreePtr
, middle
);
493 (void) ReleaseNode (btreePtr
, right
);
497 *foundRecord
= false;
504 /////////////////////////////// CheckInsertParams ///////////////////////////////
506 OSStatus
CheckInsertParams (FCB
*filePtr
,
507 BTreeIterator
*iterator
,
508 FSBufferDescriptor
*record
,
511 BTreeControlBlockPtr btreePtr
;
513 if (filePtr
== nil
) return paramErr
;
515 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBTCBPtr
;
516 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
517 if (iterator
== nil
) return paramErr
;
518 if (record
== nil
) return paramErr
;
520 // check total key/record size limit
521 if ( CalcKeyRecordSize (CalcKeySize(btreePtr
, &iterator
->key
), recordLen
) > (btreePtr
->nodeSize
>> 1))
522 return fsBTRecordTooLargeErr
;
529 /*-------------------------------------------------------------------------------
530 Routine: TrySimpleReplace - Attempts a simple insert, set, or replace.
532 Function: If a hint exitst for the iterator, attempt to find the key in the hint
533 node. If the key is found, an insert operation fails. If the is not
534 found, a replace operation fails. If the key was not found, and the
535 insert position is greater than 0 and less than numRecords, the record
536 is inserted, provided there is enough freeSpace. If the key was found,
537 and there is more freeSpace than the difference between the new record
538 and the old record, the old record is deleted and the new record is
541 Assumptions: iterator key has already been checked by CheckKey
544 Input: btreePtr - description
545 iterator - description
547 recordLen - description
548 operation - description
551 Output: recordInserted - description
554 Result: noErr - success
555 E_RecordExits - insert operation failure
556 != noErr - GetNode, ReleaseNode, UpdateNode returned an error
557 -------------------------------------------------------------------------------*/
559 OSStatus
TrySimpleReplace (BTreeControlBlockPtr btreePtr
,
561 BTreeIterator
*iterator
,
562 FSBufferDescriptor
*record
,
564 Boolean
*recordInserted
)
574 *recordInserted
= false; // we'll assume this won't work...
576 if ( nodePtr
->kind
!= kBTLeafNode
)
577 return noErr
; // we're in the weeds!
579 foundIt
= SearchNode (btreePtr
, nodePtr
, &iterator
->key
, &index
);
581 if ( foundIt
== false )
582 return noErr
; // we might be lost...
584 keySize
= CalcKeySize(btreePtr
, &iterator
->key
); // includes length field
586 spaceNeeded
= CalcKeyRecordSize (keySize
, recordLen
);
588 oldSpace
= GetRecordSize (btreePtr
, nodePtr
, index
);
590 if ( spaceNeeded
== oldSpace
)
594 dst
= GetRecordAddress (btreePtr
, nodePtr
, index
);
596 if ( M_IsOdd (keySize
) )
597 ++keySize
; // add pad byte
599 dst
+= keySize
; // skip over key to point at record
601 BlockMoveData(record
->bufferAddress
, dst
, recordLen
); // blast away...
603 *recordInserted
= true;
605 else if ( (GetNodeFreeSize(btreePtr
, nodePtr
) + oldSpace
) >= spaceNeeded
)
607 DeleteRecord (btreePtr
, nodePtr
, index
);
609 didItFit
= InsertKeyRecord (btreePtr
, nodePtr
, index
,
610 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
611 record
->bufferAddress
, recordLen
);
612 PanicIf (didItFit
== false, "\pTrySimpleInsert: InsertKeyRecord returned false!");
614 *recordInserted
= true;
616 // else not enough space...
622 /*-------------------------------------------------------------------------------
623 Routine: IsItAHint - checks the hint within a BTreeInterator.
625 Function: checks the hint within a BTreeInterator. If it is non-zero, it may
628 Input: btreePtr - pointer to control block for BTree file
629 iterator - pointer to BTreeIterator
631 Output: answer - true if the hint looks reasonable
632 - false if the hint is 0
634 Result: noErr - success
635 -------------------------------------------------------------------------------*/
638 OSStatus
IsItAHint (BTreeControlBlockPtr btreePtr
, BTreeIterator
*iterator
, Boolean
*answer
)
640 ++btreePtr
->numHintChecks
;
643 if (iterator
->hint
.nodeNum
>= btreePtr
->totalNodes
)
649 if (iterator
->hint
.nodeNum
== 0)
656 ++btreePtr
->numPossibleHints
;