2 * Copyright (c) 1996-2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/kernel.h>
29 #include "../headers/BTreeScanner.h"
31 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
);
32 static int ReadMultipleNodes( BTScanState
*scanState
);
35 //_________________________________________________________________________________
37 // Routine: BTScanNextRecord
39 // Purpose: Return the next leaf record in a scan.
42 // scanState Scanner's current state
43 // avoidIO If true, don't do any I/O to refill the buffer
46 // key Key of found record (points into buffer)
47 // data Data of found record (points into buffer)
48 // dataSize Size of data in found record
51 // noErr Found a valid record
52 // btNotFound No more records
53 // ??? Needed to do I/O to get next node, but avoidIO set
56 // This routine returns pointers to the found record's key and data. It
57 // does not copy the key or data to a caller-supplied buffer (like
58 // GetBTreeRecord would). The caller must not modify the key or data.
59 //_________________________________________________________________________________
61 int BTScanNextRecord( BTScanState
* scanState
,
65 u_int32_t
* dataSize
)
68 u_int16_t dataSizeShort
;
73 // If this is the first call, there won't be any nodes in the buffer, so go
74 // find the first first leaf node (if any).
76 if ( scanState
->nodesLeftInBuffer
== 0 )
78 err
= FindNextLeafNode( scanState
, avoidIO
);
81 while ( err
== noErr
)
83 // See if we have a record in the current node
84 err
= GetRecordByIndex( scanState
->btcb
, scanState
->currentNodePtr
,
85 scanState
->recordNum
, (KeyPtr
*) key
,
86 (UInt8
**) data
, &dataSizeShort
);
90 ++scanState
->recordsFound
;
91 ++scanState
->recordNum
;
93 *dataSize
= dataSizeShort
;
98 // We didn't get the node through the cache, so we can't invalidate it.
99 //XXX Should we do something else to avoid seeing the same record again?
103 // We're done with the current node. See if we've returned all the records
104 if ( scanState
->recordsFound
>= scanState
->btcb
->leafRecords
)
109 // Move to the first record of the next leaf node
110 scanState
->recordNum
= 0;
111 err
= FindNextLeafNode( scanState
, avoidIO
);
115 // If we got an EOF error from FindNextLeafNode, then there are no more leaf
116 // records to be found.
118 if ( err
== fsEndOfIterationErr
)
123 } /* BTScanNextRecord */
126 //_________________________________________________________________________________
128 // Routine: FindNextLeafNode
130 // Purpose: Point to the next leaf node in the buffer. Read more nodes
131 // into the buffer if needed (and allowed).
134 // scanState Scanner's current state
135 // avoidIO If true, don't do any I/O to refill the buffer
138 // noErr Found a valid record
139 // fsEndOfIterationErr No more nodes in file
140 // ??? Needed to do I/O to get next node, but avoidIO set
141 //_________________________________________________________________________________
143 static int FindNextLeafNode( BTScanState
*scanState
, Boolean avoidIO
)
147 err
= noErr
; // Assume everything will be OK
151 if ( scanState
->nodesLeftInBuffer
== 0 )
153 // Time to read some more nodes into the buffer
156 return fsBTTimeOutErr
;
160 // read some more nodes into buffer
161 err
= ReadMultipleNodes( scanState
);
168 // Adjust the node counters and point to the next node in the buffer
169 ++scanState
->nodeNum
;
170 --scanState
->nodesLeftInBuffer
;
172 // If we've looked at all nodes in the tree, then we're done
173 if ( scanState
->nodeNum
>= scanState
->btcb
->totalNodes
)
174 return fsEndOfIterationErr
;
176 if ( scanState
->nodesLeftInBuffer
== 0 )
178 scanState
->recordNum
= 0;
182 (u_int8_t
*) scanState
->currentNodePtr
+= scanState
->btcb
->nodeSize
;
185 // Make sure this is a valid node
186 if ( CheckNode( scanState
->btcb
, scanState
->currentNodePtr
) != noErr
)
191 if ( scanState
->currentNodePtr
->kind
== kBTLeafNode
)
197 } /* FindNextLeafNode */
200 //_________________________________________________________________________________
202 // Routine: ReadMultipleNodes
204 // Purpose: Read one or more nodes into the buffer.
207 // theScanStatePtr Scanner's current state
210 // noErr One or nodes were read
211 // fsEndOfIterationErr No nodes left in file, none in buffer
212 //_________________________________________________________________________________
214 static int ReadMultipleNodes( BTScanState
*theScanStatePtr
)
217 BTreeControlBlockPtr myBTreeCBPtr
;
218 daddr_t myPhyBlockNum
;
219 u_int32_t myBufferSize
;
220 struct vnode
* myDevPtr
;
222 u_int32_t myBlocksInBufferCount
;
224 // release old buffer if we have one
225 if ( theScanStatePtr
->bufferPtr
!= NULL
)
227 theScanStatePtr
->bufferPtr
->b_flags
|= (B_INVAL
| B_AGE
);
228 brelse( theScanStatePtr
->bufferPtr
);
229 theScanStatePtr
->bufferPtr
= NULL
;
230 theScanStatePtr
->currentNodePtr
= NULL
;
233 myBTreeCBPtr
= theScanStatePtr
->btcb
;
235 // map logical block in catalog btree file to physical block on volume
236 myErr
= VOP_BMAP( myBTreeCBPtr
->fileRefNum
, theScanStatePtr
->nodeNum
,
237 &myDevPtr
, &myPhyBlockNum
, &myBlockRun
);
238 if ( myErr
!= E_NONE
)
240 goto ExitThisRoutine
;
243 // bmap block run gives us the remaining number of valid blocks (number of blocks
244 // minus the first). so if there are 10 valid blocks our run number will be 9.
245 // blocks, in our case is the same as nodes (both are 4K)
246 myBlocksInBufferCount
= (theScanStatePtr
->bufferSize
/ myBTreeCBPtr
->nodeSize
);
247 myBufferSize
= theScanStatePtr
->bufferSize
;
248 if ( (myBlockRun
+ 1) < myBlocksInBufferCount
)
250 myBufferSize
= (myBlockRun
+ 1) * myBTreeCBPtr
->nodeSize
;
253 // now read blocks from the device
254 myErr
= bread( myDevPtr
,
258 &theScanStatePtr
->bufferPtr
);
259 if ( myErr
!= E_NONE
)
261 goto ExitThisRoutine
;
264 theScanStatePtr
->nodesLeftInBuffer
= theScanStatePtr
->bufferPtr
->b_bcount
/ theScanStatePtr
->btcb
->nodeSize
;
265 theScanStatePtr
->currentNodePtr
= (BTNodeDescriptor
*) theScanStatePtr
->bufferPtr
->b_data
;
270 } /* ReadMultipleNodes */
274 //_________________________________________________________________________________
276 // Routine: BTScanInitialize
278 // Purpose: Prepare to start a new BTree scan, or resume a previous one.
281 // btreeFile The B-Tree's file control block
282 // startingNode Initial node number
283 // startingRecord Initial record number within node
284 // recordsFound Number of valid records found so far
285 // bufferSize Size (in bytes) of buffer
288 // scanState Scanner's current state; pass to other scanner calls
291 // To begin a new scan and see all records in the B-Tree, pass zeroes for
292 // startingNode, startingRecord, and recordsFound.
294 // To resume a scan from the point of a previous BTScanTerminate, use the
295 // values returned by BTScanTerminate as input for startingNode, startingRecord,
298 // When resuming a scan, the caller should check the B-tree's write count. If
299 // it is different from the write count when the scan was terminated, then the
300 // tree may have changed and the current state may be incorrect. In particular,
301 // you may see some records more than once, or never see some records. Also,
302 // the scanner may not be able to detect when all leaf records have been seen,
303 // and will have to scan through many empty nodes.
305 // XXXĂPerhaps the write count should be managed by BTScanInitialize and
306 // XXX BTScanTerminate? This would avoid the caller having to peek at
307 // XXX internal B-Tree structures.
308 //_________________________________________________________________________________
310 int BTScanInitialize( const FCB
* btreeFile
,
311 u_int32_t startingNode
,
312 u_int32_t startingRecord
,
313 u_int32_t recordsFound
,
314 u_int32_t bufferSize
,
315 BTScanState
* scanState
)
317 BTreeControlBlock
*btcb
;
320 // Make sure this is a valid B-Tree file
322 btcb
= (BTreeControlBlock
*) btreeFile
->fcbBTCBPtr
;
324 return fsBTInvalidFileErr
;
327 // Make sure buffer size is big enough, and a multiple of the
330 if ( bufferSize
< btcb
->nodeSize
)
332 bufferSize
= (bufferSize
/ btcb
->nodeSize
) * btcb
->nodeSize
;
335 // Set up the scanner's state
337 scanState
->bufferSize
= bufferSize
;
338 scanState
->bufferPtr
= NULL
;
339 scanState
->btcb
= btcb
;
340 scanState
->nodeNum
= startingNode
;
341 scanState
->recordNum
= startingRecord
;
342 scanState
->currentNodePtr
= NULL
;
343 scanState
->nodesLeftInBuffer
= 0; // no nodes currently in buffer
344 scanState
->recordsFound
= recordsFound
;
345 scanState
->startTime
= time
; // initialize our throttle
349 } /* BTScanInitialize */
352 //_________________________________________________________________________________
354 // Routine: BTScanTerminate
356 // Purpose: Return state information about a scan so that it can be resumed
357 // later via BTScanInitialize.
360 // scanState Scanner's current state
363 // nextNode Node number to resume a scan (pass to BTScanInitialize)
364 // nextRecord Record number to resume a scan (pass to BTScanInitialize)
365 // recordsFound Valid records seen so far (pass to BTScanInitialize)
366 //_________________________________________________________________________________
368 int BTScanTerminate( BTScanState
* scanState
,
369 u_int32_t
* startingNode
,
370 u_int32_t
* startingRecord
,
371 u_int32_t
* recordsFound
)
373 *startingNode
= scanState
->nodeNum
;
374 *startingRecord
= scanState
->recordNum
;
375 *recordsFound
= scanState
->recordsFound
;
377 if ( scanState
->bufferPtr
!= NULL
)
379 scanState
->bufferPtr
->b_flags
|= (B_INVAL
| B_AGE
);
380 brelse( scanState
->bufferPtr
);
381 scanState
->bufferPtr
= NULL
;
382 scanState
->currentNodePtr
= NULL
;
387 } /* BTScanTerminate */