diff options
-rw-r--r-- | src/mongo/db/exec/filter.h | 2 | ||||
-rw-r--r-- | src/mongo/db/field_ref.cpp | 75 | ||||
-rw-r--r-- | src/mongo/db/field_ref.h | 25 | ||||
-rw-r--r-- | src/mongo/db/field_ref_test.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/matcher/path.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/ops/modifier_rename.cpp | 6 |
6 files changed, 67 insertions, 44 deletions
diff --git a/src/mongo/db/exec/filter.h b/src/mongo/db/exec/filter.h index 8eb05759d0a..ff9916bfb27 100644 --- a/src/mongo/db/exec/filter.h +++ b/src/mongo/db/exec/filter.h @@ -82,7 +82,7 @@ namespace mongo { } // This should not happen. - massert(16920, "trying to match on unknown field: " + path->fieldRef().dottedField(), + massert(16920, "trying to match on unknown field: " + path->fieldRef().dottedField().toString(), 0); return new SingleElementElementIterator(BSONElement()); diff --git a/src/mongo/db/field_ref.cpp b/src/mongo/db/field_ref.cpp index 85d0c123fd9..15a1ed8c19f 100644 --- a/src/mongo/db/field_ref.cpp +++ b/src/mongo/db/field_ref.cpp @@ -108,6 +108,37 @@ namespace mongo { return ++_size; } + void FieldRef::reserialize() const { + std::string nextDotted; + // Reserve some space in the string. We know we will have, at minimum, a character for + // each component we are writing, and a dot for each component, less one. We don't want + // to reserve more, since we don't want to forfeit the SSO if it is applicable. + nextDotted.reserve((_size * 2) - 1); + + // Concatenate the fields to a new string + for (size_t i = 0; i != _size; ++i) { + if (i > 0) + nextDotted.append(1, '.'); + const StringData part = getPart(i); + nextDotted.append(part.rawData(), part.size()); + } + + // Make the new string our contents + _dotted.swap(nextDotted); + + // Fixup the parts to refer to the new string + std::string::const_iterator where = _dotted.begin(); + for (size_t i = 0; i != _size; ++i) { + StringData& part = (i < kReserveAhead) ? _fixed[i] : _variable[getIndex(i)]; + const size_t size = part.size(); + part = StringData(&*where, size); + where += (size + 1); // account for '.' + } + + // Drop any replacements + _replacements.clear(); + } + StringData FieldRef::getPart(size_t i) const { dassert(i < _size); @@ -152,25 +183,23 @@ namespace mongo { return prefixSize; } - std::string FieldRef::dottedField( size_t offset ) const { - std::string result; + StringData FieldRef::dottedField( size_t offset ) const { + if (_size == 0 || offset >= numParts() ) + return StringData(); - if (_size == 0 || offset >= numParts() ) { - // fall through, we will return the empty string. - } - else if (_replacements.empty() && (offset == 0)) { - result = _dotted; - } - else { - // Reserve some space in the string. We know we will have, at minimum, a character - // for each component we are writing, and a dot for each component, less one. - result.reserve(((_size - offset) * 2) - 1); - for (size_t i=offset; i<_size; i++) { - if ( i > offset ) - result.append(1, '.'); - StringData part = getPart(i); - result.append(part.rawData(), part.size()); - } + if (!_replacements.empty()) + reserialize(); + dassert(_replacements.empty()); + + // Assume we want the whole thing + StringData result(_dotted); + + // Strip off any leading parts we were asked to ignore + for (size_t i = 0; i < offset; ++i) { + const StringData part = getPart(i); + result = StringData( + result.rawData() + part.size() + 1, + result.size() - part.size() - 1); } return result; @@ -231,16 +260,6 @@ namespace mongo { _replacements.clear(); } - size_t FieldRef::numReplaced() const { - size_t res = 0; - for (size_t i = 0; i < _replacements.size(); i++) { - if (!_replacements[i].empty()) { - res++; - } - } - return res; - } - std::ostream& operator<<(std::ostream& stream, const FieldRef& field) { return stream << field.dottedField(); } diff --git a/src/mongo/db/field_ref.h b/src/mongo/db/field_ref.h index 681f9873932..06e60c2c165 100644 --- a/src/mongo/db/field_ref.h +++ b/src/mongo/db/field_ref.h @@ -90,7 +90,7 @@ namespace mongo { * Returns a copy of the full dotted field in its current state (i.e., some parts may * have been replaced since the parse() call). */ - std::string dottedField( size_t offset = 0 ) const; + StringData dottedField( size_t offsetFromStart = 0 ) const; /** * Compares the full dotted path represented by this FieldRef to other @@ -117,12 +117,6 @@ namespace mongo { */ size_t numParts() const { return _size; } - /** - * Returns the number of fields parts that were replaced so far. Replacing the same - * fields several times only counts for 1. - */ - size_t numReplaced() const; - private: // Dotted fields are most often not longer than four parts. We use a mixed structure // here that will not require any extra memory allocation when that is the case. And @@ -139,20 +133,29 @@ namespace mongo { */ size_t appendPart(const StringData& part); + /** + * Re-assemble _dotted from components, including any replacements in _replacements, + * and update the StringData components in _fixed and _variable to refer to the parts + * of the new _dotted. This is used to make the storage for the current value of this + * FieldRef contiguous so it can be returned as a StringData from the dottedField + * method above. + */ + void reserialize() const; + // number of field parts stored size_t _size; // first kResevedAhead field components - StringData _fixed[kReserveAhead]; + mutable StringData _fixed[kReserveAhead]; // remaining field components - std::vector<StringData> _variable; + mutable std::vector<StringData> _variable; // cached dotted name - std::string _dotted; + mutable std::string _dotted; // back memory added with the setPart call pointed to by _fized and _variable - std::vector<std::string> _replacements; + mutable std::vector<std::string> _replacements; }; inline bool operator==(const FieldRef& lhs, const FieldRef& rhs) { diff --git a/src/mongo/db/field_ref_test.cpp b/src/mongo/db/field_ref_test.cpp index 1d98c76b2e3..67336b8ffea 100644 --- a/src/mongo/db/field_ref_test.cpp +++ b/src/mongo/db/field_ref_test.cpp @@ -147,7 +147,6 @@ namespace { fieldRef.setPart(1, parts[i]); ASSERT_EQUALS(fieldRef.dottedField(), prefix + parts[i]); } - ASSERT_EQUALS(fieldRef.numReplaced(), 1U); } TEST( Prefix, Normal ) { diff --git a/src/mongo/db/matcher/path.cpp b/src/mongo/db/matcher/path.cpp index 8c94514ca63..cd4f1ee9c0a 100644 --- a/src/mongo/db/matcher/path.cpp +++ b/src/mongo/db/matcher/path.cpp @@ -110,7 +110,7 @@ namespace mongo { void BSONElementIterator::ArrayIterationState::reset( const FieldRef& ref, int start ) { - restOfPath = ref.dottedField( start ); + restOfPath = ref.dottedField( start ).toString(); hasMore = restOfPath.size() > 0; if ( hasMore ) { nextPieceOfPath = ref.getPart( start ); diff --git a/src/mongo/db/ops/modifier_rename.cpp b/src/mongo/db/ops/modifier_rename.cpp index f9505de53cc..cb7d2875afe 100644 --- a/src/mongo/db/ops/modifier_rename.cpp +++ b/src/mongo/db/ops/modifier_rename.cpp @@ -253,8 +253,10 @@ namespace mongo { dassert(_preparedState->applyCalled); const bool isPrefix = _fromFieldRef.isPrefixOf(_toFieldRef); - const string setPath = (isPrefix ? _fromFieldRef : _toFieldRef).dottedField(); - const string unsetPath = isPrefix ? "" : _fromFieldRef.dottedField(); + const StringData setPath = + (isPrefix ? _fromFieldRef : _toFieldRef).dottedField(); + const StringData unsetPath = + isPrefix ? StringData() : _fromFieldRef.dottedField(); const bool doUnset = !isPrefix; // We'd like to create an entry such as {$set: {<fieldname>: <value>}} under 'logRoot'. |