clientcursor.h   clientcursor.h 
skipping to change at line 48 skipping to change at line 48
typedef long long CursorId; /* passed to the client so it can send back on getMore */ typedef long long CursorId; /* passed to the client so it can send back on getMore */
class Cursor; /* internal server cursor base class */ class Cursor; /* internal server cursor base class */
class ClientCursor; class ClientCursor;
class ParsedQuery; class ParsedQuery;
/* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps. /* todo: make this map be per connection. this will prevent cursor hij acking security attacks perhaps.
*/ */
typedef map<CursorId, ClientCursor*> CCById; typedef map<CursorId, ClientCursor*> CCById;
typedef multimap<DiskLoc, ClientCursor*> CCByLoc;
extern BSONObj id_obj; extern BSONObj id_obj;
class ClientCursor { class ClientCursor {
friend class CmdCursorInfo; friend class CmdCursorInfo;
DiskLoc _lastLoc; // use getter and setter n ot this (important) DiskLoc _lastLoc; // use getter and setter n ot this (important)
unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time unsigned _idleAgeMillis; // how long has the cursor been around, relative to server idle time
/* 0 = normal /* 0 = normal
1 = no timeout allowed 1 = no timeout allowed
100 = in use (pinned) -- see Pointer class 100 = in use (pinned) -- see Pointer class
*/ */
unsigned _pinValue; unsigned _pinValue;
bool _doingDeletes; bool _doingDeletes;
ElapsedTracker _yieldSometimesTracker; ElapsedTracker _yieldSometimesTracker;
static CCById clientCursorsById; static CCById clientCursorsById;
static CCByLoc byLoc; static long long numberTimedOut;
static boost::recursive_mutex ccmutex; // must use this for all s tatics above! static boost::recursive_mutex ccmutex; // must use this for all s tatics above!
static CursorId allocCursorId_inlock(); static CursorId allocCursorId_inlock();
public: public:
static void assertNoCursors();
/* use this to assure we don't in the background time out cursor wh ile it is under use. /* use this to assure we don't in the background time out cursor wh ile it is under use.
if you are using noTimeout() already, there is no risk anyway. if you are using noTimeout() already, there is no risk anyway.
Further, this mechanism guards against two getMore requests on t he same cursor executing Further, this mechanism guards against two getMore requests on t he same cursor executing
at the same time - which might be bad. That should never happen , but if a client driver at the same time - which might be bad. That should never happen , but if a client driver
had a bug, it could (or perhaps some sort of attack situation). had a bug, it could (or perhaps some sort of attack situation).
*/ */
class Pointer : boost::noncopyable { class Pointer : boost::noncopyable {
public: public:
ClientCursor *_c; ClientCursor *_c;
void release() { void release() {
skipping to change at line 140 skipping to change at line 139
DESTRUCTOR_GUARD ( reset(); ); DESTRUCTOR_GUARD ( reset(); );
} }
operator bool() { return _c; } operator bool() { return _c; }
ClientCursor * operator-> () { return _c; } ClientCursor * operator-> () { return _c; }
private: private:
ClientCursor *_c; ClientCursor *_c;
CursorId _id; CursorId _id;
}; };
/*const*/ CursorId cursorid; /*const*/ CursorId cursorid;
string ns; const string ns;
shared_ptr<Cursor> c; const shared_ptr<Cursor> c;
int pos; // # objects into the cursor so far int pos; // # objects into the cursor so far
BSONObj query; BSONObj query;
int _queryOptions; // see enum QueryOptions dbclient.h const int _queryOptions; // see enum QueryOptions dbclient.h
OpTime _slaveReadTill; OpTime _slaveReadTill;
Database * const _db;
ClientCursor(int queryOptions, shared_ptr<Cursor>& _c, const string & _ns) : ClientCursor(int queryOptions, shared_ptr<Cursor>& _c, const string & _ns) :
_idleAgeMillis(0), _pinValue(0), _idleAgeMillis(0), _pinValue(0),
_doingDeletes(false), _yieldSometimesTracker(128,10), _doingDeletes(false), _yieldSometimesTracker(128,10),
ns(_ns), c(_c), ns(_ns), c(_c),
pos(0), _queryOptions(queryOptions) pos(0), _queryOptions(queryOptions),
_db( cc().database() )
{ {
assert( _db );
assert( str::startsWith(_ns, _db->name) );
if( queryOptions & QueryOption_NoCursorTimeout ) if( queryOptions & QueryOption_NoCursorTimeout )
noTimeout(); noTimeout();
recursive_scoped_lock lock(ccmutex); recursive_scoped_lock lock(ccmutex);
cursorid = allocCursorId_inlock(); cursorid = allocCursorId_inlock();
clientCursorsById.insert( make_pair(cursorid, this) ); clientCursorsById.insert( make_pair(cursorid, this) );
} }
~ClientCursor(); ~ClientCursor();
DiskLoc lastLoc() const { DiskLoc lastLoc() const {
return _lastLoc; return _lastLoc;
skipping to change at line 309 skipping to change at line 312
/* if ( !ids_.get() ) /* if ( !ids_.get() )
return; return;
stringstream ss; stringstream ss;
ss << ns << "." << cursorid; ss << ns << "." << cursorid;
ids_->mayUpgradeStorage( ss.str() );*/ ids_->mayUpgradeStorage( ss.str() );*/
} }
/** /**
* @param millis amount of idle passed time since last call * @param millis amount of idle passed time since last call
*/ */
bool shouldTimeout( unsigned millis ){ bool shouldTimeout( unsigned millis );
_idleAgeMillis += millis;
return _idleAgeMillis > 600000 && _pinValue == 0;
}
void storeOpForSlave( DiskLoc last ); void storeOpForSlave( DiskLoc last );
void updateSlaveLocation( CurOp& curop ); void updateSlaveLocation( CurOp& curop );
unsigned idleTime(){ unsigned idleTime(){
return _idleAgeMillis; return _idleAgeMillis;
} }
static void idleTimeReport(unsigned millis); static void idleTimeReport(unsigned millis);
private: private:
// cursors normally timeout after an inactivy period to prevent exc ess memory use // cursors normally timeout after an inactivy period to prevent exc ess memory use
// setting this prevents timeout of the cursor in question. // setting this prevents timeout of the cursor in question.
void noTimeout() { void noTimeout() {
_pinValue++; _pinValue++;
} }
multimap<DiskLoc, ClientCursor*>& byLoc() {
return _db->ccByLoc;
}
public: public:
void setDoingDeletes( bool doingDeletes ){ void setDoingDeletes( bool doingDeletes ){
_doingDeletes = doingDeletes; _doingDeletes = doingDeletes;
} }
static unsigned byLocSize(); // just for diagnostics static void appendStats( BSONObjBuilder& result );
static unsigned numCursors() { return clientCursorsById.size(); }
static void informAboutToDeleteBucket(const DiskLoc& b); static void informAboutToDeleteBucket(const DiskLoc& b);
static void aboutToDelete(const DiskLoc& dl); static void aboutToDelete(const DiskLoc& dl);
static void find( const string& ns , set<CursorId>& all ); static void find( const string& ns , set<CursorId>& all );
}; };
class ClientCursorMonitor : public BackgroundJob { class ClientCursorMonitor : public BackgroundJob {
public: public:
void run(); void run();
 End of changes. 12 change blocks. 
13 lines changed or deleted 19 lines changed or added


 curop.h   curop.h 
skipping to change at line 87 skipping to change at line 87
void setNS(const char *ns) { void setNS(const char *ns) {
strncpy(_ns, ns, Namespace::MaxNsLen); strncpy(_ns, ns, Namespace::MaxNsLen);
} }
public: public:
int querySize() const { return *((int *) _queryBuf); } int querySize() const { return *((int *) _queryBuf); }
bool haveQuery() const { return querySize() != 0; } bool haveQuery() const { return querySize() != 0; }
BSONObj query() { BSONObj query( bool threadSafe = false);
if( querySize() == 1 ) {
return _tooBig;
}
BSONObj o(_queryBuf);
return o;
}
void ensureStarted(){ void ensureStarted(){
if ( _start == 0 ) if ( _start == 0 )
_start = _checkpoint = curTimeMicros64(); _start = _checkpoint = curTimeMicros64();
} }
void enter( Client::Context * context ){ void enter( Client::Context * context ){
ensureStarted(); ensureStarted();
setNS( context->ns() ); setNS( context->ns() );
if ( context->_db && context->_db->profile > _dbprofile ) if ( context->_db && context->_db->profile > _dbprofile )
_dbprofile = context->_db->profile; _dbprofile = context->_db->profile;
 End of changes. 1 change blocks. 
7 lines changed or deleted 1 lines changed or added


 database.h   database.h 
skipping to change at line 25 skipping to change at line 25
* You should have received a copy of the GNU Affero General Public Licen se * You should have received a copy of the GNU Affero General Public Licen se
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include "cmdline.h" #include "cmdline.h"
namespace mongo { namespace mongo {
class ClientCursor;
/** /**
* Database represents a database database * Database represents a database database
* Each database database has its own set of files -- dbname.ns, dbname .0, dbname.1, ... * Each database database has its own set of files -- dbname.ns, dbname .0, dbname.1, ...
* NOT memory mapped * NOT memory mapped
*/ */
class Database { class Database {
public: public:
static bool _openAllFiles; static bool _openAllFiles;
Database(const char *nm, bool& newDb, const string& _path = dbpath) ; Database(const char *nm, bool& newDb, const string& _path = dbpath) ;
skipping to change at line 199 skipping to change at line 201
} }
void flushFiles( bool sync ); void flushFiles( bool sync );
vector<MongoDataFile*> files; vector<MongoDataFile*> files;
string name; // "alleyinsider" string name; // "alleyinsider"
string path; string path;
NamespaceIndex namespaceIndex; NamespaceIndex namespaceIndex;
int profile; // 0=off. int profile; // 0=off.
string profileName; // "alleyinsider.system.profile" string profileName; // "alleyinsider.system.profile"
multimap<DiskLoc, ClientCursor*> ccByLoc;
int magic; // used for making sure the object is still loaded in me mory int magic; // used for making sure the object is still loaded in me mory
}; };
} // namespace mongo } // namespace mongo
 End of changes. 2 change blocks. 
0 lines changed or deleted 5 lines changed or added


 dbclient.h   dbclient.h 
skipping to change at line 109 skipping to change at line 109
class ConnectionString { class ConnectionString {
public: public:
enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC }; enum ConnectionType { INVALID , MASTER , PAIR , SET , SYNC };
ConnectionString( const HostAndPort& server ){ ConnectionString( const HostAndPort& server ){
_type = MASTER; _type = MASTER;
_servers.push_back( server ); _servers.push_back( server );
_finishInit(); _finishInit();
} }
ConnectionString( ConnectionType type , const vector<HostAndPort>& // TODO Delete if nobody is using
servers ) //ConnectionString( ConnectionType type , const vector<HostAndPort>
: _type( type ) , _servers( servers ){ & servers )
_finishInit(); // : _type( type ) , _servers( servers ){
} // _finishInit();
//}
ConnectionString( ConnectionType type , const string& s , const str ing& setName = "" ){ ConnectionString( ConnectionType type , const string& s , const str ing& setName = "" ){
_type = type; _type = type;
_setName = setName; _setName = setName;
_fillServers( s ); _fillServers( s );
switch ( _type ){ switch ( _type ){
case MASTER: case MASTER:
assert( _servers.size() == 1 ); assert( _servers.size() == 1 );
break; break;
skipping to change at line 159 skipping to change at line 160
bool isValid() const { return _type != INVALID; } bool isValid() const { return _type != INVALID; }
string toString() const { string toString() const {
return _string; return _string;
} }
DBClientBase* connect( string& errmsg ) const; DBClientBase* connect( string& errmsg ) const;
static ConnectionString parse( const string& url , string& errmsg ) ; static ConnectionString parse( const string& url , string& errmsg ) ;
string getSetName() const{
return _setName;
}
vector<HostAndPort> getServers() const {
return _servers;
}
private: private:
ConnectionString(){ ConnectionString(){
_type = INVALID; _type = INVALID;
} }
void _fillServers( string s ){ void _fillServers( string s ){
string::size_type idx; string::size_type idx;
while ( ( idx = s.find( ',' ) ) != string::npos ){ while ( ( idx = s.find( ',' ) ) != string::npos ){
_servers.push_back( s.substr( 0 , idx ) ); _servers.push_back( s.substr( 0 , idx ) );
skipping to change at line 317 skipping to change at line 326
BSONObjBuilder b; BSONObjBuilder b;
b.appendElements(obj); b.appendElements(obj);
b.append(fieldName, val); b.append(fieldName, val);
obj = b.obj(); obj = b.obj();
} }
}; };
/** Typically one uses the QUERY(...) macro to construct a Query object. /** Typically one uses the QUERY(...) macro to construct a Query object.
Example: QUERY( "age" << 33 << "school" << "UCLA" ) Example: QUERY( "age" << 33 << "school" << "UCLA" )
*/ */
#define QUERY(x) Query( BSON(x) ) #define QUERY(x) mongo::Query( BSON(x) )
/** /**
interface that handles communication with the db interface that handles communication with the db
*/ */
class DBConnector { class DBConnector {
public: public:
virtual ~DBConnector() {} virtual ~DBConnector() {}
virtual bool call( Message &toSend, Message &response, bool assertO k=true ) = 0; virtual bool call( Message &toSend, Message &response, bool assertO k=true ) = 0;
virtual void say( Message &toSend ) = 0; virtual void say( Message &toSend ) = 0;
virtual void sayPiggyBack( Message &toSend ) = 0; virtual void sayPiggyBack( Message &toSend ) = 0;
skipping to change at line 1000 skipping to change at line 1009
virtual void say( Message &toSend ) { checkMaster()->say( toSend ); } virtual void say( Message &toSend ) { checkMaster()->say( toSend ); }
virtual bool callRead( Message& toSend , Message& response ){ retur n checkMaster()->callRead( toSend , response ); } virtual bool callRead( Message& toSend , Message& response ){ retur n checkMaster()->callRead( toSend , response ); }
virtual ConnectionString::ConnectionType type() const { return Conn ectionString::SET; } virtual ConnectionString::ConnectionType type() const { return Conn ectionString::SET; }
virtual bool isMember( const DBConnector * conn ) const; virtual bool isMember( const DBConnector * conn ) const;
virtual void checkResponse( const char *data, int nReturned ) { che ckMaster()->checkResponse( data , nReturned ); } virtual void checkResponse( const char *data, int nReturned ) { che ckMaster()->checkResponse( data , nReturned ); }
protected: protected:
virtual void sayPiggyBack( Message &toSend ) { assert(false); } virtual void sayPiggyBack( Message &toSend ) { checkMaster()->say( toSend ); }
bool isFailed() const { bool isFailed() const {
return _currentMaster == 0 || _currentMaster->isFailed(); return _currentMaster == 0 || _currentMaster->isFailed();
} }
}; };
/** pings server to check if it's up /** pings server to check if it's up
*/ */
bool serverAlive( const string &uri ); bool serverAlive( const string &uri );
 End of changes. 4 change blocks. 
7 lines changed or deleted 16 lines changed or added


 grid.h   grid.h 
skipping to change at line 60 skipping to change at line 60
bool allowLocalHost() const; bool allowLocalHost() const;
/** /**
* @param whether to allow shards and config servers to use 'localh ost' in address * @param whether to allow shards and config servers to use 'localh ost' in address
*/ */
void setAllowLocalHost( bool allow ); void setAllowLocalHost( bool allow );
/** /**
* *
* addShard will create a new shard in the grid. It expects a mongo d process to be runing * addShard will create a new shard in the grid. It expects a mongo d process to be runing
* on the provided address. * on the provided address. Adding a shard that is a replica set is
* TODO - add the mongod's databases to the grid supported.
* *
* @param name is an optional string with the name of the shard. if ommited, grid will * @param name is an optional string with the name of the shard. if ommited, grid will
* generate one and update the parameter. * generate one and update the parameter.
* @param host is the complete address of the machine where the sha rd will be * @param servers is the connection string of the shard being added
* @param maxSize is the optional space quota in bytes. Zeros means there's no limitation to * @param maxSize is the optional space quota in bytes. Zeros means there's no limitation to
* space usage * space usage
* @param errMsg is the error description in case the operation fai led. * @param errMsg is the error description in case the operation fai led.
* @return true if shard was successfully added. * @return true if shard was successfully added.
*/ */
bool addShard( string* name , const string& host , long long maxSiz e , string& errMsg ); bool addShard( string* name , const ConnectionString& servers , lon g long maxSize , string& errMsg );
/** /**
* @return true if the config database knows about a host 'name' * @return true if the config database knows about a host 'name'
*/ */
bool knowAboutShard( const string& name ) const; bool knowAboutShard( const string& name ) const;
/** /**
* @return true if the chunk balancing functionality is enabled * @return true if the chunk balancing functionality is enabled
*/ */
bool shouldBalance() const; bool shouldBalance() const;
 End of changes. 3 change blocks. 
4 lines changed or deleted 4 lines changed or added


 lasterror.h   lasterror.h 
skipping to change at line 35 skipping to change at line 35
struct LastError { struct LastError {
int code; int code;
string msg; string msg;
enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ; enum UpdatedExistingType { NotUpdate, True, False } updatedExisting ;
OID upsertedId; OID upsertedId;
OID writebackId; OID writebackId;
long long nObjects; long long nObjects;
int nPrev; int nPrev;
bool valid; bool valid;
bool overridenById;
bool disabled; bool disabled;
void writeback( OID& oid ){ void writeback( OID& oid ){
reset( true ); reset( true );
writebackId = oid; writebackId = oid;
} }
void raiseError(int _code , const char *_msg) { void raiseError(int _code , const char *_msg) {
reset( true ); reset( true );
code = _code; code = _code;
msg = _msg; msg = _msg;
} }
skipping to change at line 59 skipping to change at line 58
updatedExisting = _updateObjects ? True : False; updatedExisting = _updateObjects ? True : False;
if ( _upsertedId.isSet() ) if ( _upsertedId.isSet() )
upsertedId = _upsertedId; upsertedId = _upsertedId;
} }
void recordDelete( long long nDeleted ) { void recordDelete( long long nDeleted ) {
reset( true ); reset( true );
nObjects = nDeleted; nObjects = nDeleted;
} }
LastError() { LastError() {
overridenById = false;
reset(); reset();
} }
void reset( bool _valid = false ) { void reset( bool _valid = false ) {
code = 0; code = 0;
msg.clear(); msg.clear();
updatedExisting = NotUpdate; updatedExisting = NotUpdate;
nObjects = 0; nObjects = 0;
nPrev = 1; nPrev = 1;
valid = _valid; valid = _valid;
disabled = false; disabled = false;
skipping to change at line 130 skipping to change at line 128
* id of 0 means should use thread local management * id of 0 means should use thread local management
*/ */
void setID( int id ); void setID( int id );
int getID(); int getID();
void remove( int id ); void remove( int id );
void release(); void release();
/** when db receives a message/request, call this */ /** when db receives a message/request, call this */
void startRequest( Message& m , LastError * connectionOwned ); void startRequest( Message& m , LastError * connectionOwned );
LastError * startRequest( Message& m , int clientId = 0 ); LastError * startRequest( Message& m , int clientId );
void disconnect( int clientId ); void disconnect( int clientId );
// used to disable lastError reporting while processing a killCurso rs message // used to disable lastError reporting while processing a killCurso rs message
// disable causes get() to return 0. // disable causes get() to return 0.
LastError *disableForCommand(); // only call once per command invoc ation! LastError *disableForCommand(); // only call once per command invoc ation!
private: private:
ThreadLocalValue<int> _id; ThreadLocalValue<int> _id;
boost::thread_specific_ptr<LastError> _tl; boost::thread_specific_ptr<LastError> _tl;
 End of changes. 3 change blocks. 
3 lines changed or deleted 1 lines changed or added


 replpair.h   replpair.h 
skipping to change at line 167 skipping to change at line 167
} }
/* we allow queries to SimpleSlave's -- but not to the slave (nonmaster ) member of a replica pair /* we allow queries to SimpleSlave's -- but not to the slave (nonmaster ) member of a replica pair
so that queries to a pair are realtime consistent as much as possibl e. use setSlaveOk() to so that queries to a pair are realtime consistent as much as possibl e. use setSlaveOk() to
query the nonmaster member of a replica pair. query the nonmaster member of a replica pair.
*/ */
inline void replVerifyReadsOk(ParsedQuery& pq) { inline void replVerifyReadsOk(ParsedQuery& pq) {
if( replSet ) { if( replSet ) {
/* todo: speed up the secondary case. as written here there ar e 2 mutex entries, it can be 1. */ /* todo: speed up the secondary case. as written here there ar e 2 mutex entries, it can be 1. */
if( isMaster() ) return; if( isMaster() ) return;
notMasterUnless( pq.hasOption(QueryOption_SlaveOk) && theReplSe t->isSecondary() ); notMasterUnless( pq.hasOption(QueryOption_SlaveOk) && theReplSe t && theReplSet->isSecondary() );
} else { } else {
notMasterUnless(isMaster() || pq.hasOption(QueryOption_SlaveOk) || replSettings.slave == SimpleSlave ); notMasterUnless(isMaster() || pq.hasOption(QueryOption_SlaveOk) || replSettings.slave == SimpleSlave );
} }
} }
inline bool isMasterNs( const char *ns ) { inline bool isMasterNs( const char *ns ) {
char cl[ 256 ]; char cl[ 256 ];
nsToDatabase( ns, cl ); nsToDatabase( ns, cl );
return isMaster( cl ); return isMaster( cl );
} }
 End of changes. 1 change blocks. 
1 lines changed or deleted 1 lines changed or added


 scanandorder.h   scanandorder.h 
skipping to change at line 91 skipping to change at line 91
typedef multimap<BSONObj,BSONObj,BSONObjCmp> BestMap; typedef multimap<BSONObj,BSONObj,BSONObjCmp> BestMap;
class ScanAndOrder { class ScanAndOrder {
BestMap best; // key -> full object BestMap best; // key -> full object
int startFrom; int startFrom;
int limit; // max to send back. int limit; // max to send back.
KeyType order; KeyType order;
unsigned approxSize; unsigned approxSize;
void _add(BSONObj& k, BSONObj o, DiskLoc* loc) { void _add(BSONObj& k, BSONObj o, DiskLoc* loc) {
if (!loc){ if (!loc){
best.insert(make_pair(k,o)); best.insert(make_pair(k.getOwned(),o.getOwned()));
} else { } else {
BSONObjBuilder b; BSONObjBuilder b;
b.appendElements(o); b.appendElements(o);
b.append("$diskLoc", loc->toBSONObj()); b.append("$diskLoc", loc->toBSONObj());
best.insert(make_pair(k, b.obj())); best.insert(make_pair(k.getOwned(), b.obj().getOwned()));
} }
} }
void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i, DiskL oc* loc) { void _addIfBetter(BSONObj& k, BSONObj o, BestMap::iterator i, DiskL oc* loc) {
/* todo : we don't correct approxSize here. */
const BSONObj& worstBestKey = i->first; const BSONObj& worstBestKey = i->first;
int c = worstBestKey.woCompare(k, order.pattern); int c = worstBestKey.woCompare(k, order.pattern);
if ( c > 0 ) { if ( c > 0 ) {
// k is better, 'upgrade' // k is better, 'upgrade'
best.erase(i); best.erase(i);
_add(k, o, loc); _add(k, o, loc);
} }
} }
public: public:
skipping to change at line 127 skipping to change at line 128
int size() const { int size() const {
return best.size(); return best.size();
} }
void add(BSONObj o, DiskLoc* loc) { void add(BSONObj o, DiskLoc* loc) {
assert( o.isValid() ); assert( o.isValid() );
BSONObj k = order.getKeyFromObject(o); BSONObj k = order.getKeyFromObject(o);
if ( (int) best.size() < limit ) { if ( (int) best.size() < limit ) {
approxSize += k.objsize(); approxSize += k.objsize();
uassert( 10128 , "too much key data for sort() with no ind approxSize += o.objsize();
ex. add an index or specify a smaller limit", approxSize < 1 * 1024 * 1024
); /* note : adjust when bson return limit adjusts. note this
limit should be a bit higher. */
uassert( 10128 , "too much data for sort() with no index.
add an index or specify a smaller limit", approxSize < 32 * 1024 * 1024 );
_add(k, o, loc); _add(k, o, loc);
return; return;
} }
BestMap::iterator i; BestMap::iterator i;
assert( best.end() != best.begin() ); assert( best.end() != best.begin() );
i = best.end(); i = best.end();
i--; i--;
_addIfBetter(k, o, i, loc); _addIfBetter(k, o, i, loc);
} }
 End of changes. 4 change blocks. 
5 lines changed or deleted 10 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/