// Ryzom - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "stdpch.h" //#define TRACE_READ_DELTA //#define TRACE_WRITE_DELTA //#define TRACE_SET_VALUE ////////////// // Includes // ////////////// #include "cdb_branch.h" #include "cdb_leaf.h" #include "game_share/xml_auto_ptr.h" //#include #include "interface_v3/interface_manager.h" //////////////// // Namespaces // //////////////// using namespace NLMISC; using namespace std; #include "nel/misc/types_nl.h" #include "nel/misc/debug.h" #include "nel/misc/file.h" #include "nel/misc/i_xml.h" #include "nel/misc/progress_callback.h" #include //#include #include #include #include using namespace std; using namespace NLMISC; ///////////// // GLOBALS // ///////////// CCDBNodeBranch::CDBBranchObsInfo *CCDBNodeBranch::_FirstNotifiedObs[2] = { NULL, NULL }; CCDBNodeBranch::CDBBranchObsInfo *CCDBNodeBranch::_LastNotifiedObs[2] = { NULL, NULL }; CCDBNodeBranch::CDBBranchObsInfo *CCDBNodeBranch::_CurrNotifiedObs = NULL; CCDBNodeBranch::CDBBranchObsInfo *CCDBNodeBranch::_NextNotifiedObs = NULL; // uint CCDBNodeBranch::_CurrNotifiedObsList = 0; // Mapping from server database index to client database index (first-level nodes) vector CCDBNodeBranch::_CDBBankToUnifiedIndexMapping [NB_CDB_BANKS]; // Mapping from client database index to TCDBBank (first-level nodes) vector CCDBNodeBranch::_UnifiedIndexToBank; // Last index mapped uint CCDBNodeBranch::_CDBLastUnifiedIndex = 0; // Number of bits for first-level branches, by bank uint CCDBNodeBranch::_FirstLevelIdBitsByBank [NB_CDB_BANKS]; extern const char *CDBBankNames[INVALID_CDB_BANK+1]; // reset all static data void CCDBNodeBranch::reset() { for ( uint b=0; b!=INVALID_CDB_BANK; ++b ) _CDBBankToUnifiedIndexMapping[b].clear(); _UnifiedIndexToBank.clear(); _CDBLastUnifiedIndex = 0; _FirstNotifiedObs[0] = NULL; _FirstNotifiedObs[1] = NULL; _LastNotifiedObs[0] = NULL; _LastNotifiedObs[1] = NULL; _CurrNotifiedObsList = 0; _CurrNotifiedObs = NULL; _NextNotifiedObs = NULL; } // Internal use only. First-level bank-mapping. void CCDBNodeBranch::mapNodeByBank( ICDBNode * /* node */, const string& bankStr, bool /* clientOnly */, uint /* nodeIndex */ ) { /*if ( clientOnly ) { //nldebug( "CDB: Unified.%u is ClientOnly", _CDBLastUnifiedIndex ); ++_CDBLastUnifiedIndex; _UnifiedIndexToBank.push_back( CDBPlayer ); } else*/ // now clientOnly indices are known by the server as well { for ( uint b=0; b!=INVALID_CDB_BANK; ++b ) { if ( string(CDBBankNames[b]) == bankStr ) { //nldebug( "CDB: Mapping %s.%u to Unified.%u", CDBBankNames[b], _CDBBankToUnifiedIndexMapping[b].size(), _CDBLastUnifiedIndex ); _CDBBankToUnifiedIndexMapping[b].push_back( _CDBLastUnifiedIndex ); ++_CDBLastUnifiedIndex; _UnifiedIndexToBank.push_back( (TCDBBank)b ); break; } } } } //----------------------------------------------- // init // //----------------------------------------------- static /*inline*/ void addNode( ICDBNode *newNode, std::string newName, CCDBNodeBranch* parent, std::vector &nodes, std::vector &nodesSorted, xmlNodePtr &child, const string& bankName, bool atomBranch, bool clientOnly, NLMISC::IProgressCallback &progressCallBack, bool mapBanks ) { nodesSorted.push_back(newNode); nodes.push_back(newNode); nodes.back()->setParent(parent); nodes.back()->setAtomic( parent->isAtomic() || atomBranch ); nodes.back()->init(child, progressCallBack); // Setup bank mapping for first-level node if ( mapBanks && (parent->getParent() == NULL) ) { if ( ! bankName.empty() ) { CCDBNodeBranch::mapNodeByBank( newNode, bankName, clientOnly, (uint)nodes.size()-1 ); //nldebug( "CDB: Mapping %s for %s (node %u)", newName.c_str(), bankName.c_str(), nodes.size()-1 ); } else { nlerror( "Missing bank for first-level node %s", newName.c_str() ); } } } void CCDBNodeBranch::init( xmlNodePtr node, NLMISC::IProgressCallback &progressCallBack, bool mapBanks ) { xmlNodePtr child; _Sorted = false; // look for other branches within this branch uint countNode = CIXml::countChildren (node, "branch") + CIXml::countChildren (node, "leaf"); uint nodeId = 0; for (child = CIXml::getFirstChildNode (node, "branch"); child; child = CIXml::getNextChildNode (child, "branch")) { // Progress bar progressCallBack.progress ((float)nodeId/(float)countNode); progressCallBack.pushCropedValues ((float)nodeId/(float)countNode, (float)(nodeId+1)/(float)countNode); CXMLAutoPtr name((const char*)xmlGetProp (child, (xmlChar*)"name")); CXMLAutoPtr count((const char*)xmlGetProp (child, (xmlChar*)"count")); CXMLAutoPtr bank((const char*)xmlGetProp (child, (xmlChar*)"bank")); CXMLAutoPtr atom((const char*)xmlGetProp (child, (xmlChar*)"atom")); CXMLAutoPtr clientonly((const char*)xmlGetProp (child, (xmlChar*)"clientonly")); string sBank, sAtom, sClientonly; if ( bank ) sBank = bank.getDatas(); if ( atom ) sAtom = (const char*)atom; if ( clientonly ) sClientonly = clientonly.getDatas(); nlassert((const char *) name != NULL); if ((const char *) count != NULL) { // dealing with an array of entries uint countAsInt; fromString((const char*) count, countAsInt); for (uint i=0;i 0 ) for ( idb=1; nbNodesOfBank > unsigned(1< 0 ) for ( _IdBits=1; _Nodes.size() > unsigned(1<<_IdBits) ; _IdBits++ ) {} else _IdBits = 0; } find(""); // Sort ! } //----------------------------------------------- // attachChild // //----------------------------------------------- void CCDBNodeBranch::attachChild( ICDBNode * node, string nodeName ) { nlassert(_Parent==NULL); if (node) { node->setParent(this); _Nodes.push_back( node ); //nldebug ( "CDB: Attaching node" ); _NodesByName.push_back( node ); _Sorted = false; } } // attachChild // //----------------------------------------------- // getLeaf // //----------------------------------------------- CCDBNodeLeaf *CCDBNodeBranch::getLeaf( const char *id, bool bCreate ) { // get the last name piece const char *last = strrchr( id, ':' ); if( !last ) return NULL; ICDBNode *pNode = find( &last[1] ); if( !pNode && bCreate ) { pNode = new CCDBNodeLeaf( id ); _Nodes.push_back( pNode ); _NodesByName.push_back( pNode ); _Sorted = false; pNode->setParent(this); } return dynamic_cast(pNode); } //----------------------------------------------- // getNode // //----------------------------------------------- ICDBNode * CCDBNodeBranch::getNode (const CTextId& id, bool bCreate) { // lookup next element from textid in my index => idx const string &str = id.readNext(); ICDBNode *pNode = find(str); // If the node do not exists if ( pNode == NULL ) { if (bCreate) { // Yoyo: must not be SERVER or LOCAL, cause definied through xml. // This may cause some important crash error //nlassert(id.size()>0); //nlassert(id.getElement(0)!="SERVER"); //nlassert(id.getElement(0)!="LOCAL"); ICDBNode *newNode; if (id.getCurrentIndex() == id.size() ) newNode= new CCDBNodeLeaf (str); else newNode= new CCDBNodeBranch (str); _Nodes.push_back( newNode ); _NodesByName.push_back( newNode ); _Sorted = false; newNode->setParent(this); pNode = newNode; } else { return NULL; } } // get property from child if (!id.hasElements()) return pNode; return pNode->getNode( id, bCreate ); } // getNode // //----------------------------------------------- // getNode // //----------------------------------------------- ICDBNode * CCDBNodeBranch::getNode( uint16 idx ) { if ( idx < _Nodes.size() ) return _Nodes[idx]; else return NULL; } // getNode // //----------------------------------------------- // write // //----------------------------------------------- void CCDBNodeBranch::write( CTextId& id, FILE * f) { uint i; for( i = 0; i < _Nodes.size(); i++ ) { id.push (*_Nodes[i]->getName()); _Nodes[i]->write(id,f); id.pop(); } } // write // //----------------------------------------------- // getProp // //----------------------------------------------- sint64 CCDBNodeBranch::getProp( CTextId& id ) { // lookup next element from textid in my index => idx const string &str = id.readNext(); ICDBNode *pNode = find( str ); nlassert( pNode != NULL ); // get property from child return pNode->getProp( id ); } // getProp // //----------------------------------------------- // setProp : // Set the value of a property (the update flag is set to true) // \param id is the text id of the property/grp // \param name is the name of the property // \param value is the value of the property // \return bool : 'true' if property found. //----------------------------------------------- bool CCDBNodeBranch::setProp( CTextId& id, sint64 value ) { // lookup next element from textid in my index => idx const string &str = id.readNext(); ICDBNode *pNode = find( str ); // Property not found. if(pNode == NULL) { nlwarning("Property %s not found in %s", str.c_str(), id.toString().c_str()); return false; } // set property in child pNode->setProp(id,value); // Done return true; }// setProp // /* * Update the database from the delta, but map the first level with the bank mapping (see _CDBBankToUnifiedIndexMapping) */ void CCDBNodeBranch::readAndMapDelta( NLMISC::TGameCycle gc, NLMISC::CBitMemStream& s, TCDBBank bank ) { nlassert( ! isAtomic() ); // root node mustn't be atomic // Read index uint32 idx; s.serial( idx, _FirstLevelIdBitsByBank[bank] ); // Translate bank index -> unified index idx = _CDBBankToUnifiedIndexMapping[bank][idx]; if (idx >= _Nodes.size()) { throw Exception ("idx %d > _Nodes.size() %d ", idx, _Nodes.size()); } // Display the Name if we are in verbose mode if ( VerboseDatabase ) { string displayStr = string("Reading: ") + *(_Nodes[idx]->getName()); //CInterfaceManager::getInstance()->getChatOutput()->addTextChild( ucstring( displayStr ),CRGBA(255,255,255,255)); nlinfo( "CDB: %s%s %u/%d", (!getParent())?"[root] ":"-", displayStr.c_str(), idx, _IdBits ); } // Apply delta to children nodes _Nodes[idx]->readDelta( gc, s ); } //----------------------------------------------- // readDelta // //----------------------------------------------- void CCDBNodeBranch::readDelta( NLMISC::TGameCycle gc, CBitMemStream & f ) { if ( isAtomic() ) { // Read the atom bitfield uint nbAtomElements = countLeaves(); if(VerboseDatabase) nlinfo( "CDB/ATOM: %u leaves", nbAtomElements ); CBitSet bitfield( nbAtomElements ); f.readBits( bitfield ); if ( ! bitfield.getVector().empty() ) { if(VerboseDatabase) { nldebug( "CDB/ATOM: Bitfield: %s LastBits:", bitfield.toString().c_str() ); f.displayLastBits( bitfield.size() ); } } // Set each modified property uint atomIndex; for ( uint i=0; i!=bitfield.size(); ++i ) { if ( bitfield[i] ) { if(VerboseDatabase) nldebug( "CDB/ATOM: Reading prop[%u] of atom", i ); atomIndex = i; CCDBNodeLeaf *leaf = findLeafAtCount( atomIndex ); if ( leaf ) leaf->readDelta( gc, f ); else nlwarning( "CDB: Can't find leaf with index %u in atom branch %s", i, getParent()?getName()->c_str():"(root)" ); } } } else { uint32 idx; f.serial(idx,_IdBits); if (idx >= _Nodes.size()) { throw Exception ("idx %d > _Nodes.size() %d ", idx, _Nodes.size()); } // Display the Name if we are in verbose mode if ( VerboseDatabase ) { string displayStr = string("Reading: ") + *(_Nodes[idx]->getName()); //CInterfaceManager::getInstance()->getChatOutput()->addTextChild( ucstring( displayStr ),CRGBA(255,255,255,255)); nlinfo( "CDB: %s%s %u/%d", (!getParent())?"[root] ":"-", displayStr.c_str(), idx, _IdBits ); } _Nodes[idx]->readDelta(gc, f); } }// readDelta // //----------------------------------------------- // clear // //----------------------------------------------- // For old debug of random crash (let it in case it come back) //static bool AllowTestYoyoWarning= true; void CCDBNodeBranch::clear() { // TestYoyo. Track the random crash at exit /*if(AllowTestYoyoWarning) { std::string name= getFullName(); nlinfo("** clear: %s", name.c_str()); }*/ vector::iterator itNode; for( itNode = _Nodes.begin(); itNode != _Nodes.end(); ++itNode ) { (*itNode)->clear(); // TestYoyo //AllowTestYoyoWarning= false; delete (*itNode); //AllowTestYoyoWarning= true; } _Nodes.clear(); _NodesByName.clear(); // must remove all branch observers, to avoid any problem in subsequent flushObserversCalls() removeAllBranchObserver(); } // clear // /* * Find the leaf which count is specified (if found, the returned value is non-null and count is 0) */ CCDBNodeLeaf *CCDBNodeBranch::findLeafAtCount( uint& count ) { vector::const_iterator itNode; for ( itNode = _Nodes.begin(); itNode != _Nodes.end(); ++itNode ) { CCDBNodeLeaf *leaf = (*itNode)->findLeafAtCount( count ); if ( leaf ) return leaf; } return NULL; } /* * Count the leaves */ uint CCDBNodeBranch::countLeaves() const { uint n = 0; vector::const_iterator itNode; for ( itNode = _Nodes.begin(); itNode != _Nodes.end(); ++itNode ) { n += (*itNode)->countLeaves(); } return n; } void CCDBNodeBranch::display (const std::string &prefix) { nlinfo("%sB %s", prefix.c_str(), _DBSM->localUnmap(_Name).c_str()); string newPrefix = " " + prefix; vector::const_iterator itNode; for ( itNode = _Nodes.begin(); itNode != _Nodes.end(); ++itNode ) { (*itNode)->display(newPrefix); } } void CCDBNodeBranch::removeNode (CTextId& id) { // Look for the node CCDBNodeBranch *pNode = dynamic_cast(getNode(id,false)); if (pNode == NULL) { nlwarning("node %s not found", id.toString().c_str()); return; } CCDBNodeBranch *pParent = pNode->_Parent; if (pParent== NULL) { nlwarning("parent node not found"); return; } // search index node unsorted uint indexNode; for (indexNode = 0; indexNode < pParent->_Nodes.size(); ++indexNode) if (pParent->_Nodes[indexNode] == pNode) break; if (indexNode == pParent->_Nodes.size()) { nlwarning("node not found"); return; } // search index node sorted uint indexSorted; for (indexSorted = 0; indexSorted < pParent->_NodesByName.size(); ++indexSorted) if (pParent->_NodesByName[indexSorted] == pNode) break; if (indexSorted == pParent->_NodesByName.size()) { nlwarning("node not found"); return; } // Remove node from parent pParent->_Nodes.erase (pParent->_Nodes.begin()+indexNode); pParent->_NodesByName.erase (pParent->_NodesByName.begin()+indexSorted); pParent->_Sorted = false; // Delete the node pNode->clear(); delete pNode; } //----------------------------------------------- void CCDBNodeBranch::flushObserversCalls() { H_AUTO ( RZ_Interface_flushObserversCalls ) // nlassert(_CrtCheckMemory()); _CurrNotifiedObs = _FirstNotifiedObs[_CurrNotifiedObsList]; while (_CurrNotifiedObs) { // modified node should now store them in other list when they're modified _CurrNotifiedObsList = 1 - _CurrNotifiedObsList; // switch list so that modified node are stored in the other list while (_CurrNotifiedObs) { _NextNotifiedObs = _CurrNotifiedObs->NextNotifiedObserver[1 - _CurrNotifiedObsList]; nlassert(_CurrNotifiedObs->Owner); if (_CurrNotifiedObs->Observer) _CurrNotifiedObs->Observer->update(_CurrNotifiedObs->Owner); if (_CurrNotifiedObs) // this may be modified by the call (if current observer is removed) { _CurrNotifiedObs->unlink(1 - _CurrNotifiedObsList); } _CurrNotifiedObs = _NextNotifiedObs; } nlassert(_FirstNotifiedObs[1 - _CurrNotifiedObsList] == NULL); nlassert(_LastNotifiedObs[1 - _CurrNotifiedObsList] == NULL); // update triggered link CInterfaceLink::updateTrigeredLinks(); // examine other list to see if nodes have been registered _CurrNotifiedObs = _FirstNotifiedObs[_CurrNotifiedObsList]; } CInterfaceLink::updateTrigeredLinks(); // should call it at least once // nlassert(_CrtCheckMemory()); } //----------------------------------------------- void CCDBNodeBranch::CDBBranchObsInfo::link(uint list, NLMISC::TStringId modifiedLeafName) { // If there a filter set? if (!PositiveLeafNameFilter.empty()) { // Don't link if modifiedLeafName is not in the filter if (std::find(PositiveLeafNameFilter.begin(), PositiveLeafNameFilter.end(), modifiedLeafName) == PositiveLeafNameFilter.end()) return; } // nlassert(_CrtCheckMemory()); nlassert(list < 2); if (Touched[list]) return; // already inserted in list Touched[list] = true; nlassert(!PrevNotifiedObserver[list]); nlassert(!NextNotifiedObserver[list]); if (!_FirstNotifiedObs[list]) { _FirstNotifiedObs[list] = _LastNotifiedObs[list] = this; } else { nlassert(!_LastNotifiedObs[list]->NextNotifiedObserver[list]); _LastNotifiedObs[list]->NextNotifiedObserver[list] = this; PrevNotifiedObserver[list] = _LastNotifiedObs[list]; _LastNotifiedObs[list] = this; } // nlassert(_CrtCheckMemory()); } //----------------------------------------------- void CCDBNodeBranch::CDBBranchObsInfo::unlink(uint list) { // nlassert(_CrtCheckMemory()); nlassert(list < 2); if (!Touched[list]) { // not linked in this list nlassert(PrevNotifiedObserver[list] == NULL); nlassert(NextNotifiedObserver[list] == NULL); return; } if (PrevNotifiedObserver[list]) { PrevNotifiedObserver[list]->NextNotifiedObserver[list] = NextNotifiedObserver[list]; } else { // this was the first node _FirstNotifiedObs[list] = NextNotifiedObserver[list]; } if (NextNotifiedObserver[list]) { NextNotifiedObserver[list]->PrevNotifiedObserver[list] = PrevNotifiedObserver[list]; } else { // this was the last node _LastNotifiedObs[list] = PrevNotifiedObserver[list]; } PrevNotifiedObserver[list] = NULL; NextNotifiedObserver[list] = NULL; Touched[list] = false; // nlassert(_CrtCheckMemory()); } //----------------------------------------------- void CCDBNodeBranch::linkInModifiedNodeList(NLMISC::TStringId modifiedLeafName) { // nlassert(_CrtCheckMemory()); CCDBNodeBranch *curr = this; do { for(TObsList::iterator it = curr->_Observers.begin(); it != curr->_Observers.end(); ++it) { it->link(_CurrNotifiedObsList, modifiedLeafName); } curr = curr->getParent(); } while(curr); // nlassert(_CrtCheckMemory()); } //----------------------------------------------- // addObserver // //----------------------------------------------- bool CCDBNodeBranch::addObserver(IPropertyObserver* observer,CTextId& id) { //test if this node is the desired one, if yes, add the observer to all the children nodes if ( id.getCurrentIndex() == id.size() ) { for (uint i = 0; i < _Nodes.size(); ++i) { if (!_Nodes[i]->addObserver(observer,id)) return false; } return true; } // lookup next element from textid in my index => idx const string &str = id.readNext(); ICDBNode *pNode = find( str ); // Property not found. if(pNode == NULL) { nlwarning(" Property %s not found", id.toString().c_str()); return false; } // set property in child pNode->addObserver(observer,id); return true; } // addObserver // //----------------------------------------------- // removeObserver // //----------------------------------------------- bool CCDBNodeBranch::removeObserver(IPropertyObserver* observer, CTextId& id) { //test if this node is the desired one, if yes, remove the observer to all the children nodes if ( id.getCurrentIndex() == id.size() ) { for (uint i = 0; i < _Nodes.size(); ++i) { if (!_Nodes[i]->removeObserver(observer, id)) return false; } return true; } // lookup next element from textid in my index => idx const string &str = id.readNext(); ICDBNode *pNode = find( str ); // Property not found. if(pNode == NULL) { nlwarning(" Property %s not found", id.toString().c_str()); return false; } // remove observer in child pNode->removeObserver(observer,id); return true; } // removeObserver // //----------------------------------------------- void CCDBNodeBranch::addBranchObserver(IPropertyObserver* observer, const std::vector& positiveLeafNameFilter) { CDBBranchObsInfo oi(observer, this, positiveLeafNameFilter); _Observers.push_front(oi); } //----------------------------------------------- void CCDBNodeBranch::addBranchObserver(const char *dbPathFromThisNode, ICDBNode::IPropertyObserver& observer, const char **positiveLeafNameFilter, uint positiveLeafNameFilterSize) { CCDBNodeBranch *branchNode; if (dbPathFromThisNode[0] == '\0') // empty string { branchNode = this; } else { branchNode = safe_cast(getNode(ICDBNode::CTextId(dbPathFromThisNode), false)); BOMB_IF (!branchNode, (*getName()) << ":" << dbPathFromThisNode << " branch missing in DB", return); } std::vector leavesToMonitor(positiveLeafNameFilterSize); for (uint i=0; i!=positiveLeafNameFilterSize; ++i) { leavesToMonitor[i] = string(positiveLeafNameFilter[i]); } branchNode->addBranchObserver(&observer, leavesToMonitor); } //----------------------------------------------- void CCDBNodeBranch::removeBranchObserver(const char *dbPathFromThisNode, ICDBNode::IPropertyObserver& observer) { CCDBNodeBranch *branchNode = safe_cast(getNode(ICDBNode::CTextId(dbPathFromThisNode), false)); BOMB_IF (!branchNode, (*getName()) << ":" << dbPathFromThisNode << " branch missing in DB", return); branchNode->removeBranchObserver(&observer); } //----------------------------------------------- bool CCDBNodeBranch::removeBranchObserver(IPropertyObserver* observer) { bool found = false; TObsList::iterator it = _Observers.begin(); while (it != _Observers.end()) { if (it->Observer == observer) { removeBranchInfoIt(it); it = _Observers.erase(it); found = true; } else { ++it; } } return found; } //----------------------------------------------- void CCDBNodeBranch::removeAllBranchObserver() { TObsList::iterator it = _Observers.begin(); while (it != _Observers.end()) { removeBranchInfoIt(it); ++it; } _Observers.clear(); } //----------------------------------------------- void CCDBNodeBranch::removeBranchInfoIt(TObsList::iterator it) { CDBBranchObsInfo *oi = &(*it); // if this node is currenlty being notified, update iterators if (oi == _CurrNotifiedObs) { _CurrNotifiedObs = NULL; } if (oi == _NextNotifiedObs) { nlassert(_CurrNotifiedObsList < 2); _NextNotifiedObs = _NextNotifiedObs->NextNotifiedObserver[1 - _CurrNotifiedObsList]; } // unlink observer from both update lists oi->unlink(0); oi->unlink(1); } //----------------------------------------------- // Usefull for find //----------------------------------------------- class CCDBNodeBranchComp : public std::binary_function { public: bool operator()(const ICDBNode * x, const ICDBNode * y) const { return *(x->getName()) < *(y->getName()); } }; class CCDBNodeBranchComp2 : public std::binary_function { public: bool operator()(const ICDBNode * x, const string & y) const { return *(x->getName()) < y; } }; //----------------------------------------------- ICDBNode *CCDBNodeBranch::find(const std::string &nodeName) { if (!_Sorted) { _Sorted = true; sort(_NodesByName.begin(), _NodesByName.end(), CCDBNodeBranchComp()); } CCDBNodeLeaf tmp(nodeName); vector::iterator it = lower_bound(_NodesByName.begin(), _NodesByName.end(), &tmp, CCDBNodeBranchComp()); if (it == _NodesByName.end()) return NULL; else { if (*(*it)->getName() == nodeName) return *it; else return NULL; } } #ifdef TRACE_READ_DELTA #undef TRACE_READ_DELTA #endif #ifdef TRACE_WRITE_DELTA #undef TRACE_WRITE_DELTA #endif #ifdef TRACE_SET_VALUE #undef TRACE_SET_VALUE #endif