]> git.saurik.com Git - apple/hfs.git/blobdiff - core/BTreeTreeOps.c
hfs-522.100.5.tar.gz
[apple/hfs.git] / core / BTreeTreeOps.c
diff --git a/core/BTreeTreeOps.c b/core/BTreeTreeOps.c
new file mode 100644 (file)
index 0000000..74cd04e
--- /dev/null
@@ -0,0 +1,1338 @@
+/*
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ * 
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+/*
+       File:           BTreeTreeOps.c
+
+       Contains:       Multi-node tree operations for the BTree Module.
+
+       Version:        xxx put the technology version here xxx
+
+       Written by:     Gordon Sheridan and Bill Bruffey
+
+       Copyright:      (c) 1992-1999 by Apple Inc., all rights reserved.
+
+       File Ownership:
+
+               DRI:                            Don Brady
+
+               Other Contact:          Mark Day
+
+               Technology:                     File Systems
+
+       Writers:
+
+               (msd)   Mark Day
+               (DSH)   Deric Horn
+               (djb)   Don Brady
+
+       Change History (most recent first):
+
+          <MOSXS>        6/1/99        djb             Sync up with Mac OS 8.6.
+          <CS5>         12/8/97        djb             Radar #2200632, CollapseTree wasn't marking root node dirty.
+          <CS4>        11/24/97        djb             Radar #2005325, InsertLevel incorrectly handled root splits!
+          <CS3>        10/17/97        msd             Conditionalize DebugStrs.
+          <CS2>         5/16/97        msd             InsertNode() needs a return statement in ErrorExit.
+          <CS1>         4/23/97        djb             first checked in
+
+         <HFS8>         3/17/97        DSH             Conditionalize out Panic assertion for SC.
+         <HFS7>          3/3/97        djb             Removed DebugStr in InsertLevel.
+         <HFS6>         2/19/97        djb             Major re-write of insert code; added InsertLevel and InsertNode.
+         <HFS5>         1/27/97        djb             InsertTree and DeleteTree are now recursive and support variable
+                                                                       sized index keys.
+         <HFS4>         1/16/97        djb             Removed DebugStr in SearchTree. Added initial support for
+                                                                       variable sized index keys.
+         <HFS3>          1/3/97        djb             Changed len8 to length8.
+         <HFS2>          1/3/97        djb             Added support for large keys.
+         <HFS1>        12/19/96        djb             first checked in
+
+       History applicable to original Scarecrow Design:
+
+                <3>    10/25/96        ser             Changing for new VFPI
+                <2>     1/22/96        dkh             Add #include Memory.h
+                <1>    10/18/95        rst             Moved from Scarecrow project.
+
+               <12>     7/18/95        mbb             Change MoveData & ClearBytes to BlockMoveData & BlockZero.
+               <11>     9/30/94        prp             Get in sync with D2 interface changes.
+               <10>     7/25/94        wjk             Eliminate usage of BytePtr in favor of UInt8 *.
+                <9>     7/22/94        wjk             Convert to the new set of header files.
+                <8>     12/2/93        wjk             Move from Makefiles to BuildFiles. Fit into the ModernOS and
+                                                                       NRCmds environments.
+                <7>    11/30/93        wjk             Change some Ptr's to BytePtr's in function definitions so they
+                                                                       agree with their prototypes.
+                <6>     5/21/93        gs              Debug DeleteTree. Modify InsertTree for BTReplaceRecord.
+                <5>     5/10/93        gs              Modify RotateLeft, and add DeleteTree, CollapseTree routines.
+                <4>     3/23/93        gs              revise RotateLeft to use InsertKeyRecord instead of
+                                                                       InsertRecord.
+                <3>     3/23/93        gs              Implement SplitLeft, InsertTree routine.
+                <2>      2/8/93        gs              Implement SearchTree, and RotateLeft.
+                <1>    11/15/92        gs              first checked in
+
+*/
+
+#include "BTreesPrivate.h"
+#include "hfs_btreeio.h"
+
+//
+/////////////////////// Routines Internal To BTree Module ///////////////////////
+//
+//     SearchTree
+//     InsertTree
+//
+////////////////////// Routines Internal To BTreeTreeOps.c //////////////////////
+
+static OSStatus   AddNewRootNode       (BTreeControlBlockPtr            btreePtr,
+                                                                        NodeDescPtr                             leftNode,
+                                                                        NodeDescPtr                             rightNode );
+
+static OSStatus   CollapseTree         (BTreeControlBlockPtr            btreePtr,
+                                                                        BlockDescriptor                        *blockPtr );
+
+static OSStatus   RotateLeft           (BTreeControlBlockPtr            btreePtr,
+                                                                        NodeDescPtr                             leftNode,
+                                                                        NodeDescPtr                             rightNode,
+                                                                        u_int16_t                                       rightInsertIndex,
+                                                                        KeyPtr                                          keyPtr,
+                                                                        u_int8_t *                                      recPtr,
+                                                                        u_int16_t                                       recSize,
+                                                                        u_int16_t                                      *insertIndex,
+                                                                        u_int32_t                                      *insertNodeNum,
+                                                                        Boolean                                        *recordFit,
+                                                                        u_int16_t                                      *recsRotated );
+
+static Boolean    RotateRecordLeft     (BTreeControlBlockPtr            btreePtr,
+                                                                        NodeDescPtr                             leftNode,
+                                                                        NodeDescPtr                             rightNode );
+
+static OSStatus           SplitLeft            (BTreeControlBlockPtr            btreePtr,
+                                                                        BlockDescriptor                        *leftNode,
+                                                                        BlockDescriptor                        *rightNode,
+                                                                        u_int32_t                                       rightNodeNum,
+                                                                        u_int16_t                                       index,
+                                                                        KeyPtr                                          keyPtr,
+                                                                        u_int8_t *                                      recPtr,
+                                                                        u_int16_t                                       recSize,
+                                                                        u_int16_t                                      *insertIndex,
+                                                                        u_int32_t                                      *insertNodeNum,
+                                                                        u_int16_t                                      *recsRotated );
+                                                                
+
+
+static OSStatus        InsertLevel             (BTreeControlBlockPtr            btreePtr,
+                                                                        TreePathTable                           treePathTable,
+                                                                        InsertKey                                      *primaryKey,
+                                                                        InsertKey                                      *secondaryKey,
+                                                                        BlockDescriptor                        *targetNode,
+                                                                        u_int16_t                                       index,
+                                                                        u_int16_t                                       level,
+                                                                        u_int32_t                                      *insertNode );
+                                                
+static OSErr           InsertNode              (BTreeControlBlockPtr            btreePtr,
+                                                                        InsertKey                                      *key,
+                                                                        BlockDescriptor                        *rightNode,
+                                                                        u_int32_t                                       node,
+                                                                        u_int16_t                                       index,
+                                                                        u_int32_t                                      *newNode,       
+                                                                        u_int16_t                                      *newIndex,
+                                                                        BlockDescriptor                        *leftNode,
+                                                                        Boolean                                        *updateParent,
+                                                                        Boolean                                        *insertParent,
+                                                                        Boolean                                        *rootSplit );
+                                                                        
+static u_int16_t               GetKeyLength    (const BTreeControlBlock *btreePtr,
+                                                                        const BTreeKey *key,
+                                                                        Boolean forLeafNode );
+
+
+
+//////////////////////// BTree Multi-node Tree Operations ///////////////////////
+
+
+/*-------------------------------------------------------------------------------
+
+Routine:       SearchTree      -       Search BTree for key and set up Tree Path Table.
+
+Function:      Searches BTree for specified key, setting up the Tree Path Table to
+                       reflect the search path.
+
+
+Input:         btreePtr                - pointer to control block of BTree to search
+                       keyPtr                  - pointer to the key to search for
+                       treePathTable   - pointer to the tree path table to construct
+                       
+Output:                nodeNum                 - number of the node containing the key position
+                       iterator                - BTreeIterator specifying record or insert position
+                       
+Result:                noErr                   - key found, index is record index
+                       fsBTRecordNotFoundErr   - key not found, index is insert index
+                       fsBTEmptyErr            - key not found, return params are nil
+                       otherwise                       - catastrophic failure (GetNode/ReleaseNode failed)
+-------------------------------------------------------------------------------*/
+
+OSStatus       SearchTree      (BTreeControlBlockPtr    btreePtr,
+                                                BTreeKeyPtr                     searchKey,
+                                                TreePathTable                   treePathTable,
+                                                u_int32_t                              *nodeNum,
+                                                BlockDescriptor                *nodePtr,
+                                                u_int16_t                              *returnIndex )
+{
+       OSStatus        err;
+       int16_t         level;                                  //      Expected depth of current node
+       u_int32_t       curNodeNum;                             //      Current node we're searching
+       NodeRec         nodeRec;
+       u_int16_t       index;
+       Boolean         keyFound;
+       int8_t          nodeKind;                               //      Kind of current node (index/leaf)
+       KeyPtr          keyPtr;
+       u_int8_t *      dataPtr;
+       u_int16_t       dataSize;
+       
+       
+       curNodeNum              = btreePtr->rootNode;
+       level                   = btreePtr->treeDepth;
+       
+       if (level == 0)                                         // is the tree empty?
+       {
+               err = fsBTEmptyErr;
+               goto ErrorExit;
+       }
+       
+       //\80\80 for debugging...
+       treePathTable [0].node          = 0;
+       treePathTable [0].index         = 0;
+
+       while (true)
+       {
+        //
+        //     [2550929] Node number 0 is the header node.  It is never a valid
+        //     index or leaf node.  If we're ever asked to search through node 0,
+        //     something has gone wrong (typically a bad child node number, or
+        //     we found a node full of zeroes that we thought was an index node).
+        //
+        if (curNodeNum == 0)
+        {
+//          Panic("SearchTree: curNodeNum is zero!");
+            err = btBadNode;
+            goto ErrorExit;
+        }
+        
+        err = GetNode (btreePtr, curNodeNum, 0, &nodeRec);
+        if (err != noErr)
+        {
+                goto ErrorExit;
+        }
+               
+        //
+        //     [2550929] Sanity check the node height and node type.  We expect
+        //     particular values at each iteration in the search.  This checking
+        //     quickly finds bad pointers, loops, and other damage to the
+        //     hierarchy of the B-tree.
+        //
+        if (((BTNodeDescriptor*)nodeRec.buffer)->height != level)
+        {
+//             Panic("Incorrect node height");
+                err = btBadNode;
+                goto ReleaseAndExit;
+        }
+        nodeKind = ((BTNodeDescriptor*)nodeRec.buffer)->kind;
+        if (level == 1)
+        {
+            // Nodes at level 1 must be leaves, by definition
+            if (nodeKind != kBTLeafNode)
+            {
+ //            Panic("Incorrect node type: expected leaf");
+                err = btBadNode;
+                goto ReleaseAndExit;           
+            }
+        }
+        else
+        {
+            // A node at any other depth must be an index node
+            if (nodeKind != kBTIndexNode)
+            {
+//             Panic("Incorrect node type: expected index");
+                err = btBadNode;
+                goto ReleaseAndExit;
+            }
+        }
+        
+        keyFound = SearchNode (btreePtr, nodeRec.buffer, searchKey, &index);
+
+        treePathTable [level].node             = curNodeNum;
+
+        if (nodeKind == kBTLeafNode)
+        {
+                treePathTable [level].index = index;
+                break;                 // were done...
+        }
+        
+        if ( (keyFound != true) && (index != 0))
+                --index;
+
+        treePathTable [level].index = index;
+        
+        err = GetRecordByIndex (btreePtr, nodeRec.buffer, index, &keyPtr, &dataPtr, &dataSize);
+        if (err != noErr)
+        {
+            // [2550929] If we got an error, it is probably because the index was bad
+            // (typically a corrupt node that confused SearchNode).  Invalidate the node
+            // so we won't accidentally use the corrupted contents.  NOTE: the Mac OS 9
+            // sources call this InvalidateNode.
+            
+                (void) TrashNode(btreePtr, &nodeRec);
+                goto ErrorExit;
+        }
+        
+        //     Get the child pointer out of this index node.  We're now done with the current
+        //     node and can continue the search with the child node.
+        curNodeNum = *(u_int32_t *)dataPtr;
+        err = ReleaseNode (btreePtr, &nodeRec);
+        if (err != noErr)
+        {
+                goto ErrorExit;
+        }
+
+        //     The child node should be at a level one less than the parent.
+        --level;
+       }
+       
+       *nodeNum                        = curNodeNum;
+       *nodePtr                        = nodeRec;
+       *returnIndex            = index;
+
+       if (keyFound)
+               return  noErr;                  // searchKey found, index identifies record in node
+       else
+               return  fsBTRecordNotFoundErr;  // searchKey not found, index identifies insert point
+
+ReleaseAndExit:
+    (void) ReleaseNode(btreePtr, &nodeRec);
+    // fall into ErrorExit
+
+ErrorExit:
+       
+       *nodeNum                                        = 0;
+       nodePtr->buffer                         = nil;
+       nodePtr->blockHeader            = nil;
+       *returnIndex                            = 0;
+
+       return  err;
+}
+
+
+
+
+////////////////////////////////// InsertTree ///////////////////////////////////
+
+OSStatus       InsertTree ( BTreeControlBlockPtr                btreePtr,
+                                                TreePathTable                           treePathTable,
+                                                KeyPtr                                          keyPtr,
+                                                u_int8_t *                                      recPtr,
+                                                u_int16_t                                       recSize,
+                                                BlockDescriptor                        *targetNode,
+                                                u_int16_t                                       index,
+                                                u_int16_t                                       level,
+                                                Boolean                                         replacingKey,
+                                                u_int32_t                                      *insertNode )
+{
+       InsertKey                       primaryKey;
+       OSStatus                        err;
+
+       primaryKey.keyPtr               = keyPtr;
+       primaryKey.keyLength    = GetKeyLength(btreePtr, primaryKey.keyPtr, (level == 1));
+       primaryKey.recPtr               = recPtr;
+       primaryKey.recSize              = recSize;
+       primaryKey.replacingKey = replacingKey;
+       primaryKey.skipRotate   = false;
+
+       err     = InsertLevel (btreePtr, treePathTable, &primaryKey, nil,
+                                          targetNode, index, level, insertNode );
+                                               
+       return err;
+
+} // End of InsertTree
+
+
+////////////////////////////////// InsertLevel //////////////////////////////////
+
+OSStatus       InsertLevel (BTreeControlBlockPtr                btreePtr,
+                                                TreePathTable                           treePathTable,
+                                                InsertKey                                      *primaryKey,
+                                                InsertKey                                      *secondaryKey,
+                                                BlockDescriptor                        *targetNode,
+                                                u_int16_t                                       index,
+                                                u_int16_t                                       level,
+                                                u_int32_t                                      *insertNode )
+{
+       OSStatus                         err;
+       BlockDescriptor          leftNode;
+       u_int32_t                        targetNodeNum;
+       u_int32_t                        newNodeNum;
+       u_int16_t                        newIndex;
+       Boolean                          insertParent;
+       Boolean                          updateParent;
+       Boolean                          newRoot;
+       InsertKey                       insertKey;
+
+#if defined(applec) && !defined(__SC__)
+       PanicIf ((level == 1) && (((NodeDescPtr)targetNode->buffer)->kind != kBTLeafNode), " InsertLevel: non-leaf at level 1! ");
+#endif
+       leftNode.buffer = nil;
+       leftNode.blockHeader = nil;
+       targetNodeNum = treePathTable [level].node;
+
+       insertParent = false;
+       updateParent = false;
+
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, targetNode);
+
+       ////// process first insert //////
+
+       err = InsertNode (btreePtr, primaryKey, targetNode, targetNodeNum, index,
+                                         &newNodeNum, &newIndex, &leftNode, &updateParent, &insertParent, &newRoot );
+       M_ExitOnError (err);
+
+       if ( newRoot )
+       {
+               // Extend the treePathTable by adding an entry for the new
+               // root node that references the current targetNode.
+               // 
+               // If inserting the secondaryKey changes the first key of
+               // the target node, then we'll have to update the second
+               // key in the new root node.
+
+               treePathTable [level + 1].node  = btreePtr->rootNode;
+               treePathTable [level + 1].index = 1;    // 1 since we always split/rotate left
+       }
+       
+       if ( level == 1 )
+               *insertNode = newNodeNum;               
+       
+       ////// process second insert (if any) //////
+
+       if  ( secondaryKey != nil )
+       {
+               Boolean                         temp;
+
+               err = InsertNode (btreePtr, secondaryKey, targetNode, newNodeNum, newIndex,
+                                                 &newNodeNum, &newIndex, &leftNode, &updateParent, &insertParent, &temp);
+               M_ExitOnError (err);
+       }
+
+       //////////////////////// Update Parent(s) ///////////////////////////////
+
+       if ( insertParent || updateParent )
+       {
+               BlockDescriptor         parentNode;
+               u_int32_t                       parentNodeNum;
+               KeyPtr                          keyPtr;
+               u_int8_t *                      recPtr;
+               u_int16_t                       recSize;
+               
+               parentNode.buffer = nil;
+               parentNode.blockHeader = nil;
+
+               secondaryKey = nil;
+               
+               PanicIf ( (level == btreePtr->treeDepth), " InsertLevel: unfinished insert!?");
+
+               ++level;
+
+               // Get Parent Node data...
+               index = treePathTable [level].index;
+               parentNodeNum = treePathTable [level].node;
+
+               PanicIf ( parentNodeNum == 0, " InsertLevel: parent node is zero!?");
+
+               err = GetNode (btreePtr, parentNodeNum, 0, &parentNode);        // released as target node in next level up
+               M_ExitOnError (err);
+               ////////////////////////// Update Parent Index //////////////////////////////
+       
+               if ( updateParent )
+               {
+                       // XXXdbg
+                       ModifyBlockStart(btreePtr->fileRefNum, &parentNode);
+
+                       //\80\80 debug: check if ptr == targetNodeNum
+                       GetRecordByIndex (btreePtr, parentNode.buffer, index, &keyPtr, &recPtr, &recSize);
+                       PanicIf( (*(u_int32_t *) recPtr) != targetNodeNum, " InsertLevel: parent ptr doesn't match target node!");
+                       
+                       // need to delete and re-insert this parent key/ptr
+                       // we delete it here and it gets re-inserted in the
+                       // InsertLevel call below.
+                       DeleteRecord (btreePtr, parentNode.buffer, index);
+       
+                       primaryKey->keyPtr               = (KeyPtr) GetRecordAddress( btreePtr, targetNode->buffer, 0 );
+                       primaryKey->keyLength    = GetKeyLength(btreePtr, primaryKey->keyPtr, false);
+                       primaryKey->recPtr               = (u_int8_t *) &targetNodeNum;
+                       primaryKey->recSize              = sizeof(targetNodeNum);
+                       primaryKey->replacingKey = kReplaceRecord;
+                       primaryKey->skipRotate   = insertParent;                // don't rotate left if we have two inserts occuring
+               }
+       
+               ////////////////////////// Add New Parent Index /////////////////////////////
+       
+               if ( insertParent )
+               {
+                       InsertKey       *insertKeyPtr;
+                       
+                       if ( updateParent )
+                       {
+                               insertKeyPtr = &insertKey;
+                               secondaryKey = &insertKey;
+                       }
+                       else
+                       {
+                               insertKeyPtr = primaryKey;
+                       }
+                       
+                       insertKeyPtr->keyPtr            = (KeyPtr) GetRecordAddress (btreePtr, leftNode.buffer, 0);
+                       insertKeyPtr->keyLength         = GetKeyLength(btreePtr, insertKeyPtr->keyPtr, false);
+                       insertKeyPtr->recPtr            = (u_int8_t *) &((NodeDescPtr)targetNode->buffer)->bLink;
+                       insertKeyPtr->recSize           = sizeof(u_int32_t);
+                       insertKeyPtr->replacingKey      = kInsertRecord;
+                       insertKeyPtr->skipRotate        = false;                // a rotate is OK during second insert
+               }       
+               
+               err = InsertLevel (btreePtr, treePathTable, primaryKey, secondaryKey,
+                                                  &parentNode, index, level, insertNode );
+               M_ExitOnError (err);
+       }
+
+       err = UpdateNode (btreePtr, targetNode, 0, kLockTransaction);   // all done with target
+       M_ExitOnError (err);
+
+       err = UpdateNode (btreePtr, &leftNode, 0, kLockTransaction);            // all done with left sibling
+       M_ExitOnError (err);
+       
+       return  noErr;
+
+ErrorExit:
+
+       (void) ReleaseNode (btreePtr, targetNode);
+       (void) ReleaseNode (btreePtr, &leftNode);
+
+       Panic (" InsertLevel: an error occurred!");
+
+       return  err;
+
+} // End of InsertLevel
+
+
+
+////////////////////////////////// InsertNode ///////////////////////////////////
+
+static OSErr   InsertNode      (BTreeControlBlockPtr    btreePtr,
+                                                        InsertKey                              *key,
+
+                                                        BlockDescriptor                *rightNode,
+                                                        u_int32_t                               node,
+                                                        u_int16_t                               index,
+
+                                                        u_int32_t                              *newNode,       
+                                                        u_int16_t                              *newIndex,
+
+                                                        BlockDescriptor                *leftNode,
+                                                        Boolean                                *updateParent,
+                                                        Boolean                                *insertParent,
+                                                        Boolean                                *rootSplit )
+{
+       BlockDescriptor         *targetNode = NULL;
+       u_int32_t                        leftNodeNum;
+       u_int16_t                        recsRotated;
+       OSErr                            err;
+       Boolean                          recordFit;
+
+       *rootSplit = false;
+       
+       PanicIf ( rightNode->buffer == leftNode->buffer, " InsertNode: rightNode == leftNode, huh?");
+       
+       leftNodeNum = ((NodeDescPtr) rightNode->buffer)->bLink;
+
+
+       /////////////////////// Try Simple Insert ///////////////////////////////
+
+       /* sanity check our left and right nodes here. */
+       if (node == leftNodeNum) {
+               if (leftNode->buffer == NULL) {
+                       err = fsBTInvalidNodeErr;
+                       M_ExitOnError(err);     
+               }
+               else{
+                       targetNode = leftNode;
+               }
+       }
+       else {
+               // we can assume right node is initialized.
+               targetNode = rightNode; 
+       }
+       
+       
+       recordFit = InsertKeyRecord (btreePtr, targetNode->buffer, index, key->keyPtr, key->keyLength, key->recPtr, key->recSize);
+
+       if ( recordFit )
+       {
+               *newNode  = node;
+               *newIndex = index;
+       
+               if ( (index == 0) && (((NodeDescPtr) targetNode->buffer)->height != btreePtr->treeDepth) )
+                       *updateParent = true;   // the first record changed so we need to update the parent
+       }
+
+
+       //////////////////////// Try Rotate Left ////////////////////////////////
+       
+       if ( !recordFit && leftNodeNum > 0 )
+       {
+               PanicIf ( leftNode->buffer != nil, " InsertNode: leftNode already acquired!");
+
+               if ( leftNode->buffer == nil )
+               {
+                       err = GetNode (btreePtr, leftNodeNum, 0, leftNode);     // will be released by caller or a split below
+                       M_ExitOnError (err);
+                       // XXXdbg
+                       ModifyBlockStart(btreePtr->fileRefNum, leftNode);
+               }
+
+               PanicIf ( ((NodeDescPtr) leftNode->buffer)->fLink != node, " InsertNode, RotateLeft: invalid sibling link!" );
+
+               if ( !key->skipRotate )         // are rotates allowed?
+               {
+                       err = RotateLeft (btreePtr, leftNode->buffer, rightNode->buffer, index, key->keyPtr, key->recPtr,
+                                                         key->recSize, newIndex, newNode, &recordFit, &recsRotated );  
+                       M_ExitOnError (err);
+
+                       if ( recordFit )
+                       {
+                               if ( key->replacingKey || (recsRotated > 1) || (index > 0) )
+                                       *updateParent = true;                   
+                       }
+               }
+       }       
+
+
+       //////////////////////// Try Split Left /////////////////////////////////
+
+       if ( !recordFit )
+       {
+               // might not have left node...
+               err = SplitLeft (btreePtr, leftNode, rightNode, node, index, key->keyPtr,
+                                                key->recPtr, key->recSize, newIndex, newNode, &recsRotated);
+               M_ExitOnError (err);
+
+               // if we split root node - add new root
+               
+               if ( ((NodeDescPtr) rightNode->buffer)->height == btreePtr->treeDepth )
+               {
+                       err = AddNewRootNode (btreePtr, leftNode->buffer, rightNode->buffer);   // Note: does not update TPT
+                       M_ExitOnError (err);
+                       *rootSplit = true;
+               }
+               else
+               {
+                       *insertParent = true;
+
+                       if ( key->replacingKey || (recsRotated > 1) || (index > 0) )
+                               *updateParent = true;
+               }
+       }
+       
+       return noErr;
+
+ErrorExit:
+       (void) ReleaseNode (btreePtr, leftNode);
+       return err;
+       
+} // End of InsertNode
+
+
+/*-------------------------------------------------------------------------------
+Routine:       DeleteTree      -       One_line_description.
+
+Function:      Brief_description_of_the_function_and_any_side_effects
+
+ToDo:          
+
+Input:         btreePtr                - description
+                       treePathTable   - description
+                       targetNode              - description
+                       index                   - description
+                                               
+Result:                noErr           - success
+                       != noErr        - failure
+-------------------------------------------------------------------------------*/
+
+OSStatus       DeleteTree                      (BTreeControlBlockPtr            btreePtr,
+                                                                TreePathTable                           treePathTable,
+                                                                BlockDescriptor                        *targetNode,
+                                                                u_int16_t                                       index,
+                                                                u_int16_t                                       level )
+{
+       OSStatus                        err;
+       BlockDescriptor         parentNode;
+       BTNodeDescriptor        *targetNodePtr;
+       u_int32_t                       targetNodeNum;
+       Boolean                         deleteRequired;
+       Boolean                         updateRequired;
+
+       // XXXdbg - initialize these to null in case we get an
+       //          error and try to exit before it's initialized
+       parentNode.buffer      = nil;   
+       parentNode.blockHeader = nil;
+       
+       deleteRequired = false;
+       updateRequired = false;
+
+       targetNodeNum = treePathTable[level].node;
+       targetNodePtr = targetNode->buffer;
+       PanicIf (targetNodePtr == nil, "DeleteTree: targetNode has nil buffer!");
+
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, targetNode);
+
+       DeleteRecord (btreePtr, targetNodePtr, index);
+               
+       //\80\80 coalesce remaining records?
+
+       if ( targetNodePtr->numRecords == 0 )   // did we delete the last record?
+       {
+               BlockDescriptor         siblingNode;
+               u_int32_t                       siblingNodeNum;
+
+               deleteRequired = true;
+               
+               siblingNode.buffer = nil;
+               siblingNode.blockHeader = nil;
+
+               ////////////////// Get Siblings & Update Links //////////////////////////
+               
+               siblingNodeNum = targetNodePtr->bLink;                          // Left Sibling Node
+               if ( siblingNodeNum != 0 )
+               {
+                       err = GetNode (btreePtr, siblingNodeNum, 0, &siblingNode);
+                       M_ExitOnError (err);
+
+                       // XXXdbg
+                       ModifyBlockStart(btreePtr->fileRefNum, &siblingNode);
+
+                       ((NodeDescPtr)siblingNode.buffer)->fLink = targetNodePtr->fLink;
+                       err = UpdateNode (btreePtr, &siblingNode, 0, kLockTransaction);
+                       M_ExitOnError (err);
+               }
+               else if ( targetNodePtr->kind == kBTLeafNode )          // update firstLeafNode
+               {
+                       btreePtr->firstLeafNode = targetNodePtr->fLink;
+               }
+
+               siblingNodeNum = targetNodePtr->fLink;                          // Right Sibling Node
+               if ( siblingNodeNum != 0 )
+               {
+                       err = GetNode (btreePtr, siblingNodeNum, 0, &siblingNode);
+                       M_ExitOnError (err);
+
+                       // XXXdbg
+                       ModifyBlockStart(btreePtr->fileRefNum, &siblingNode);
+
+                       ((NodeDescPtr)siblingNode.buffer)->bLink = targetNodePtr->bLink;
+                       err = UpdateNode (btreePtr, &siblingNode, 0, kLockTransaction);
+                       M_ExitOnError (err);
+               }
+               else if ( targetNodePtr->kind == kBTLeafNode )          // update lastLeafNode
+               {
+                       btreePtr->lastLeafNode = targetNodePtr->bLink;
+               }
+               
+               //////////////////////// Free Empty Node ////////////////////////////////
+
+               ClearNode (btreePtr, targetNodePtr);
+               
+               err = UpdateNode (btreePtr, targetNode, 0, kLockTransaction);
+               M_ExitOnError (err);
+
+               err = FreeNode (btreePtr, targetNodeNum);
+               M_ExitOnError (err);
+       }
+       else if ( index == 0 )                  // did we delete the first record?
+       {
+               updateRequired = true;          // yes, so we need to update parent
+       }
+
+
+       if ( level == btreePtr->treeDepth )             // then targetNode->buffer is the root node
+       {
+               deleteRequired = false;
+               updateRequired = false;
+               
+               if ( targetNode->buffer == nil )        // then root was freed and the btree is empty
+               {
+                       btreePtr->rootNode  = 0;
+                       btreePtr->treeDepth = 0;
+               }
+               else if ( ((NodeDescPtr)targetNode->buffer)->numRecords == 1 )
+               {
+                       err = CollapseTree (btreePtr, targetNode);
+                       M_ExitOnError (err);
+               }
+       }
+
+
+       if ( updateRequired || deleteRequired )
+       {
+               ++level;        // next level
+
+               //// Get Parent Node and index
+               index = treePathTable [level].index;
+               err = GetNode (btreePtr, treePathTable[level].node, 0, &parentNode);
+               M_ExitOnError (err);
+
+               if ( updateRequired )
+               {
+                        KeyPtr         keyPtr;
+                        u_int8_t *     recPtr;
+                        u_int16_t      recSize;
+                        u_int32_t      insertNode;
+                        
+                        // XXXdbg
+                        ModifyBlockStart(btreePtr->fileRefNum, &parentNode);
+
+                       //\80\80 debug: check if ptr == targetNodeNum
+                       GetRecordByIndex (btreePtr, parentNode.buffer, index, &keyPtr, &recPtr, &recSize);
+                       PanicIf( (*(u_int32_t *) recPtr) != targetNodeNum, " DeleteTree: parent ptr doesn't match targetNodeNum!!");
+                       
+                       // need to delete and re-insert this parent key/ptr
+                       DeleteRecord (btreePtr, parentNode.buffer, index);
+       
+                       keyPtr = (KeyPtr) GetRecordAddress( btreePtr, targetNode->buffer, 0 );
+                       recPtr = (u_int8_t *) &targetNodeNum;
+                       recSize = sizeof(targetNodeNum);
+                       
+                       err = InsertTree (btreePtr, treePathTable, keyPtr, recPtr, recSize,
+                                                         &parentNode, index, level, kReplaceRecord, &insertNode);
+                       M_ExitOnError (err);
+               }
+               else // deleteRequired
+               {
+                       err = DeleteTree (btreePtr, treePathTable, &parentNode, index, level);
+                       M_ExitOnError (err);
+               }
+       }       
+
+
+       err = UpdateNode (btreePtr, targetNode, 0, kLockTransaction);
+       M_ExitOnError (err);
+
+       return  noErr;
+
+ErrorExit:
+
+       (void) ReleaseNode (btreePtr, targetNode);
+       (void) ReleaseNode (btreePtr, &parentNode);
+
+       return  err;
+
+} // end DeleteTree
+
+
+
+///////////////////////////////// CollapseTree //////////////////////////////////
+
+static OSStatus        CollapseTree    (BTreeControlBlockPtr           btreePtr,
+                                                                BlockDescriptor                        *blockPtr )
+{
+       OSStatus                err;
+       u_int32_t               originalRoot;
+       u_int32_t               nodeNum;
+       
+       originalRoot    = btreePtr->rootNode;
+       
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, blockPtr);
+
+       while (true)
+       {
+               if ( ((NodeDescPtr)blockPtr->buffer)->numRecords > 1)
+                       break;                                                  // this will make a fine root node
+               
+               if ( ((NodeDescPtr)blockPtr->buffer)->kind == kBTLeafNode)
+                       break;                                                  // we've hit bottom
+               
+               nodeNum                         = btreePtr->rootNode;
+               btreePtr->rootNode      = GetChildNodeNum (btreePtr, blockPtr->buffer, 0);
+               --btreePtr->treeDepth;
+
+               //// Clear and Free Current Old Root Node ////
+               ClearNode (btreePtr, blockPtr->buffer);
+               err = UpdateNode (btreePtr, blockPtr, 0, kLockTransaction);
+               M_ExitOnError (err);
+               err = FreeNode (btreePtr, nodeNum);
+               M_ExitOnError (err);
+               
+               //// Get New Root Node
+               err = GetNode (btreePtr, btreePtr->rootNode, 0, blockPtr);
+               M_ExitOnError (err);
+
+               // XXXdbg
+               ModifyBlockStart(btreePtr->fileRefNum, blockPtr);
+       }
+       
+       if (btreePtr->rootNode != originalRoot)
+               M_BTreeHeaderDirty (btreePtr);
+               
+       err = UpdateNode (btreePtr, blockPtr, 0, kLockTransaction);     // always update!
+       M_ExitOnError (err);
+       
+       return  noErr;
+       
+
+/////////////////////////////////// ErrorExit ///////////////////////////////////
+
+ErrorExit:
+       (void)  ReleaseNode (btreePtr, blockPtr);
+       return  err;
+}
+
+
+
+////////////////////////////////// RotateLeft ///////////////////////////////////
+
+/*-------------------------------------------------------------------------------
+
+Routine:       RotateLeft      -       One_line_description.
+
+Function:      Brief_description_of_the_function_and_any_side_effects
+
+Algorithm:     if rightIndex > insertIndex, subtract 1 for actual rightIndex
+
+Input:         btreePtr                        - description
+                       leftNode                        - description
+                       rightNode                       - description
+                       rightInsertIndex        - description
+                       keyPtr                          - description
+                       recPtr                          - description
+                       recSize                         - description
+                       
+Output:                insertIndex
+                       insertNodeNum           - description
+                       recordFit                       - description
+                       recsRotated
+                       
+Result:                noErr           - success
+                       != noErr        - failure
+-------------------------------------------------------------------------------*/
+
+static OSStatus        RotateLeft              (BTreeControlBlockPtr            btreePtr,
+                                                                NodeDescPtr                             leftNode,
+                                                                NodeDescPtr                             rightNode,
+                                                                u_int16_t                                       rightInsertIndex,
+                                                                KeyPtr                                          keyPtr,
+                                                                u_int8_t *                                      recPtr,
+                                                                u_int16_t                                       recSize,
+                                                                u_int16_t                                      *insertIndex,
+                                                                u_int32_t                                      *insertNodeNum,
+                                                                Boolean                                        *recordFit,
+                                                                u_int16_t                                      *recsRotated )
+{
+       OSStatus                        err;
+       int32_t                         insertSize;
+       int32_t                         nodeSize;
+       int32_t                         leftSize, rightSize;
+       int32_t                         moveSize = 0;
+       u_int16_t                       keyLength;
+       u_int16_t                       lengthFieldSize;
+       u_int16_t                       index, moveIndex;
+       Boolean                         didItFit;
+
+       ///////////////////// Determine If Record Will Fit //////////////////////////
+       
+       keyLength = GetKeyLength(btreePtr, keyPtr, (rightNode->kind == kBTLeafNode));
+
+       // the key's length field is 8-bits in HFS and 16-bits in HFS+
+       if ( btreePtr->attributes & kBTBigKeysMask )
+               lengthFieldSize = sizeof(u_int16_t);
+       else
+               lengthFieldSize = sizeof(u_int8_t);
+
+       insertSize = keyLength + lengthFieldSize + recSize + sizeof(u_int16_t);
+
+       if ( M_IsOdd (insertSize) )
+               ++insertSize;   // add pad byte;
+
+       nodeSize                = btreePtr->nodeSize;
+
+       // add size of insert record to right node
+       rightSize               = nodeSize - GetNodeFreeSize (btreePtr, rightNode) + insertSize;
+       leftSize                = nodeSize - GetNodeFreeSize (btreePtr, leftNode);
+
+       moveIndex       = 0;
+
+       while ( leftSize < rightSize )
+       {
+               if ( moveIndex < rightInsertIndex )
+               {
+                       moveSize = GetRecordSize (btreePtr, rightNode, moveIndex) + 2;
+               }
+               else if ( moveIndex == rightInsertIndex )
+               {
+                       moveSize = insertSize;
+               }
+               else // ( moveIndex > rightInsertIndex )
+               {
+                       moveSize = GetRecordSize (btreePtr, rightNode, moveIndex - 1) + 2;
+               }
+               
+               leftSize        += moveSize;
+               rightSize       -= moveSize;
+               ++moveIndex;
+       }       
+       
+       if ( leftSize > nodeSize )      // undo last move
+       {
+               leftSize        -= moveSize;
+               rightSize       += moveSize;
+               --moveIndex;
+       }
+       
+       if ( rightSize > nodeSize )     // record won't fit - failure, but not error
+       {
+               *insertIndex    = 0;
+               *insertNodeNum  = 0;
+               *recordFit              = false;
+               *recsRotated    = 0;
+               
+               return  noErr;
+       }
+       
+       // we've found balance point, moveIndex == number of records moved into leftNode
+       
+
+       //////////////////////////// Rotate Records /////////////////////////////////
+
+       *recsRotated    = moveIndex;
+       *recordFit              = true;
+       index                   = 0;
+
+       while ( index < moveIndex )
+       {
+               if ( index == rightInsertIndex )        // insert new record in left node
+               {
+                       u_int16_t       leftInsertIndex;
+                       
+                       leftInsertIndex = leftNode->numRecords;
+
+                       didItFit = InsertKeyRecord (btreePtr, leftNode, leftInsertIndex,
+                                                                               keyPtr, keyLength, recPtr, recSize);
+                       if ( !didItFit )
+                       {
+                               Panic ("RotateLeft: InsertKeyRecord (left) returned false!");
+                               err = fsBTBadRotateErr;
+                               goto ErrorExit;
+                       }
+                       
+                       *insertIndex = leftInsertIndex;
+                       *insertNodeNum = rightNode->bLink;
+               }
+               else
+               {
+                       didItFit = RotateRecordLeft (btreePtr, leftNode, rightNode);
+                       if ( !didItFit )
+                       {
+                               Panic ("RotateLeft: RotateRecordLeft returned false!");
+                               err = fsBTBadRotateErr;
+                               goto ErrorExit;
+                       }
+               }
+               
+               ++index;
+       }
+       
+       if ( moveIndex <= rightInsertIndex )    // then insert new record in right node
+       {
+               rightInsertIndex -= index;                      // adjust for records already rotated
+               
+               didItFit = InsertKeyRecord (btreePtr, rightNode, rightInsertIndex,
+                                                                       keyPtr, keyLength, recPtr, recSize);
+               if ( !didItFit )
+               {
+                       Panic ("RotateLeft: InsertKeyRecord (right) returned false!");
+                       err = fsBTBadRotateErr;
+                       goto ErrorExit;
+               }
+       
+               *insertIndex = rightInsertIndex;
+               *insertNodeNum = leftNode->fLink;
+       }
+
+
+       return noErr;
+
+
+       ////////////////////////////// Error Exit ///////////////////////////////////
+
+ErrorExit:
+
+       *insertIndex    = 0;
+       *insertNodeNum  = 0;
+       *recordFit              = false;
+       *recsRotated    = 0;
+       
+       return  err;
+}
+
+
+
+/////////////////////////////////// SplitLeft ///////////////////////////////////
+
+static OSStatus        SplitLeft               (BTreeControlBlockPtr            btreePtr,
+                                                                BlockDescriptor                        *leftNode,
+                                                                BlockDescriptor                        *rightNode,
+                                                                u_int32_t                                       rightNodeNum,
+                                                                u_int16_t                                       index,
+                                                                KeyPtr                                          keyPtr,
+                                                                u_int8_t *                                      recPtr,
+                                                                u_int16_t                                       recSize,
+                                                                u_int16_t                                      *insertIndex,
+                                                                u_int32_t                                      *insertNodeNum,
+                                                                u_int16_t                                      *recsRotated )
+{
+       OSStatus                        err;
+       NodeDescPtr                     left, right;
+       u_int32_t                       newNodeNum;
+       Boolean                         recordFit;
+       
+       
+       ///////////////////////////// Compare Nodes /////////////////////////////////
+
+       right = rightNode->buffer;
+       left  = leftNode->buffer;
+       
+       PanicIf ( right->bLink != 0 && left == 0, " SplitLeft: left sibling missing!?" );
+       
+       /* type should be kBTLeafNode or kBTIndexNode */
+       
+       if ( (right->height == 1) && (right->kind != kBTLeafNode) )
+               return  fsBTInvalidNodeErr;
+       
+       if ( left != nil )
+       {
+               if ( left->fLink != rightNodeNum )
+                       return fsBTInvalidNodeErr;                                                                              //\80\80 E_BadSibling ?
+       
+               if ( left->height != right->height )
+                       return  fsBTInvalidNodeErr;                                                                             //\80\80 E_BadNodeHeight ?
+               
+               if ( left->kind != right->kind )
+                       return  fsBTInvalidNodeErr;                                                                             //\80\80 E_BadNodeType ?
+       }
+       
+
+       ///////////////////////////// Allocate Node /////////////////////////////////
+
+       err = AllocateNode (btreePtr, &newNodeNum);
+       M_ExitOnError (err);
+       
+
+       /////////////// Update Forward Link In Original Left Node ///////////////////
+
+       if ( left != nil )
+       {
+               // XXXdbg
+               ModifyBlockStart(btreePtr->fileRefNum, leftNode);
+
+               left->fLink     = newNodeNum;
+               err = UpdateNode (btreePtr, leftNode, 0, kLockTransaction);
+               M_ExitOnError (err);
+       }
+
+
+       /////////////////////// Initialize New Left Node ////////////////////////////
+
+       err = GetNewNode (btreePtr, newNodeNum, leftNode);
+       M_ExitOnError (err);
+       
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, leftNode);
+
+       left            = leftNode->buffer;
+       left->fLink     = rightNodeNum;
+       
+
+       // Steal Info From Right Node
+       
+       left->bLink  = right->bLink;
+       left->kind   = right->kind;
+       left->height = right->height;
+       
+       right->bLink            = newNodeNum;                   // update Right bLink
+
+       if ( (left->kind == kBTLeafNode) && (left->bLink == 0) )
+       {
+               // if we're adding a new first leaf node - update BTreeInfoRec
+               
+               btreePtr->firstLeafNode = newNodeNum;
+               M_BTreeHeaderDirty (btreePtr);          //\80\80 AllocateNode should have set the bit already...
+       }
+
+       ////////////////////////////// Rotate Left //////////////////////////////////
+
+       err = RotateLeft (btreePtr, left, right, index, keyPtr, recPtr, recSize,
+                                         insertIndex, insertNodeNum, &recordFit, recsRotated);
+       
+       M_ExitOnError (err);
+
+       return noErr;
+       
+ErrorExit:
+       
+       (void) ReleaseNode (btreePtr, leftNode);
+       (void) ReleaseNode (btreePtr, rightNode);
+       
+       //\80\80 Free new node if allocated?
+
+       *insertIndex    = 0;
+       *insertNodeNum  = 0;
+       *recsRotated    = 0;
+       
+       return  err;
+}
+
+
+
+/////////////////////////////// RotateRecordLeft ////////////////////////////////
+
+static Boolean RotateRecordLeft (BTreeControlBlockPtr          btreePtr,
+                                                                NodeDescPtr                            leftNode,
+                                                                NodeDescPtr                            rightNode )
+{
+       u_int16_t       size;
+       u_int8_t *      recPtr;
+       Boolean         recordFit;
+       
+       size    = GetRecordSize (btreePtr, rightNode, 0);
+       recPtr  = GetRecordAddress (btreePtr, rightNode, 0);
+       
+       recordFit = InsertRecord (btreePtr, leftNode, leftNode->numRecords, recPtr, size);
+       
+       if ( !recordFit )
+               return false;
+       
+       DeleteRecord (btreePtr, rightNode, 0);
+       
+       return true;
+}
+
+
+//////////////////////////////// AddNewRootNode /////////////////////////////////
+
+static OSStatus        AddNewRootNode  (BTreeControlBlockPtr    btreePtr,
+                                                                NodeDescPtr                     leftNode,
+                                                                NodeDescPtr                     rightNode )
+{
+       OSStatus                        err;
+       BlockDescriptor         rootNode;
+       u_int32_t                       rootNum;
+       KeyPtr                          keyPtr;
+       Boolean                         didItFit;
+       u_int16_t                       keyLength;      
+       
+       rootNode.buffer = nil;
+       rootNode.blockHeader = nil;
+
+       PanicIf (leftNode == nil, "AddNewRootNode: leftNode == nil");
+       PanicIf (rightNode == nil, "AddNewRootNode: rightNode == nil");
+       
+       
+       /////////////////////// Initialize New Root Node ////////////////////////////
+       
+       err = AllocateNode (btreePtr, &rootNum);
+       M_ExitOnError (err);
+       
+       err = GetNewNode (btreePtr, rootNum, &rootNode);
+       M_ExitOnError (err);
+               
+       // XXXdbg
+       ModifyBlockStart(btreePtr->fileRefNum, &rootNode);
+
+       ((NodeDescPtr)rootNode.buffer)->kind = kBTIndexNode;
+       ((NodeDescPtr)rootNode.buffer)->height  = ++btreePtr->treeDepth;
+       
+
+       ///////////////////// Insert Left Node Index Record /////////////////////////   
+
+       keyPtr = (KeyPtr) GetRecordAddress (btreePtr, leftNode, 0);
+       keyLength = GetKeyLength(btreePtr, keyPtr, false);
+
+       didItFit = InsertKeyRecord ( btreePtr, rootNode.buffer, 0, keyPtr, keyLength,
+                                                                (u_int8_t *) &rightNode->bLink, 4 );
+
+       PanicIf ( !didItFit, "AddNewRootNode:InsertKeyRecord failed for left index record");
+
+
+       //////////////////// Insert Right Node Index Record /////////////////////////
+
+       keyPtr = (KeyPtr) GetRecordAddress (btreePtr, rightNode, 0);
+       keyLength = GetKeyLength(btreePtr, keyPtr, false);
+
+       didItFit = InsertKeyRecord ( btreePtr, rootNode.buffer, 1, keyPtr, keyLength,
+                                                                (u_int8_t *) &leftNode->fLink, 4 );
+
+       PanicIf ( !didItFit, "AddNewRootNode:InsertKeyRecord failed for right index record");
+
+       
+       /////////////////////////// Release Root Node ///////////////////////////////
+       
+       err = UpdateNode (btreePtr, &rootNode, 0, kLockTransaction);
+       M_ExitOnError (err);
+       
+       // update BTreeInfoRec
+       
+       btreePtr->rootNode       = rootNum;
+       M_BTreeHeaderDirty(btreePtr);
+
+       return noErr;
+
+
+       ////////////////////////////// Error Exit ///////////////////////////////////
+
+ErrorExit:
+
+       return  err;
+}
+
+
+static u_int16_t       GetKeyLength ( const BTreeControlBlock *btreePtr, const BTreeKey *key, Boolean forLeafNode )
+{
+       u_int16_t length;
+
+       if ( forLeafNode || btreePtr->attributes & kBTVariableIndexKeysMask )
+               length = KeyLength (btreePtr, key);             // just use actual key length
+       else
+               length = btreePtr->maxKeyLength;                // fixed sized index key (i.e. HFS)             //\80\80 shouldn't we clear the pad bytes?
+
+       return length;
+}
+