//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1996. // // File: DirP.CXX // // Contents: Private CDirectory child tree methods // // Notes: // //-------------------------------------------------------------------------- #include "msfhead.cxx" #include "h/dirfunc.hxx" //+------------------------------------------------------------------------- // // Member: CDirectory::InsertEntry, private // // Synopsis: insert entry into child tree // // Arguments: [sidTree] -- storage entry in which to insert entry // [sidNew] -- new entry // [pdfnNew] -- new entry name // // Returns: S_OK, STG_E_FILEALREADYEXISTS, or other error // // Modifies: sidParent's child tree // // Algorithm: Search down the binary tree to find the leaf node to which // to add the new entry (failing if we find the name already // exists). Along the way we split nodes where needed to keep // the tree balanced. // //-------------------------------------------------------------------------- SCODE CDirectory::InsertEntry( SID sidTree, SID sidNew, CDfName const *pdfnNew) { SCODE sc; // To insert the key and keep the tree balanced, we need to know // the parent, grandparent, and greatgrandparent of the node we're // inserting. SID sidChild, sidParent, sidGrandParent, sidGreatGrandParent; CDirEntry *pdeParent; int iCmp; // When we're ready to insert, sidParent will be the entry to which we // attach sidNew sidParent = sidGrandParent = sidGreatGrandParent = sidTree; // Begin the search with the root of the child tree msfChk(GetDirEntry(sidTree, FB_NONE, &pdeParent)); sidChild = pdeParent->GetChild(); // Search down the child tree to find the correct leaf entry while (sidChild != NOSTREAM) { // The sidParent entry has a child along the search path, so we // move down the tree (letting go of sidParent and taking hold of // its child) ReleaseEntry(sidParent); // Check to see if we need to split this node (nothing is held) do { SID sidLeft, sidRight; BOOL fRed; { CDirEntry *pdeChild; msfChk(GetDirEntry(sidChild, FB_NONE, &pdeChild)); msfAssert(((sidTree != sidParent) || (pdeChild->GetColor() == DE_BLACK)) && aMsg("Dir tree corrupt - root child not black!")); sidLeft = pdeChild->GetLeftSib(); sidRight = pdeChild->GetRightSib(); ReleaseEntry(sidChild); } if (sidLeft == NOSTREAM || sidRight == NOSTREAM) break; { CDirEntry *pdeLeft; msfChk(GetDirEntry(sidLeft, FB_NONE, &pdeLeft)); fRed = (pdeLeft->GetColor() == DE_RED); ReleaseEntry(sidLeft); } if (!fRed) break; { CDirEntry *pdeRight; msfChk(GetDirEntry(sidRight, FB_NONE, &pdeRight)); fRed = (pdeRight->GetColor() == DE_RED); ReleaseEntry(sidRight); } if (fRed) msfChk(SplitEntry(pdfnNew, sidTree, sidGreatGrandParent, sidGrandParent, sidParent, sidChild, &sidChild)); } while (FALSE); // msfAssert(sidChild != NOSTREAM); // Advance the search sidGreatGrandParent = sidGrandParent; sidGrandParent = sidParent; sidParent = sidChild; msfChk(GetDirEntry(sidParent, FB_NONE, &pdeParent)); iCmp = NameCompare(pdfnNew, pdeParent->GetName()); if (iCmp == 0) { // The new name exactly matched an existing name. Fail. msfChkTo(EH_RelParent, STG_E_FILEALREADYEXISTS); } // Move down the tree, left or right depending on the comparison if (iCmp < 0) sidChild = pdeParent->GetLeftSib(); else sidChild = pdeParent->GetRightSib(); } msfAssert(sidChild == NOSTREAM); // We've found the position to insert the new entry. // We're going to dirty sidParent, so we need to change our holding flags ReleaseEntry(sidParent); msfChk(GetDirEntry(sidParent, FB_DIRTY, &pdeParent)); if (sidParent == sidTree) { // sidParent never made it past sidTree - we must be inserting the // first child into sidTree msfAssert(pdeParent->GetChild() == NOSTREAM); // The SplitInsert call below will make sidNew black. pdeParent->SetChild(sidNew); } else { msfAssert(iCmp != 0); // Use the comparison to determine which side to insert the new entry if (iCmp < 0) { msfAssert(pdeParent->GetLeftSib() == NOSTREAM); msfAssert(NameCompare(pdfnNew, pdeParent->GetName()) < 0); pdeParent->SetLeftSib(sidNew); } else { msfAssert(pdeParent->GetRightSib() == NOSTREAM); msfAssert(NameCompare(pdfnNew, pdeParent->GetName()) > 0); pdeParent->SetRightSib(sidNew); } } EH_RelParent: ReleaseEntry(sidParent); if (SUCCEEDED(sc)) { SID sidTemp; sc = SplitEntry(pdfnNew, sidTree, sidGreatGrandParent, sidGrandParent, sidParent, sidNew, &sidTemp); } Err: return(sc); } //+------------------------------------------------------------------------- // // Member: CDirectory::SplitEntry, private // // Synopsis: Split 4-node // // Effects: Passes up red link to parent // // Arguments: [pdfn] -- search key // [sidTree] -- child tree sid // [sidGreat] -- greatgrandparent of child to split // [sidGrand] -- grandparent of child to split // [sidParent] -- parent of child to split // [sidChild] -- child to split // [psid] -- place holder for tree position // // Returns: S_OK, or error // // Modifies: psid, tree // // Algorithm: // // Notes: // //-------------------------------------------------------------------------- SCODE CDirectory::SplitEntry( CDfName const *pdfn, SID sidTree, SID sidGreat, SID sidGrand, SID sidParent, SID sidChild, SID *psid) { SCODE sc; CDirEntry *pdeChild; SID sidLeft, sidRight; // pn is a 4-node; start split by moving red link up // pn->GetLeft()->SetColor(BLACK); msfChk(GetDirEntry(sidChild, FB_DIRTY, &pdeChild)); sidLeft = pdeChild->GetLeftSib(); sidRight = pdeChild->GetRightSib(); // The root must always be black; new non-root children are red pdeChild->SetColor((sidParent == sidTree) ? DE_BLACK : DE_RED); ReleaseEntry(sidChild); if (sidLeft != NOSTREAM) { msfChk(SetColorBlack(sidLeft)); } // pn->GetRight()->SetColor(BLACK); if (sidRight != NOSTREAM) { msfChk(SetColorBlack(sidRight)); } if (sidParent != sidTree) { CDirEntry *pdeParent; BOOL fRedParent; int iCmpParent; msfChk(GetDirEntry(sidParent, FB_NONE, &pdeParent)); fRedParent = (pdeParent->GetColor() == DE_RED); if (fRedParent) iCmpParent = NameCompare(pdfn, pdeParent->GetName()); ReleaseEntry(sidParent); // if (pnp->IsRed()) if (fRedParent) { int iCmpGrand; // parent is red - adjacent red links are not allowed // Note - grandparent may be sidTree if (sidGrand == sidTree) { iCmpGrand = 1; } else { CDirEntry *pdeGrand; msfChk(GetDirEntry(sidGrand, FB_DIRTY, &pdeGrand)); iCmpGrand = NameCompare(pdfn, pdeGrand->GetName()); // png->SetColor(RED); pdeGrand->SetColor(DE_RED); ReleaseEntry(sidGrand); } // if ((ikey < png->GetKey()) != (ikey < pnp->GetKey())) if ((iCmpGrand < 0) != (iCmpParent < 0)) { /* two cases: // // | | // g g // / \ // p p // \ / // x x // // the red links are oriented differently */ // pn = Rotate(ikey, png); msfChk(RotateEntry(pdfn, sidTree, sidGrand, &sidChild)); /* // | | // g g // / \ // x x // / \ // p p */ } // the red links are now oriented the same - we balance the tree // by rotating // pn = Rotate(ikey, pngg); msfChk(RotateEntry(pdfn, sidTree, sidGreat, &sidChild)); // pn->SetColor(BLACK); msfAssert(sidChild != sidTree); msfChk(SetColorBlack(sidChild)); } } // return(pn); *psid = sidChild; // The first node's link must always be black. #if DBG == 1 CDirEntry *pdeTree; msfChk(GetDirEntry(sidTree, FB_NONE, &pdeTree)); sidChild = pdeTree->GetChild(); ReleaseEntry(sidTree); msfChk(GetDirEntry(sidChild, FB_NONE, &pdeChild)); msfAssert(pdeChild->GetColor() == DE_BLACK); ReleaseEntry(sidChild); #endif Err: return(sc); } //+------------------------------------------------------------------------- // // Member: CDirectory::RotateEntry // // Synopsis: rotation for balancing // // Effects: rotates localized portion of child tree // // Arguments: [pdfn] -- search key // [sidTree] -- child tree sid // [sidParent] -- root of rotation // [psid] -- placeholder for root after rotation // // Returns: S_OK, or error // // Modifies: child tree // // Algorithm: // // Notes: // //-------------------------------------------------------------------------- SCODE CDirectory::RotateEntry( CDfName const *pdfn, SID sidTree, SID sidParent, SID *psid) { SCODE sc; int iCmp; // PNODE pnc, pngc; SID sidChild, sidGrand; // find the child CDirEntry *pdeParent, *pdeChild, *pdeGrand; msfChk(GetDirEntry(sidParent, FB_DIRTY, &pdeParent)); if (sidParent == sidTree) { sidChild = pdeParent->GetChild(); } else { iCmp = NameCompare(pdfn, pdeParent->GetName()); if (iCmp < 0) sidChild = pdeParent->GetLeftSib(); else sidChild = pdeParent->GetRightSib(); } // find the grandchild msfChkTo(EH_RelParent, GetDirEntry(sidChild, FB_DIRTY, &pdeChild)); msfAssert(sidChild != sidTree); iCmp = NameCompare(pdfn, pdeChild->GetName()); if (iCmp < 0) { // pngc = pnc->GetLeft(); sidGrand = pdeChild->GetLeftSib(); msfChkTo(EH_RelChild, GetDirEntry(sidGrand, FB_DIRTY, &pdeGrand)); /* // | // c // / \ // / \ // g X // \ // Y */ // pnc->SetLeft(pngc->GetRight()); pdeChild->SetLeftSib(pdeGrand->GetRightSib()); /* // | // c // / \ // | \ // g | X // \| // Y */ // pngc->SetRight(pnc); pdeGrand->SetRightSib(sidChild); /* // g // \ // \| // c // / \ // | \ // | X // | // Y */ } else { // pngc = pnc->GetRight(); sidGrand = pdeChild->GetRightSib(); msfChkTo(EH_RelChild, GetDirEntry(sidGrand, FB_DIRTY, &pdeGrand)); // pnc->SetRight(pngc->GetLeft()); pdeChild->SetRightSib(pdeGrand->GetLeftSib()); // pngc->SetLeft(pnc); pdeGrand->SetLeftSib(sidChild); } // update parent if (sidParent == sidTree) { // The root must always be black pdeGrand->SetColor(DE_BLACK); pdeParent->SetChild(sidGrand); } else { iCmp = NameCompare(pdfn, pdeParent->GetName()); if (iCmp < 0) { // pnp->SetLeft(pngc); pdeParent->SetLeftSib(sidGrand); } else { // pnp->SetRight(pngc); pdeParent->SetRightSib(sidGrand); } } ReleaseEntry(sidGrand); /* // | // g // \ // \ // c // / \ // | \ // | X // | // Y */ // return(pngc); *psid = sidGrand; EH_RelChild: ReleaseEntry(sidChild); EH_RelParent: ReleaseEntry(sidParent); Err: return(sc); } //+------------------------------------------------------------------------- // // Member: CDirectory::FindEntry, private // // Synopsis: find entry info based on name (optionally removing it) // // Effects: find - none, remove - takes entry out of child list // // Arguments: [sidParent] -- sid of parent entry to search // [pdfn] -- name to search for // [deop] -- entry operation (find or remove) // [peb] -- entry information buffer // // Returns: S_OK, STG_E_FILENOTFOUND, or other error // // Modifies: peb // // Algorithm: To find the entry we search down the binary tree. // To remove the entry, we need to patch the tree to keep it // as a valid binary tree. // //-------------------------------------------------------------------------- SCODE CDirectory::FindEntry( SID sidParent, CDfName const *pdfn, DIRENTRYOP deop, SEntryBuffer *peb) { SCODE sc; SID sidPrev, sidFind; CDirEntry *pdePrev, *pdeFind; int iCmp; // Once we've found the right child, sidPrev will be that entry's parent // in the child tree sidPrev = sidParent; // Begin the search with the root of the child tree msfChk(GetDirEntry(sidPrev, FB_NONE, &pdePrev)); sidFind = pdePrev->GetChild(); // sidPrev is held for(;;) { if (sidFind == NOSTREAM) { // we didn't find the child. fail. sc = STG_E_FILENOTFOUND; goto EH_RelPrev; // Removed this line to supress the debug error print. // msfChkTo(EH_RelPrev, STG_E_FILENOTFOUND); } msfChkTo(EH_RelPrev, GetDirEntry(sidFind, FB_NONE, &pdeFind)); // sidPrev and sidFind are held int tmpCmp = NameCompare(pdfn, pdeFind->GetName()); if (tmpCmp == 0) { // We found the entry that matches our search name break; } // The names did not match. Advance the search down the tree. ReleaseEntry(sidPrev); pdePrev = pdeFind; sidPrev = sidFind; // sidPrev is held // remember the comparison with sidPrev so we can use it to insert // an entry when we patch the tree iCmp = tmpCmp; if (iCmp < 0) sidFind = pdePrev->GetLeftSib(); else sidFind = pdePrev->GetRightSib(); } msfAssert(sidFind != NOSTREAM); // sidFind is held // sidPrev is held msfAssert(NameCompare(pdfn, pdeFind->GetName()) == 0); // fill in entry information peb->sid = sidFind; peb->dwType = pdeFind->GetFlags(); peb->luid = DF_NOLUID; if (deop == DEOP_REMOVE) { ReleaseEntry(sidFind); ReleaseEntry(sidPrev); msfChk(GetDirEntry(sidPrev, FB_DIRTY, &pdePrev)); msfChkTo(EH_RelPrev, GetDirEntry(sidFind, FB_DIRTY, &pdeFind)); // Remove the found child from tree (carefully!). We remove it by // finding another entry in the tree with which to replace it. // sidFind is the node we're removing // sidPrev is the parent of sidFind in the child tree // sidInsert is the entry which will replace sidFind SID sidInsert = pdeFind->GetRightSib(); if (sidInsert == NOSTREAM) { // sidFind has no right child, so we can patch the tree by // replacing sidFind with the sidFind's left child sidInsert = pdeFind->GetLeftSib(); // set the inserted to the right color if (sidInsert != NOSTREAM) { // we always set the inserted node to black (since the // parent may not exist (we could be inserting at the // root) msfChkTo(EH_RelPrev, SetColorBlack(sidInsert)); } } else { CDirEntry *pdeInsert; // The node we're removing has a right child msfChkTo(EH_RelFind, GetDirEntry(sidInsert, FB_NONE, &pdeInsert)); // sidPrev, sidFind, and sidInsert are all held if (pdeInsert->GetLeftSib() != NOSTREAM) { // sidFind's right child has a left child. // sidInsert will be the leftmost child of sidFind's right // child (which will keep the tree ordered) // sidPreInsert will be the leftmost child's parent int the // child tree SID sidPreInsert = sidInsert; CDirEntry *pdePreInsert = pdeInsert; // we wait to assign sidInsert so we can clean up msfChkTo(EH_RelIns, GetDirEntry(pdePreInsert->GetLeftSib(), FB_NONE, &pdeInsert)); sidInsert = pdePreInsert->GetLeftSib(); // sidPrev, sidFind, sidPreInsert, sidInsert are held // find the leftmost child of sidFind's right child SID sidLeft; while ((sidLeft = pdeInsert->GetLeftSib()) != NOSTREAM) { ReleaseEntry(sidPreInsert); // sidPrev, sidFind, sidInsert are held sidPreInsert = sidInsert; pdePreInsert = pdeInsert; // we wait to assign sidInsert to we can clean up msfChkTo(EH_RelIns, GetDirEntry(sidLeft, FB_NONE, &pdeInsert)); sidInsert = sidLeft; } msfAssert(pdeInsert->GetLeftSib() == NOSTREAM); // sidPrev, sidFind, sidPreInsert, sidInsert are held // Remove sidInsert so we can reinsert it in place of sidFind. // We remove sidInsert (which has no left child) by making // sidPreInsert's left child point to sidInsert's right child ReleaseEntry(sidPreInsert); msfChkTo(EH_RelIns, GetDirEntry(sidPreInsert, FB_DIRTY, &pdePreInsert)); pdePreInsert->SetLeftSib(pdeInsert->GetRightSib()); ReleaseEntry(sidPreInsert); // sidPrev, sidFind, sidInsert is held // Begin to replace sidFind with sidInsert by setting the // right child of sidInsert to be the right child of sidFind ReleaseEntry(sidInsert); msfChkTo(EH_RelFind, GetDirEntry(sidInsert, FB_DIRTY, &pdeInsert)); pdeInsert->SetRightSib(pdeFind->GetRightSib()); } else { // sidFind's right child has no left child, so we can patch // the tree by making sidFind's right child's left child // point to sidFind's left child, and then replacing sidFind // with sidFind's right child. ReleaseEntry(sidInsert); msfChkTo(EH_RelFind, GetDirEntry(sidInsert, FB_DIRTY, &pdeInsert)); // fall through to do the work } pdeInsert->SetColor(DE_BLACK); // Complete sidInsert's patching by setting its left child to be // the left child of sidFind pdeInsert->SetLeftSib(pdeFind->GetLeftSib()); EH_RelIns: ReleaseEntry(sidInsert); } if (SUCCEEDED(sc)) { if (sidPrev == sidParent) { // We're removing the first child; update sidParent. // We made sure sidInsert is black (above). pdePrev->SetChild(sidInsert); } else if (iCmp < 0) { pdePrev->SetLeftSib(sidInsert); } else pdePrev->SetRightSib(sidInsert); // make sure sidFind is clean pdeFind->SetLeftSib(NOSTREAM); pdeFind->SetRightSib(NOSTREAM); } } EH_RelFind: ReleaseEntry(sidFind); EH_RelPrev: ReleaseEntry(sidPrev); Err: return(sc); } //+------------------------------------------------------------------------- // // Member: CDirectory::NameCompare, private static // // Synopsis: name ordering function for child tree // // Arguments: [pdfn1] - name 1 // [pdfn2] - name 2 // // Requires: One but not both names cannot may have zero length. // // Returns: <0 if name 1 < name 2 // 0 if name 1 = name 2 // >0 if name 1 > name 2 // // Algorithm: To speed the comparision (and to allow zero length names), // we first compare the name lengths. (Shorter names are "less" // than longer names). If the lengths are equal we compare the // strings. // //-------------------------------------------------------------------------- int CDirectory::NameCompare(CDfName const *pdfn1, CDfName const *pdfn2) { int iCmp = pdfn1->GetLength() - pdfn2->GetLength(); if (iCmp == 0) { msfAssert(pdfn1->GetLength() != 0); iCmp = dfwcsnicmp((WCHAR *)pdfn1->GetBuffer(), (WCHAR *)pdfn2->GetBuffer(), pdfn1->GetLength()); } return(iCmp); } //+------------------------------------------------------------------------- // // Method: CDirectory::SetColorBlack, private // // Synopsis: Sets a directory entry to black // // Arguments: [sid] -- SID of entry to be modified // // Returns: S_OK or error // // Notes: Added to reduce code size // //-------------------------------------------------------------------------- SCODE CDirectory::SetColorBlack(const SID sid) { SCODE sc; CDirEntry *pde; msfChk(GetDirEntry(sid, FB_DIRTY, &pde)); pde->SetColor(DE_BLACK); ReleaseEntry(sid); Err: return sc; }