2 * Copyright (c) 1996-2002 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@
24 #include <sys/kernel.h>
26 #include "../headers/BTreeScanner.h"
28 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
);
29 static int ReadMultipleNodes( BTScanState
*scanState
);
32 //_________________________________________________________________________________
34 // Routine: BTScanNextRecord
36 // Purpose: Return the next leaf record in a scan.
39 // scanState Scanner's current state
40 // avoidIO If true, don't do any I/O to refill the buffer
43 // key Key of found record (points into buffer)
44 // data Data of found record (points into buffer)
45 // dataSize Size of data in found record
48 // noErr Found a valid record
49 // btNotFound No more records
50 // ??? Needed to do I/O to get next node, but avoidIO set
53 // This routine returns pointers to the found record's key and data. It
54 // does not copy the key or data to a caller-supplied buffer (like
55 // GetBTreeRecord would). The caller must not modify the key or data.
56 //_________________________________________________________________________________
58 int BTScanNextRecord( BTScanState
* scanState
,
62 u_int32_t
* dataSize
)
65 u_int16_t dataSizeShort
;
70 // If this is the first call, there won't be any nodes in the buffer, so go
71 // find the first first leaf node (if any).
73 if ( scanState
->nodesLeftInBuffer
== 0 )
75 err
= FindNextLeafNode( scanState
, avoidIO
);
78 while ( err
== noErr
)
80 // See if we have a record in the current node
81 err
= GetRecordByIndex( scanState
->btcb
, scanState
->currentNodePtr
,
82 scanState
->recordNum
, (KeyPtr
*) key
,
83 (UInt8
**) data
, &dataSizeShort
);
87 ++scanState
->recordsFound
;
88 ++scanState
->recordNum
;
90 *dataSize
= dataSizeShort
;
95 // We didn't get the node through the cache, so we can't invalidate it.
96 //XXX Should we do something else to avoid seeing the same record again?
100 // We're done with the current node. See if we've returned all the records
101 if ( scanState
->recordsFound
>= scanState
->btcb
->leafRecords
)
106 // Move to the first record of the next leaf node
107 scanState
->recordNum
= 0;
108 err
= FindNextLeafNode( scanState
, avoidIO
);
112 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
113 // records to be found.
115 if ( err
== fsEndOfIterationErr
)
120 } /* BTScanNextRecord */
123 //_________________________________________________________________________________
125 // Routine: FindNextLeafNode
127 // Purpose: Point to the next leaf node in the buffer. Read more nodes
128 // into the buffer if needed (and allowed).
131 // scanState Scanner's current state
132 // avoidIO If true, don't do any I/O to refill the buffer
135 // noErr Found a valid record
136 // fsEndOfIterationErr No more nodes in file
137 // ??? Needed to do I/O to get next node, but avoidIO set
138 //_________________________________________________________________________________
140 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
)
144 err
= noErr
; // Assume everything will be OK
148 if ( scanState
->nodesLeftInBuffer
== 0 )
150 // Time to read some more nodes into the buffer
153 return fsBTTimeOutErr
;
157 // read some more nodes into buffer
158 err
= ReadMultipleNodes( scanState
);
165 // Adjust the node counters and point to the next node in the buffer
166 ++scanState
->nodeNum
;
167 --scanState
->nodesLeftInBuffer
;
169 // If we've looked at all nodes in the tree, then we're done
170 if ( scanState
->nodeNum
>= scanState
->btcb
->totalNodes
)
171 return fsEndOfIterationErr
;
173 if ( scanState
->nodesLeftInBuffer
== 0 )
175 scanState
->recordNum
= 0;
179 (u_int8_t
*) scanState
->currentNodePtr
+= scanState
->btcb
->nodeSize
;
182 // Make sure this is a valid node
183 if ( CheckNode( scanState
->btcb
, scanState
->currentNodePtr
) != noErr
)
188 if ( scanState
->currentNodePtr
->kind
== kBTLeafNode
)
194 } /* FindNextLeafNode */
197 //_________________________________________________________________________________
199 // Routine: ReadMultipleNodes
201 // Purpose: Read one or more nodes into the buffer.
204 // theScanStatePtr Scanner's current state
207 // noErr One or nodes were read
208 // fsEndOfIterationErr No nodes left in file, none in buffer
209 //_________________________________________________________________________________
211 static int ReadMultipleNodes( BTScanState
*theScanStatePtr
)
214 BTreeControlBlockPtr myBTreeCBPtr
;
215 daddr_t myPhyBlockNum
;
216 u_int32_t myBufferSize
;
217 struct vnode
* myDevPtr
;
219 u_int32_t myBlocksInBufferCount
;
221 // release old buffer if we have one
222 if ( theScanStatePtr
->bufferPtr
!= NULL
)
224 theScanStatePtr
->bufferPtr
->b_flags
|= (B_INVAL
| B_AGE
);
225 brelse( theScanStatePtr
->bufferPtr
);
226 theScanStatePtr
->bufferPtr
= NULL
;
227 theScanStatePtr
->currentNodePtr
= NULL
;
230 myBTreeCBPtr
= theScanStatePtr
->btcb
;
232 // map logical block in catalog btree file to physical block on volume
233 myErr
= VOP_BMAP( myBTreeCBPtr
->fileRefNum
, theScanStatePtr
->nodeNum
,
234 &myDevPtr
, &myPhyBlockNum
, &myBlockRun
);
235 if ( myErr
!= E_NONE
)
237 goto ExitThisRoutine
;
240 // bmap block run gives us the remaining number of valid blocks (number of blocks
241 // minus the first). so if there are 10 valid blocks our run number will be 9.
242 // blocks, in our case is the same as nodes (both are 4K)
243 myBlocksInBufferCount
= (theScanStatePtr
->bufferSize
/ myBTreeCBPtr
->nodeSize
);
244 myBufferSize
= theScanStatePtr
->bufferSize
;
245 if ( (myBlockRun
+ 1) < myBlocksInBufferCount
)
247 myBufferSize
= (myBlockRun
+ 1) * myBTreeCBPtr
->nodeSize
;
250 // now read blocks from the device
251 myErr
= bread( myDevPtr
,
255 &theScanStatePtr
->bufferPtr
);
256 if ( myErr
!= E_NONE
)
258 goto ExitThisRoutine
;
261 theScanStatePtr
->nodesLeftInBuffer
= theScanStatePtr
->bufferPtr
->b_bcount
/ theScanStatePtr
->btcb
->nodeSize
;
262 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) theScanStatePtr
->bufferPtr
->b_data
;
267 } /* ReadMultipleNodes */
271 //_________________________________________________________________________________
273 // Routine: BTScanInitialize
275 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
278 // btreeFile The B-Tree's file control block
279 // startingNode Initial node number
280 // startingRecord Initial record number within node
281 // recordsFound Number of valid records found so far
282 // bufferSize Size (in bytes) of buffer
285 // scanState Scanner's current state; pass to other scanner calls
288 // To begin a new scan and see all records in the B-Tree, pass zeroes for
289 // startingNode, startingRecord, and recordsFound.
291 // To resume a scan from the point of a previous BTScanTerminate, use the
292 // values returned by BTScanTerminate as input for startingNode, startingRecord,
295 // When resuming a scan, the caller should check the B-tree's write count. If
296 // it is different from the write count when the scan was terminated, then the
297 // tree may have changed and the current state may be incorrect. In particular,
298 // you may see some records more than once, or never see some records. Also,
299 // the scanner may not be able to detect when all leaf records have been seen,
300 // and will have to scan through many empty nodes.
302 // XXXĂPerhaps the write count should be managed by BTScanInitialize and
303 // XXX BTScanTerminate? This would avoid the caller having to peek at
304 // XXX internal B-Tree structures.
305 //_________________________________________________________________________________
307 int BTScanInitialize( const FCB
* btreeFile
,
308 u_int32_t startingNode
,
309 u_int32_t startingRecord
,
310 u_int32_t recordsFound
,
311 u_int32_t bufferSize
,
312 BTScanState
* scanState
)
314 BTreeControlBlock
*btcb
;
317 // Make sure this is a valid B-Tree file
319 btcb
= (BTreeControlBlock
*) btreeFile
->fcbBTCBPtr
;
321 return fsBTInvalidFileErr
;
324 // Make sure buffer size is big enough, and a multiple of the
327 if ( bufferSize
< btcb
->nodeSize
)
329 bufferSize
= (bufferSize
/ btcb
->nodeSize
) * btcb
->nodeSize
;
332 // Set up the scanner's state
334 scanState
->bufferSize
= bufferSize
;
335 scanState
->bufferPtr
= NULL
;
336 scanState
->btcb
= btcb
;
337 scanState
->nodeNum
= startingNode
;
338 scanState
->recordNum
= startingRecord
;
339 scanState
->currentNodePtr
= NULL
;
340 scanState
->nodesLeftInBuffer
= 0; // no nodes currently in buffer
341 scanState
->recordsFound
= recordsFound
;
342 scanState
->startTime
= time
; // initialize our throttle
346 } /* BTScanInitialize */
349 //_________________________________________________________________________________
351 // Routine: BTScanTerminate
353 // Purpose: Return state information about a scan so that it can be resumed
354 // later via BTScanInitialize.
357 // scanState Scanner's current state
360 // nextNode Node number to resume a scan (pass to BTScanInitialize)
361 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
362 // recordsFound Valid records seen so far (pass to BTScanInitialize)
363 //_________________________________________________________________________________
365 int BTScanTerminate( BTScanState
* scanState
,
366 u_int32_t
* startingNode
,
367 u_int32_t
* startingRecord
,
368 u_int32_t
* recordsFound
)
370 *startingNode
= scanState
->nodeNum
;
371 *startingRecord
= scanState
->recordNum
;
372 *recordsFound
= scanState
->recordsFound
;
374 if ( scanState
->bufferPtr
!= NULL
)
376 scanState
->bufferPtr
->b_flags
|= (B_INVAL
| B_AGE
);
377 brelse( scanState
->bufferPtr
);
378 scanState
->bufferPtr
= NULL
;
379 scanState
->currentNodePtr
= NULL
;
384 } /* BTScanTerminate */