2 // lf_hfs_btree_node_ops.c
5 // Created by Yakov Ben Zaken on 22/03/2018.
8 #include "lf_hfs_btrees_private.h"
9 #include "lf_hfs_utils.h"
10 #include "lf_hfs_generic_buf.h"
12 ///////////////////////// BTree Module Node Operations //////////////////////////
14 // GetNode - Call FS Agent to get node
15 // GetNewNode - Call FS Agent to get a new node
16 // ReleaseNode - Call FS Agent to release node obtained by GetNode.
17 // UpdateNode - Mark a node as dirty and call FS Agent to release it.
19 // ClearNode - Clear a node to all zeroes.
21 // InsertRecord - Inserts a record into a BTree node.
22 // InsertKeyRecord - Inserts a key and record pair into a BTree node.
23 // DeleteRecord - Deletes a record from a BTree node.
25 // SearchNode - Return index for record that matches key.
26 // LocateRecord - Return pointer to key and data, and size of data.
28 // GetNodeDataSize - Return the amount of space used for data in the node.
29 // GetNodeFreeSize - Return the amount of free space in the node.
31 // GetRecordOffset - Return the offset for record "index".
32 // GetRecordAddress - Return address of record "index".
33 // GetOffsetAddress - Return address of offset for record "index".
35 // InsertOffset - Inserts a new offset into a node.
36 // DeleteOffset - Deletes an offset from a node.
38 /////////////////////////////////////////////////////////////////////////////////
42 ////////////////////// Routines Internal To BTreeNodeOps.c //////////////////////
44 u_int16_t
GetRecordOffset (BTreeControlBlockPtr btree
,
48 u_int16_t
*GetOffsetAddress (BTreeControlBlockPtr btreePtr
,
52 void InsertOffset (BTreeControlBlockPtr btreePtr
,
57 void DeleteOffset (BTreeControlBlockPtr btreePtr
,
62 /////////////////////////////////////////////////////////////////////////////////
64 #define GetRecordOffset(btreePtr,node,index) (*(short *) ((u_int8_t *)(node) + (btreePtr)->nodeSize - ((index) << 1) - kOffsetSize))
67 /*-------------------------------------------------------------------------------
69 Routine: GetNode - Call FS Agent to get node
71 Function: Gets an existing BTree node from FS Agent and verifies it.
73 Input: btreePtr - pointer to BTree control block
74 nodeNum - number of node to request
76 Output: nodePtr - pointer to beginning of node (nil if error)
81 -------------------------------------------------------------------------------*/
83 OSStatus
GetNode (BTreeControlBlockPtr btreePtr
,
89 GetBlockProcPtr getNodeProc
;
93 // is nodeNum within proper range?
94 if( nodeNum
>= btreePtr
->totalNodes
)
96 LFHFS_LOG(LEVEL_ERROR
, "GetNode:nodeNum [%u] >= totalNodes [%u]",nodeNum
, btreePtr
->totalNodes
);
97 err
= fsBTInvalidNodeErr
;
101 nodePtr
->blockSize
= btreePtr
->nodeSize
; // indicate the size of a node
104 if ( flags
& kGetNodeHint
)
106 options
|= kGetBlockHint
;
109 getNodeProc
= btreePtr
->getBlockProc
;
110 err
= getNodeProc (btreePtr
->fileRefNum
,
117 LFHFS_LOG(LEVEL_ERROR
, "GetNode: getNodeProc returned error.");
120 ++btreePtr
->numGetNodes
;
125 nodePtr
->buffer
= nil
;
126 nodePtr
->blockHeader
= nil
;
133 /*-------------------------------------------------------------------------------
135 Routine: GetNewNode - Call FS Agent to get a new node
137 Function: Gets a new BTree node from FS Agent and initializes it to an empty
140 Input: btreePtr - pointer to BTree control block
141 nodeNum - number of node to request
143 Output: returnNodePtr - pointer to beginning of node (nil if error)
145 Result: noErr - success
147 -------------------------------------------------------------------------------*/
149 OSStatus
GetNewNode (BTreeControlBlockPtr btreePtr
,
151 NodeRec
*returnNodePtr
)
156 GetBlockProcPtr getNodeProc
;
159 //////////////////////// get buffer for new node ////////////////////////////
161 returnNodePtr
->blockSize
= btreePtr
->nodeSize
; // indicate the size of a node
163 getNodeProc
= btreePtr
->getBlockProc
;
164 err
= getNodeProc (btreePtr
->fileRefNum
,
166 kGetBlock
+kGetEmptyBlock
,
171 LFHFS_LOG(LEVEL_ERROR
, "GetNewNode: getNodeProc returned error.");
172 // returnNodePtr->buffer = nil;
175 ++btreePtr
->numGetNewNodes
;
178 ////////////////////////// initialize the node //////////////////////////////
180 node
= returnNodePtr
->buffer
;
182 GenericLFBuf
*psBuf
= returnNodePtr
->blockHeader
;
183 ClearNode (btreePtr
, node
); // clear the node
184 lf_hfs_generic_buf_set_cache_flag(psBuf
, GEN_BUF_LITTLE_ENDIAN
);
186 pos
= (char *)node
+ btreePtr
->nodeSize
- 2; // find address of last offset
187 *(u_int16_t
*)pos
= sizeof (BTNodeDescriptor
); // set offset to beginning of free space
195 /*-------------------------------------------------------------------------------
197 Routine: ReleaseNode - Call FS Agent to release node obtained by GetNode.
199 Function: Informs the FS Agent that a BTree node may be released.
201 Input: btreePtr - pointer to BTree control block
202 nodeNum - number of node to release
204 Result: noErr - success
206 -------------------------------------------------------------------------------*/
208 OSStatus
ReleaseNode (BTreeControlBlockPtr btreePtr
,
212 ReleaseBlockProcPtr releaseNodeProc
;
217 if (nodePtr
->buffer
!= nil
)
219 releaseNodeProc
= btreePtr
->releaseBlockProc
;
220 err
= releaseNodeProc (btreePtr
->fileRefNum
,
225 LFHFS_LOG(LEVEL_ERROR
, "ReleaseNode: releaseNodeProc returned error.");
228 ++btreePtr
->numReleaseNodes
;
231 nodePtr
->buffer
= nil
;
232 nodePtr
->blockHeader
= nil
;
240 /*-------------------------------------------------------------------------------
242 Routine: TrashNode - Call FS Agent to release node obtained by GetNode, and
243 not store it...mark it as bad.
245 Function: Informs the FS Agent that a BTree node may be released and thrown away.
247 Input: btreePtr - pointer to BTree control block
248 nodeNum - number of node to release
250 Result: noErr - success
252 -------------------------------------------------------------------------------*/
254 OSStatus
TrashNode (BTreeControlBlockPtr btreePtr
,
258 ReleaseBlockProcPtr releaseNodeProc
;
263 if (nodePtr
->buffer
!= nil
)
265 releaseNodeProc
= btreePtr
->releaseBlockProc
;
266 err
= releaseNodeProc (btreePtr
->fileRefNum
,
268 kReleaseBlock
| kTrashBlock
);
271 LFHFS_LOG(LEVEL_ERROR
, "TrashNode: releaseNodeProc returned error.");
274 ++btreePtr
->numReleaseNodes
;
277 nodePtr
->buffer
= nil
;
278 nodePtr
->blockHeader
= nil
;
285 /*-------------------------------------------------------------------------------
287 Routine: UpdateNode - Mark a node as dirty and call FS Agent to release it.
289 Function: Marks a BTree node dirty and informs the FS Agent that it may be released.
291 Input: btreePtr - pointer to BTree control block
292 nodeNum - number of node to release
293 transactionID - ID of transaction this node update is a part of
294 flags - special flags to pass to ReleaseNodeProc
296 Result: noErr - success
298 -------------------------------------------------------------------------------*/
300 OSStatus
UpdateNode (BTreeControlBlockPtr btreePtr
,
302 u_int32_t transactionID
,
305 #pragma unused(transactionID)
308 ReleaseBlockProcPtr releaseNodeProc
;
313 if (nodePtr
->buffer
!= nil
) // Why call UpdateNode if nil ?!?
315 releaseNodeProc
= btreePtr
->releaseBlockProc
;
316 err
= releaseNodeProc (btreePtr
->fileRefNum
,
318 flags
| kMarkBlockDirty
);
319 ++btreePtr
->numUpdateNodes
;
322 nodePtr
->buffer
= nil
;
323 nodePtr
->blockHeader
= nil
;
328 /*-------------------------------------------------------------------------------
330 Routine: ClearNode - Clear a node to all zeroes.
332 Function: Writes zeroes from beginning of node for nodeSize bytes.
334 Input: btreePtr - pointer to BTree control block
335 node - pointer to node to clear
338 -------------------------------------------------------------------------------*/
340 void ClearNode (BTreeControlBlockPtr btreePtr
, NodeDescPtr node
)
342 ClearMemory( node
, btreePtr
->nodeSize
);
345 /*-------------------------------------------------------------------------------
347 Routine: InsertRecord - Inserts a record into a BTree node.
351 Note: Record size must be even!
353 Input: btreePtr - pointer to BTree control block
354 node - pointer to node to insert the record
355 index - position record is to be inserted
356 recPtr - pointer to record to insert
358 Result: noErr - success
359 fsBTFullErr - record larger than remaining free space.
360 -------------------------------------------------------------------------------*/
362 Boolean
InsertRecord (BTreeControlBlockPtr btreePtr
,
369 u_int16_t indexOffset
;
370 u_int16_t freeOffset
;
371 u_int16_t bytesToMove
;
375 //// will new record fit in node?
377 freeSpace
= GetNodeFreeSize (btreePtr
, node
);
378 //we could get freeOffset & calc freeSpace
379 if ( freeSpace
< recSize
+ 2)
385 //// make hole for new record
387 indexOffset
= GetRecordOffset (btreePtr
, node
, index
);
388 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
390 src
= ((Ptr
) node
) + indexOffset
;
391 dst
= ((Ptr
) src
) + recSize
;
392 bytesToMove
= freeOffset
- indexOffset
;
394 MoveRecordsRight (src
, dst
, bytesToMove
);
397 //// adjust offsets for moved records
399 InsertOffset (btreePtr
, node
, index
, recSize
);
402 //// move in the new record
404 dst
= ((Ptr
) node
) + indexOffset
;
405 MoveRecordsLeft (recPtr
, dst
, recSize
);
412 /*-------------------------------------------------------------------------------
414 Routine: InsertKeyRecord - Inserts a record into a BTree node.
418 Note: Record size must be even!
420 Input: btreePtr - pointer to BTree control block
421 node - pointer to node to insert the record
422 index - position record is to be inserted
423 keyPtr - pointer to key for record to insert
424 keyLength - length of key (or maxKeyLength)
425 recPtr - pointer to record to insert
426 recSize - number of bytes to copy for record
428 Result: noErr - success
429 fsBTFullErr - record larger than remaining free space.
430 -------------------------------------------------------------------------------*/
432 Boolean
InsertKeyRecord (BTreeControlBlockPtr btreePtr
,
441 u_int16_t indexOffset
;
442 u_int16_t freeOffset
;
443 u_int16_t bytesToMove
;
447 u_int16_t rawKeyLength
;
448 u_int16_t sizeOfLength
;
450 //// calculate actual key size
452 if ( btreePtr
->attributes
& kBTBigKeysMask
)
453 keySize
= keyLength
+ sizeof(u_int16_t
);
455 keySize
= keyLength
+ sizeof(u_int8_t
);
457 if ( M_IsOdd (keySize
) )
458 ++keySize
; // add pad byte
461 //// will new record fit in node?
463 freeSpace
= GetNodeFreeSize (btreePtr
, node
);
464 //we could get freeOffset & calc freeSpace
465 if ( freeSpace
< keySize
+ recSize
+ 2)
471 //// make hole for new record
473 indexOffset
= GetRecordOffset (btreePtr
, node
, index
);
474 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
476 src
= ((u_int8_t
*) node
) + indexOffset
;
477 dst
= ((u_int8_t
*) src
) + keySize
+ recSize
;
478 bytesToMove
= freeOffset
- indexOffset
;
480 MoveRecordsRight (src
, dst
, bytesToMove
);
483 //// adjust offsets for moved records
485 InsertOffset (btreePtr
, node
, index
, keySize
+ recSize
);
490 dst
= ((u_int8_t
*) node
) + indexOffset
;
492 if ( btreePtr
->attributes
& kBTBigKeysMask
)
494 *((u_int16_t
*)dst
) = keyLength
; // use keyLength rather than key.length
495 dst
= (u_int8_t
*) (((u_int16_t
*)dst
) + 1);
496 rawKeyLength
= keyPtr
->length16
;
501 *dst
++ = keyLength
; // use keyLength rather than key.length
502 rawKeyLength
= keyPtr
->length8
;
506 MoveRecordsLeft ( ((u_int8_t
*) keyPtr
) + sizeOfLength
, dst
, rawKeyLength
); // copy key
509 bytesToMove
= keySize
- rawKeyLength
;
511 ClearMemory (dst
+ rawKeyLength
, bytesToMove
); // clear pad bytes in index key
515 //// copy record data
517 dst
= ((u_int8_t
*) node
) + indexOffset
+ keySize
;
518 MoveRecordsLeft (recPtr
, dst
, recSize
);
525 /*-------------------------------------------------------------------------------
527 Routine: DeleteRecord - Deletes a record from a BTree node.
531 Input: btreePtr - pointer to BTree control block
532 node - pointer to node to insert the record
533 index - position record is to be inserted
536 -------------------------------------------------------------------------------*/
538 void DeleteRecord (BTreeControlBlockPtr btreePtr
,
549 //// compress records
550 indexOffset
= GetRecordOffset (btreePtr
, node
, index
);
551 nextOffset
= GetRecordOffset (btreePtr
, node
, index
+ 1);
552 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
554 src
= ((Ptr
) node
) + nextOffset
;
555 dst
= ((Ptr
) node
) + indexOffset
;
556 bytesToMove
= freeOffset
- nextOffset
;
558 MoveRecordsLeft (src
, dst
, bytesToMove
);
560 //// Adjust the offsets
561 DeleteOffset (btreePtr
, node
, index
);
563 /* clear out new free space */
564 bytesToMove
= nextOffset
- indexOffset
;
565 ClearMemory(GetRecordAddress(btreePtr
, node
, node
->numRecords
), bytesToMove
);
571 /*-------------------------------------------------------------------------------
573 Routine: SearchNode - Return index for record that matches key.
575 Function: Returns the record index for the record that matches the search key.
576 If no record was found that matches the search key, the "insert index"
577 of where the record should go is returned instead.
579 Algorithm: A binary search algorithm is used to find the specified key.
581 Input: btreePtr - pointer to BTree control block
582 node - pointer to node that contains the record
583 searchKey - pointer to the key to match
585 Output: index - pointer to beginning of key for record
587 Result: true - success (index = record index)
588 false - key did not match anything in node (index = insert index)
589 -------------------------------------------------------------------------------*/
591 SearchNode( BTreeControlBlockPtr btreePtr
,
594 u_int16_t
*returnIndex
)
602 KeyCompareProcPtr compareProc
= btreePtr
->keyCompareProc
;
605 upperBound
= node
->numRecords
- 1;
606 offset
= (u_int16_t
*) ((u_int8_t
*)(node
) + (btreePtr
)->nodeSize
- kOffsetSize
);
608 while (lowerBound
<= upperBound
) {
609 index
= (lowerBound
+ upperBound
) >> 1;
611 trialKey
= (KeyPtr
) ((u_int8_t
*)node
+ *(offset
- index
));
613 result
= compareProc(searchKey
, trialKey
);
616 upperBound
= index
- 1; /* search < trial */
617 } else if (result
> 0) {
618 lowerBound
= index
+ 1; /* search > trial */
620 *returnIndex
= index
; /* search == trial */
625 *returnIndex
= lowerBound
; /* lowerBound is insert index */
630 /*-------------------------------------------------------------------------------
632 Routine: GetRecordByIndex - Return pointer to key and data, and size of data.
634 Function: Returns a pointer to beginning of key for record, a pointer to the
635 beginning of the data for the record, and the size of the record data
636 (does not include the size of the key).
638 Input: btreePtr - pointer to BTree control block
639 node - pointer to node that contains the record
640 index - index of record to get
642 Output: keyPtr - pointer to beginning of key for record
643 dataPtr - pointer to beginning of data for record
644 dataSize - size of the data portion of the record
647 -------------------------------------------------------------------------------*/
649 OSStatus
GetRecordByIndex (BTreeControlBlockPtr btreePtr
,
654 u_int16_t
*dataSize
)
657 u_int16_t nextOffset
;
661 // Make sure index is valid (in range 0..numRecords-1)
663 if (index
>= node
->numRecords
)
664 return fsBTRecordNotFoundErr
;
667 offset
= GetRecordOffset (btreePtr
, node
, index
);
668 *keyPtr
= (KeyPtr
) ((Ptr
)node
+ offset
);
671 keySize
= CalcKeySize(btreePtr
, *keyPtr
);
672 if ( M_IsOdd (keySize
) )
673 ++keySize
; // add pad byte
675 offset
+= keySize
; // add the key length to find data offset
676 *dataPtr
= (u_int8_t
*) node
+ offset
;
679 nextOffset
= GetRecordOffset (btreePtr
, node
, index
+ 1);
680 *dataSize
= nextOffset
- offset
;
687 /*-------------------------------------------------------------------------------
689 Routine: GetNodeDataSize - Return the amount of space used for data in the node.
691 Function: Gets the size of the data currently contained in a node, excluding
692 the node header. (record data + offset overhead)
694 Input: btreePtr - pointer to BTree control block
695 node - pointer to node that contains the record
697 Result: - number of bytes used for data and offsets in the node.
698 -------------------------------------------------------------------------------*/
700 u_int16_t
GetNodeDataSize (BTreeControlBlockPtr btreePtr
, NodeDescPtr node
)
702 u_int16_t freeOffset
;
704 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
);
706 return freeOffset
+ (node
->numRecords
<< 1) - sizeof (BTNodeDescriptor
);
711 /*-------------------------------------------------------------------------------
713 Routine: GetNodeFreeSize - Return the amount of free space in the node.
717 Input: btreePtr - pointer to BTree control block
718 node - pointer to node that contains the record
720 Result: - number of bytes of free space in the node.
721 -------------------------------------------------------------------------------*/
723 u_int16_t
GetNodeFreeSize (BTreeControlBlockPtr btreePtr
, NodeDescPtr node
)
725 u_int16_t freeOffset
;
727 freeOffset
= GetRecordOffset (btreePtr
, node
, node
->numRecords
); //inline?
729 return btreePtr
->nodeSize
- freeOffset
- (node
->numRecords
<< 1) - kOffsetSize
;
734 /*-------------------------------------------------------------------------------
736 Routine: GetRecordOffset - Return the offset for record "index".
740 Input: btreePtr - pointer to BTree control block
741 node - pointer to node that contains the record
742 index - record to obtain offset for
744 Result: - offset (in bytes) from beginning of node of record specified by index
745 -------------------------------------------------------------------------------*/
746 // make this a macro (for inlining)
748 u_int16_t
GetRecordOffset (BTreeControlBlockPtr btreePtr
,
755 pos
= (u_int8_t
*)node
+ btreePtr
->nodeSize
- (index
<< 1) - kOffsetSize
;
757 return *(short *)pos
;
763 /*-------------------------------------------------------------------------------
765 Routine: GetRecordAddress - Return address of record "index".
769 Input: btreePtr - pointer to BTree control block
770 node - pointer to node that contains the record
771 index - record to obtain offset address for
773 Result: - pointer to record "index".
774 -------------------------------------------------------------------------------*/
775 // make this a macro (for inlining)
777 u_int8_t
* GetRecordAddress (BTreeControlBlockPtr btreePtr
,
783 pos
= (u_int8_t
*)node
+ GetRecordOffset (btreePtr
, node
, index
);
791 /*-------------------------------------------------------------------------------
793 Routine: GetRecordSize - Return size of record "index".
797 Note: This does not work on the FreeSpace index!
799 Input: btreePtr - pointer to BTree control block
800 node - pointer to node that contains the record
801 index - record to obtain record size for
803 Result: - size of record "index".
804 -------------------------------------------------------------------------------*/
806 u_int16_t
GetRecordSize (BTreeControlBlockPtr btreePtr
,
812 pos
= (u_int16_t
*) ((Ptr
)node
+ btreePtr
->nodeSize
- (index
<< 1) - kOffsetSize
);
814 return *(pos
-1) - *pos
;
819 /*-------------------------------------------------------------------------------
820 Routine: GetOffsetAddress - Return address of offset for record "index".
824 Input: btreePtr - pointer to BTree control block
825 node - pointer to node that contains the record
826 index - record to obtain offset address for
828 Result: - pointer to offset for record "index".
829 -------------------------------------------------------------------------------*/
831 u_int16_t
*GetOffsetAddress (BTreeControlBlockPtr btreePtr
,
837 pos
= (Ptr
)node
+ btreePtr
->nodeSize
- (index
<< 1) -2;
839 return (u_int16_t
*)pos
;
844 /*-------------------------------------------------------------------------------
845 Routine: GetChildNodeNum - Return child node number from index record "index".
847 Function: Returns the first u_int32_t stored after the key for record "index".
849 Assumes: The node is an Index Node.
850 The key.length stored at record "index" is ODD. //change for variable length index keys
852 Input: btreePtr - pointer to BTree control block
853 node - pointer to node that contains the record
854 index - record to obtain child node number from
856 Result: - child node number from record "index".
857 -------------------------------------------------------------------------------*/
859 u_int32_t
GetChildNodeNum (BTreeControlBlockPtr btreePtr
,
865 pos
= GetRecordAddress (btreePtr
, nodePtr
, index
);
866 pos
+= CalcKeySize(btreePtr
, (BTreeKey
*) pos
); // key.length + size of length field
868 return *(u_int32_t
*)pos
;
873 /*-------------------------------------------------------------------------------
874 Routine: InsertOffset - Add an offset and adjust existing offsets by delta.
876 Function: Add an offset at 'index' by shifting 'index+1' through the last offset
877 and adjusting them by 'delta', the size of the record to be inserted.
878 The number of records contained in the node is also incremented.
880 Input: btreePtr - pointer to BTree control block
881 node - pointer to node
882 index - index at which to insert record
883 delta - size of record to be inserted
886 -------------------------------------------------------------------------------*/
888 void InsertOffset (BTreeControlBlockPtr btreePtr
,
893 u_int16_t
*src
, *dst
;
894 u_int16_t numOffsets
;
896 src
= GetOffsetAddress (btreePtr
, node
, node
->numRecords
); // point to free offset
897 dst
= src
- 1; // point to new offset
898 numOffsets
= node
->numRecords
++ - index
; // subtract index & postincrement
901 *dst
++ = *src
++ + delta
; // to tricky?
902 } while (numOffsets
--);
907 /*-------------------------------------------------------------------------------
909 Routine: DeleteOffset - Delete an offset.
911 Function: Delete the offset at 'index' by shifting 'index+1' through the last offset
912 and adjusting them by the size of the record 'index'.
913 The number of records contained in the node is also decremented.
915 Input: btreePtr - pointer to BTree control block
916 node - pointer to node
917 index - index at which to delete record
920 -------------------------------------------------------------------------------*/
922 void DeleteOffset (BTreeControlBlockPtr btreePtr
,
926 u_int16_t
*src
, *dst
;
927 u_int16_t numOffsets
;
930 dst
= GetOffsetAddress (btreePtr
, node
, index
);
933 numOffsets
= --node
->numRecords
- index
; // predecrement numRecords & subtract index
937 *--dst
= *--src
- delta
; // work our way left