/** * Copyright (C) 2012 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/db/field_ref.h" #include // for min #include "mongo/util/assert_util.h" namespace mongo { FieldRef::FieldRef() : _size(0) {} FieldRef::FieldRef(StringData path) : _size(0) { parse(path); } void FieldRef::parse(StringData path) { if (path.size() == 0) { return; } if (_size != 0) { clear(); } // We guarantee that accesses through getPart() will be valid while 'this' is. So we // keep a copy in a local sting. _dotted = path.toString(); // Separate the field parts using '.' as a delimiter. std::string::iterator beg = _dotted.begin(); std::string::iterator cur = beg; const std::string::iterator end = _dotted.end(); while (true) { if (cur != end && *cur != '.') { cur++; continue; } // If cur != beg then we advanced cur in the loop above, so we have a real sequence // of characters to add as a new part. Otherwise, we may be parsing something odd, // like "..", and we need to add an empty StringData piece to represent the "part" // in-between the dots. This also handles the case where 'beg' and 'cur' are both // at 'end', which can happen if we are parsing anything with a terminal "." // character. In that case, we still need to add an empty part, but we will break // out of the loop below since we will not execute the guarded 'continue' and will // instead reach the break statement. if (cur != beg) appendPart(StringData(&*beg, cur - beg)); else appendPart(StringData()); if (cur != end) { beg = ++cur; continue; } break; } } void FieldRef::setPart(size_t i, StringData part) { dassert(i < _size); if (_replacements.size() != _size) { _replacements.resize(_size); } _replacements[i] = part.toString(); if (i < kReserveAhead) { _fixed[i] = _replacements[i]; } else { _variable[getIndex(i)] = _replacements[i]; } } size_t FieldRef::appendPart(StringData part) { if (_size < kReserveAhead) { _fixed[_size] = part; } else { _variable.push_back(part); } 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(); const std::string::const_iterator end = _dotted.end(); 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; // skip over '.' unless we are at the end. if (where != end) { dassert(*where == '.'); ++where; } } // Drop any replacements _replacements.clear(); } StringData FieldRef::getPart(size_t i) const { dassert(i < _size); if (i < kReserveAhead) { return _fixed[i]; } else { return _variable[getIndex(i)]; } } bool FieldRef::isPrefixOf( const FieldRef& other ) const { // Can't be a prefix if the size is equal to or larger. if ( _size >= other._size ) { return false; } // Empty FieldRef is not a prefix of anything. if ( _size == 0 ) { return false; } size_t common = commonPrefixSize( other ); return common == _size && other._size > common; } size_t FieldRef::commonPrefixSize( const FieldRef& other ) const { if (_size == 0 || other._size == 0) { return 0; } size_t maxPrefixSize = std::min( _size-1, other._size-1 ); size_t prefixSize = 0; while ( prefixSize <= maxPrefixSize ) { if ( getPart( prefixSize ) != other.getPart( prefixSize ) ) { break; } prefixSize++; } return prefixSize; } StringData FieldRef::dottedField( size_t offset ) const { return dottedSubstring(offset, numParts()); } StringData FieldRef::dottedSubstring(size_t startPart, size_t endPart) const { if (_size == 0 || startPart >= endPart || endPart > numParts()) return StringData(); if (!_replacements.empty()) reserialize(); dassert(_replacements.empty()); StringData result(_dotted); // Fast-path if we want the whole thing if (startPart == 0 && endPart == numParts()) return result; size_t startChar = 0; for (size_t i = 0; i < startPart; ++i) { startChar += getPart(i).size() + 1; // correct for '.' } size_t endChar = startChar; for (size_t i = startPart; i < endPart; ++i) { endChar += getPart(i).size() + 1; } // correct for last '.' if (endPart != numParts()) --endChar; return result.substr(startChar, endChar - startChar); } bool FieldRef::equalsDottedField( StringData other ) const { StringData rest = other; for ( size_t i = 0; i < _size; i++ ) { StringData part = getPart( i ); if ( !rest.startsWith( part ) ) return false; if ( i == _size - 1 ) return rest.size() == part.size(); // make sure next thing is a dot if ( rest.size() == part.size() ) return false; if ( rest[part.size()] != '.' ) return false; rest = rest.substr( part.size() + 1 ); } return false; } int FieldRef::compare(const FieldRef& other) const { const size_t toCompare = std::min(_size, other._size); for (size_t i = 0; i < toCompare; i++) { if (getPart(i) == other.getPart(i)) { continue; } return getPart(i) < other.getPart(i) ? -1 : 1; } const size_t rest = _size - toCompare; const size_t otherRest = other._size - toCompare; if ((rest == 0) && (otherRest == 0)) { return 0; } else if (rest < otherRest ) { return -1; } else { return 1; } } void FieldRef::clear() { _size = 0; _variable.clear(); _dotted.clear(); _replacements.clear(); } std::ostream& operator<<(std::ostream& stream, const FieldRef& field) { return stream << field.dottedField(); } } // namespace mongo