2 * Copyright (c) 1999 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: Miscellaneous operations for the BTree Module.
28 Version: xxx put the technology version here xxx
30 Written by: Gordon Sheridan and Bill Bruffey
32 Copyright: © 1992-1999 by Apple Computer, Inc., all rights reserved.
35 #include "BTreePrivate.h"
38 ////////////////////////////// Routine Definitions //////////////////////////////
40 /*-------------------------------------------------------------------------------
41 Routine: CalcKeyRecordSize - Return size of combined key/record structure.
43 Function: Rounds keySize and recSize so they will end on word boundaries.
44 Does NOT add size of offset.
46 Input: keySize - length of key (including length field)
47 recSize - length of record data
51 Result: UInt16 - size of combined key/record that will be inserted in btree
52 -------------------------------------------------------------------------------*/
54 UInt16
CalcKeyRecordSize (UInt16 keySize
,
57 if ( M_IsOdd (keySize
) ) keySize
+= 1; // pad byte
59 if (M_IsOdd (recSize
) ) recSize
+= 1; // pad byte
61 return (keySize
+ recSize
);
66 /*-------------------------------------------------------------------------------
67 Routine: VerifyHeader - Validate fields of the BTree header record.
69 Function: Examines the fields of the BTree header record to determine if the
70 fork appears to contain a valid BTree.
72 Input: forkPtr - pointer to fork control block
73 header - pointer to BTree header
76 Result: noErr - success
78 -------------------------------------------------------------------------------*/
80 OSStatus
VerifyHeader (SFCB
*filePtr
,
87 switch (header
->nodeSize
) // node size == 512*2^n
96 default: return fsBTInvalidHeaderErr
; //¥¥ E_BadNodeType
99 totalNodes
= header
->totalNodes
;
101 forkSize
= totalNodes
* header
->nodeSize
;
103 if ( forkSize
!= filePtr
->fcbLogicalSize
)
104 return fsBTInvalidHeaderErr
;
106 if ( header
->freeNodes
>= totalNodes
)
107 return fsBTInvalidHeaderErr
;
109 if ( header
->rootNode
>= totalNodes
)
110 return fsBTInvalidHeaderErr
;
112 if ( header
->firstLeafNode
>= totalNodes
)
113 return fsBTInvalidHeaderErr
;
115 if ( header
->lastLeafNode
>= totalNodes
)
116 return fsBTInvalidHeaderErr
;
118 if ( header
->treeDepth
> kMaxTreeDepth
)
119 return fsBTInvalidHeaderErr
;
122 /////////////////////////// Check BTree Type ////////////////////////////////
124 switch (header
->btreeType
)
126 case 0: // HFS Type - no Key Descriptor
127 case kUserBTreeType
: // with Key Descriptors etc.
128 case kReservedBTreeType
: // Desktop Mgr BTree ?
131 default: return fsBTUnknownVersionErr
;
139 /*-------------------------------------------------------------------------------
140 Routine: UpdateHeader - Write BTreeInfoRec fields to Header node.
142 Function: Checks the kBTHeaderDirty flag in the BTreeInfoRec and updates the
143 header node if necessary.
145 Input: btreePtr - pointer to BTreeInfoRec
148 Result: noErr - success
150 -------------------------------------------------------------------------------*/
152 OSStatus
UpdateHeader (BTreeControlBlockPtr btreePtr
)
155 BlockDescriptor node
;
159 if ((btreePtr
->flags
& kBTHeaderDirty
) == 0) // btree info already flushed
163 err
= GetNode (btreePtr
, kHeaderNodeNum
, &node
);
167 header
= (BTHeaderRec
*) (node
.buffer
+ sizeof(BTNodeDescriptor
));
169 header
->treeDepth
= btreePtr
->treeDepth
;
170 header
->rootNode
= btreePtr
->rootNode
;
171 header
->leafRecords
= btreePtr
->leafRecords
;
172 header
->firstLeafNode
= btreePtr
->firstLeafNode
;
173 header
->lastLeafNode
= btreePtr
->lastLeafNode
;
174 header
->nodeSize
= btreePtr
->nodeSize
; //¥¥ this shouldn't change
175 header
->maxKeyLength
= btreePtr
->maxKeyLength
; //¥¥ neither should this
176 header
->totalNodes
= btreePtr
->totalNodes
;
177 header
->freeNodes
= btreePtr
->freeNodes
;
178 header
->btreeType
= btreePtr
->btreeType
;
180 // ignore header->clumpSize; //¥¥ rename this field?
182 err
= UpdateNode (btreePtr
, &node
);
184 btreePtr
->flags
&= (~kBTHeaderDirty
);
191 /*-------------------------------------------------------------------------------
192 Routine: FindIteratorPosition - One_line_description.
194 Function: Brief_description_of_the_function_and_any_side_effects
196 Algorithm: see FSC.BT.BTIterateRecord.PICT
198 Note: //¥¥ document side-effects of bad node hints
200 Input: btreePtr - description
201 iterator - description
204 Output: iterator - description
208 nodeNum - description
209 returnIndex - description
210 foundRecord - description
213 Result: noErr - success
215 -------------------------------------------------------------------------------*/
217 OSStatus
FindIteratorPosition (BTreeControlBlockPtr btreePtr
,
218 BTreeIteratorPtr iterator
,
219 BlockDescriptor
*left
,
220 BlockDescriptor
*middle
,
221 BlockDescriptor
*right
,
222 UInt32
*returnNodeNum
,
224 Boolean
*foundRecord
)
229 UInt16 leftIndex
, index
, rightIndex
;
232 // assume btreePtr valid
233 // assume left, middle, right point to BlockDescriptors
234 // assume nodeNum points to UInt32
235 // assume index points to UInt16
236 // assume foundRecord points to Boolean
239 middle
->buffer
= nil
;
244 if (iterator
== nil
) // do we have an iterator?
246 err
= fsBTInvalidIteratorErr
;
250 #if SupportsKeyDescriptors
251 //¥¥ verify iterator key (change CheckKey to take btreePtr instead of keyDescPtr?)
252 if (btreePtr
->keyDescPtr
!= nil
)
254 err
= CheckKey (&iterator
->key
, btreePtr
->keyDescPtr
, btreePtr
->maxKeyLength
);
259 err
= IsItAHint (btreePtr
, iterator
, &validHint
);
262 nodeNum
= iterator
->hint
.nodeNum
;
263 if (! validHint
) // does the hint appear to be valid?
268 err
= GetNode (btreePtr
, nodeNum
, middle
);
269 if( err
== fsBTInvalidNodeErr
) // returned if nodeNum is out of range
274 if ( ((NodeDescPtr
) middle
->buffer
)->kind
!= kBTLeafNode
||
275 ((NodeDescPtr
) middle
->buffer
)->numRecords
<= 0 )
280 ++btreePtr
->numValidHints
;
282 foundIt
= SearchNode (btreePtr
, middle
->buffer
, &iterator
->key
, &index
);
290 if (((NodeDescPtr
) middle
->buffer
)->bLink
== 0) // before 1st btree record
295 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->bLink
;
297 err
= GetLeftSiblingNode (btreePtr
, middle
->buffer
, left
);
300 if ( ((NodeDescPtr
) left
->buffer
)->kind
!= kBTLeafNode
||
301 ((NodeDescPtr
) left
->buffer
)->numRecords
<= 0 )
306 foundIt
= SearchNode (btreePtr
, left
->buffer
, &iterator
->key
, &leftIndex
);
317 if (leftIndex
== 0) // we're lost!
321 else if (leftIndex
>= ((NodeDescPtr
) left
->buffer
)->numRecords
)
323 nodeNum
= ((NodeDescPtr
) left
->buffer
)->fLink
;
325 PanicIf (index
!= 0, "\pFindIteratorPosition: index != 0"); //¥¥ just checking...
338 else if (index
>= ((NodeDescPtr
) middle
->buffer
)->numRecords
)
340 if (((NodeDescPtr
) middle
->buffer
)->fLink
== 0) // beyond last record
345 nodeNum
= ((NodeDescPtr
) middle
->buffer
)->fLink
;
347 err
= GetRightSiblingNode (btreePtr
, middle
->buffer
, right
);
350 if ( ((NodeDescPtr
) right
->buffer
)->kind
!= kBTLeafNode
||
351 ((NodeDescPtr
) right
->buffer
)->numRecords
<= 0 )
356 foundIt
= SearchNode (btreePtr
, right
->buffer
, &iterator
->key
, &rightIndex
);
357 if (rightIndex
>= ((NodeDescPtr
) right
->buffer
)->numRecords
) // we're lost
361 else // we found it, or rightIndex==0, or rightIndex<numRecs
373 //////////////////////////// Search The Tree ////////////////////////////////
377 TreePathTable treePathTable
; // so we only use stack space if we need to
379 err
= ReleaseNode (btreePtr
, left
); M_ExitOnError (err
);
380 err
= ReleaseNode (btreePtr
, middle
); M_ExitOnError (err
);
381 err
= ReleaseNode (btreePtr
, right
); M_ExitOnError (err
);
383 err
= SearchTree ( btreePtr
, &iterator
->key
, treePathTable
, &nodeNum
, middle
, &index
);
384 switch (err
) //¥¥ separate find condition from exceptions
386 case noErr
: foundIt
= true; break;
387 case fsBTRecordNotFoundErr
: break;
388 default: goto ErrorExit
;
392 /////////////////////////////// Success! ////////////////////////////////////
396 *returnNodeNum
= nodeNum
;
397 *returnIndex
= index
;
398 *foundRecord
= foundIt
;
403 ////////////////////////////// Error Exit ///////////////////////////////////
407 (void) ReleaseNode (btreePtr
, left
);
408 (void) ReleaseNode (btreePtr
, middle
);
409 (void) ReleaseNode (btreePtr
, right
);
413 *foundRecord
= false;
420 /////////////////////////////// CheckInsertParams ///////////////////////////////
422 OSStatus
CheckInsertParams (SFCB
*filePtr
,
423 BTreeIterator
*iterator
,
424 FSBufferDescriptor
*record
,
427 BTreeControlBlockPtr btreePtr
;
429 if (filePtr
== nil
) return paramErr
;
431 btreePtr
= (BTreeControlBlockPtr
) filePtr
->fcbBtree
;
432 if (btreePtr
== nil
) return fsBTInvalidFileErr
;
433 if (iterator
== nil
) return paramErr
;
434 if (record
== nil
) return paramErr
;
436 #if SupportsKeyDescriptors
437 if (btreePtr
->keyDescPtr
!= nil
)
441 err
= CheckKey (&iterator
->key
, btreePtr
->keyDescPtr
, btreePtr
->maxKeyLength
);
447 // check total key/record size limit
448 if ( CalcKeyRecordSize (CalcKeySize(btreePtr
, &iterator
->key
), recordLen
) > (btreePtr
->nodeSize
>> 1))
449 return fsBTRecordTooLargeErr
;
456 /*-------------------------------------------------------------------------------
457 Routine: TrySimpleReplace - Attempts a simple insert, set, or replace.
459 Function: If a hint exitst for the iterator, attempt to find the key in the hint
460 node. If the key is found, an insert operation fails. If the is not
461 found, a replace operation fails. If the key was not found, and the
462 insert position is greater than 0 and less than numRecords, the record
463 is inserted, provided there is enough freeSpace. If the key was found,
464 and there is more freeSpace than the difference between the new record
465 and the old record, the old record is deleted and the new record is
468 Assumptions: iterator key has already been checked by CheckKey
471 Input: btreePtr - description
472 iterator - description
474 recordLen - description
475 operation - description
478 Output: recordInserted - description
481 Result: noErr - success
482 E_RecordExits - insert operation failure
483 != noErr - GetNode, ReleaseNode, UpdateNode returned an error
484 -------------------------------------------------------------------------------*/
486 OSStatus
TrySimpleReplace (BTreeControlBlockPtr btreePtr
,
488 BTreeIterator
*iterator
,
489 FSBufferDescriptor
*record
,
491 Boolean
*recordInserted
)
501 *recordInserted
= false; // we'll assume this won't work...
503 if ( nodePtr
->kind
!= kBTLeafNode
)
504 return noErr
; // we're in the weeds!
506 foundIt
= SearchNode (btreePtr
, nodePtr
, &iterator
->key
, &index
);
508 if ( foundIt
== false )
509 return noErr
; // we might be lost...
511 keySize
= CalcKeySize(btreePtr
, &iterator
->key
); // includes length field
513 spaceNeeded
= CalcKeyRecordSize (keySize
, recordLen
);
515 oldSpace
= GetRecordSize (btreePtr
, nodePtr
, index
);
517 if ( spaceNeeded
== oldSpace
)
521 dst
= GetRecordAddress (btreePtr
, nodePtr
, index
);
523 if ( M_IsOdd (keySize
) )
524 ++keySize
; // add pad byte
526 dst
+= keySize
; // skip over key to point at record
528 CopyMemory(record
->bufferAddress
, dst
, recordLen
); // blast away...
530 *recordInserted
= true;
532 else if ( (GetNodeFreeSize(btreePtr
, nodePtr
) + oldSpace
) >= spaceNeeded
)
534 DeleteRecord (btreePtr
, nodePtr
, index
);
536 didItFit
= InsertKeyRecord (btreePtr
, nodePtr
, index
,
537 &iterator
->key
, KeyLength(btreePtr
, &iterator
->key
),
538 record
->bufferAddress
, recordLen
);
539 PanicIf (didItFit
== false, "\pTrySimpleInsert: InsertKeyRecord returned false!");
541 *recordInserted
= true;
543 // else not enough space...
549 /*-------------------------------------------------------------------------------
550 Routine: IsItAHint - checks the hint within a BTreeInterator.
552 Function: checks the hint within a BTreeInterator. If it is non-zero, it may
555 Input: btreePtr - pointer to control block for BTree file
556 iterator - pointer to BTreeIterator
558 Output: answer - true if the hint looks reasonable
559 - false if the hint is 0
561 Result: noErr - success
562 -------------------------------------------------------------------------------*/
565 OSStatus
IsItAHint (BTreeControlBlockPtr btreePtr
, BTreeIterator
*iterator
, Boolean
*answer
)
567 ++btreePtr
->numHintChecks
;
569 //¥¥ shouldn't we do a range check?
570 if (iterator
->hint
.nodeNum
== 0)
577 ++btreePtr
->numPossibleHints
;