size_t UTF8SuffixStringFieldSearcher::matchTerms(const FieldRef & f, const size_t mintsz) { (void) mintsz; termcount_t words = 0; const byte * srcbuf = reinterpret_cast<const byte *> (f.data()); const byte * srcend = srcbuf + f.size(); if (f.size() >= _buf->size()) { _buf->reserve(f.size() + 1); } cmptype_t * dstbuf = &(*_buf.get())[0]; size_t tokenlen = 0; for( ; srcbuf < srcend; ) { if (*srcbuf == 0) { ++_zeroCount; ++srcbuf; } srcbuf = tokenize(srcbuf, _buf->capacity(), dstbuf, tokenlen); for (QueryTermList::iterator it = _qtl.begin(), mt = _qtl.end(); it != mt; ++it) { QueryTerm & qt = **it; const cmptype_t * term; termsize_t tsz = qt.term(term); if (matchTermSuffix(term, tsz, dstbuf, tokenlen)) { addHit(qt, words); } } words++; } return words; }
bool AllMatchExpression::matches( const BSONObj& doc, MatchDetails* details ) const { FieldRef path; path.parse(_path); bool traversedArray = false; int32_t idxPath = 0; BSONElement e = getFieldDottedOrArray( doc, path, &idxPath, &traversedArray ); string rest = pathToString( path, idxPath+1 ); if ( e.type() != Array || traversedArray || rest.size() == 0 ) { return matchesSingleElement( e ); } BSONElementSet all; BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement e = i.next(); if ( ! e.isABSONObj() ) continue; e.Obj().getFieldsDotted( rest, all ); } return _match( all ); }
size_t UTF8StrChrFieldSearcher::matchTerms(const FieldRef & f, const size_t mintsz) { (void) mintsz; termcount_t words(0); const byte * n = reinterpret_cast<const byte *> (f.data()); const byte * e = n + f.size(); if (f.size() >= _buf->size()) { _buf->reserve(f.size() + 1); } cmptype_t * fn = &(*_buf.get())[0]; size_t fl(0); for( ; n < e; ) { if (!*n) { _zeroCount++; n++; } n = tokenize(n, _buf->capacity(), fn, fl); for(QueryTermList::iterator it=_qtl.begin(), mt=_qtl.end(); it != mt; it++) { QueryTerm & qt = **it; const cmptype_t * term; termsize_t tsz = qt.term(term); if ((tsz <= fl) && (prefix() || qt.isPrefix() || (tsz == fl))) { const cmptype_t *tt=term, *et=term+tsz; for (const cmptype_t *fnt=fn; (tt < et) && (*tt == *fnt); tt++, fnt++); if (tt == et) { addHit(qt, words); } } } words++; } NEED_CHAR_STAT(addAnyUtf8Field(f.size())); return words; }
TEST(System, gemv_blocked_scaled_diagonal) { Set points; FieldRef<simit_float,2> b = points.addField<simit_float,2>("b"); FieldRef<simit_float,2> c = points.addField<simit_float,2>("c"); FieldRef<simit_float,2> a = points.addField<simit_float,2>("a"); ElementRef p0 = points.add(); ElementRef p1 = points.add(); ElementRef p2 = points.add(); a.set(p0, {1.0, 2.0}); a.set(p1, {3.0, 4.0}); a.set(p2, {5.0, 6.0}); b.set(p0, {1.0, 1.0}); b.set(p1, {1.0, 1.0}); b.set(p2, {1.0, 1.0}); Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.runSafe(); ASSERT_EQ(30.0, c.get(p0)(0)); ASSERT_EQ(60.0, c.get(p0)(1)); ASSERT_EQ(210.0, c.get(p1)(0)); ASSERT_EQ(280.0, c.get(p1)(1)); ASSERT_EQ(550.0, c.get(p2)(0)); ASSERT_EQ(660.0, c.get(p2)(1)); }
TEST(System, vector_dot_intrinsic) { Set points; FieldRef<simit_float> x = points.addField<simit_float>("x"); FieldRef<simit_float> z = points.addField<simit_float>("z"); ElementRef p0 = points.add(); ElementRef p1 = points.add(); ElementRef p2 = points.add(); ElementRef p3 = points.add(); ElementRef p4 = points.add(); ElementRef p5 = points.add(); ElementRef p6 = points.add(); ElementRef p7 = points.add(); ElementRef p8 = points.add(); ElementRef p9 = points.add(); ElementRef p10 = points.add(); x.set(p0, 1.0); x.set(p1, 2.0); x.set(p2, 3.0); x.set(p3, 4.0); x.set(p4, 5.0); x.set(p5, 6.0); x.set(p6, 7.0); x.set(p7, 8.0); x.set(p8, 9.0); x.set(p9, 10.0); x.set(p10, 11.0); Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.runSafe(); SIMIT_EXPECT_FLOAT_EQ(506.0, (int)z.get(p0)); }
Status setElementAtPath(const FieldRef& path, const BSONElement& value, mutablebson::Document* doc) { size_t deepestElemPathPart; mutablebson::Element deepestElem(doc->end()); // Get the existing parents of this path Status status = findLongestPrefix(path, doc->root(), &deepestElemPathPart, &deepestElem); // TODO: All this is pretty awkward, why not return the position immediately after the // consumed path or use a signed sentinel? Why is it a special case when we've consumed the // whole path? if (!status.isOK() && status.code() != ErrorCodes::NonExistentPath) return status; // Inc the path by one *unless* we matched nothing if (status.code() != ErrorCodes::NonExistentPath) { ++deepestElemPathPart; } else { deepestElemPathPart = 0; deepestElem = doc->root(); } if (deepestElemPathPart == path.numParts()) { // The full path exists already in the document, so just set a value return deepestElem.setValueBSONElement(value); } else { // Construct the rest of the path we need with empty documents and set the value StringData leafFieldName = path.getPart(path.numParts() - 1); mutablebson::Element leafElem = doc->makeElementWithNewFieldName(leafFieldName, value); dassert(leafElem.ok()); return createPathAt(path, deepestElemPathPart, deepestElem, leafElem); } }
void NurbsCurveEmitter::Fill(const FieldRef& field) { MFnNurbsCurve curve(mObject); // Get the range for U and V. MPlug minValuePlug = curve.findPlug("minValue"); MPlug maxValuePlug = curve.findPlug("maxValue"); const Double minValue = minValuePlug.asDouble(); const Double maxValue = maxValuePlug.asDouble(); const Double valueRange = maxValue - minValue; int i = 0; for (i = 0; i < static_cast<int>(mSample); ++ i) { double u = Stokes::Random::NextAsDouble(); double v = Stokes::Random::NextAsDouble(); double w = Stokes::Random::NextAsDouble(); double param = u * valueRange + minValue; MPoint p; curve.getPointAtParam(param, p, MSpace::kWorld); MVector t = curve.tangent(param, MSpace::kWorld); t.normalize(); MVector n = curve.normal(param, MSpace::kWorld); n.normalize(); MVector b = n ^ t; double r = sqrt(v); double phi = w * 2.0 * M_PI; double x = r * cos(phi); double y = r * sin(phi); // TODO: No radius here. MPoint newP = p + n * x + b * y; MVector radialDirection = newP - p; radialDirection.normalize(); Stokes::Vectorf noisedPoint(Random::NextAsFloat() * mScale.x - mOffset.x, Random::NextAsFloat() * mScale.y - mOffset.y, static_cast<Float>(u) * mScale.z - mOffset.z); Float displacement = Stokes::Noiser::FractalBrownianMotion(noisedPoint, mDisplacedH, mDisplacedLacunarity, mDisplacedOctave) * mDisplacedAmplitude; MPoint displacedP = newP + radialDirection * displacement; Stokes::Vectorf worldPoint(static_cast<Stokes::Float>(displacedP.x), static_cast<Stokes::Float>(displacedP.y), static_cast<Stokes::Float>(displacedP.z)); Stokes::Vectoriu index; if (field->CalculateIndexFromWorldPoint(worldPoint, index)) { Float density = Stokes::Noiser::FractalBrownianMotion(noisedPoint, mH, mLacunarity, mOctave) * mAmplitude; if (density > 0) { field->Access(index)[0] += density; } } } }
bool hasArrayFilter(const FieldRef& fieldRef) { auto size = fieldRef.numParts(); for (size_t i = 0; i < size; i++) { auto fieldPart = fieldRef.getPart(i); if (isArrayFilterIdentifier(fieldPart)) { return true; } } return false; }
void BSONElementIterator::ArrayIterationState::reset(const FieldRef& ref, int start) { restOfPath = ref.dottedField(start).toString(); hasMore = restOfPath.size() > 0; if (hasMore) { nextPieceOfPath = ref.getPart(start); nextPieceOfPathIsNumber = isAllDigits(nextPieceOfPath); } else { nextPieceOfPathIsNumber = false; } }
Status UpdateDriver::createFromQuery(const BSONObj& query, mutablebson::Document& doc) { BSONObjIteratorSorted i(query); while (i.more()) { BSONElement e = i.next(); // TODO: get this logic/exclude-list from the query system? if (e.fieldName()[0] == '$' || e.fieldNameStringData() == "_id") continue; if (e.type() == Object && e.embeddedObject().firstElementFieldName()[0] == '$') { // we have something like { x : { $gt : 5 } } // this can be a query piece // or can be a dbref or something int op = e.embeddedObject().firstElement().getGtLtOp(); if (op > 0) { // This means this is a $gt type filter, so don't make it part of the new // object. continue; } if (mongoutils::str::equals(e.embeddedObject().firstElement().fieldName(), "$not")) { // A $not filter operator is not detected in getGtLtOp() and should not // become part of the new object. continue; } } // Add to the field to doc after expanding and checking for conflicts. FieldRef elemName; const StringData& elemNameSD(e.fieldNameStringData()); elemName.parse(elemNameSD); size_t pos; mutablebson::Element* elemFound = NULL; Status status = pathsupport::findLongestPrefix(elemName, doc.root(), &pos, elemFound); // Not NonExistentPath, of OK, return if (!(status.code() == ErrorCodes::NonExistentPath || status.isOK())) return status; status = pathsupport::createPathAt(elemName, 0, doc.root(), doc.makeElementWithNewFieldName( elemName.getPart(elemName.numParts()-1), e)); if (!status.isOK()) return status; } return Status::OK(); }
TEST(System, gemv_blocked_nw) { // Points Set points; FieldRef<simit_float,2> b = points.addField<simit_float,2>("b"); FieldRef<simit_float,2> c = points.addField<simit_float,2>("c"); ElementRef p0 = points.add(); ElementRef p1 = points.add(); ElementRef p2 = points.add(); b.set(p0, {1.0, 2.0}); b.set(p1, {3.0, 4.0}); b.set(p2, {5.0, 6.0}); // Taint c c.set(p0, {42.0, 42.0}); c.set(p2, {42.0, 42.0}); // Springs Set springs(points,points); FieldRef<simit_float,2,2> a = springs.addField<simit_float,2,2>("a"); ElementRef s0 = springs.add(p0,p1); ElementRef s1 = springs.add(p1,p2); a.set(s0, {1.0, 2.0, 3.0, 4.0}); a.set(s1, {5.0, 6.0, 7.0, 8.0}); // Compile program and bind arguments Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.bind("springs", &springs); func.runSafe(); // Check that outputs are correct // TODO: add support for comparing a tensorref like so: b0 == {1.0, 2.0, 3.0} TensorRef<simit_float,2> c0 = c.get(p0); ASSERT_EQ(5.0, c0(0)); ASSERT_EQ(11.0, c0(1)); TensorRef<simit_float,2> c1 = c.get(p1); ASSERT_EQ(39.0, c1(0)); ASSERT_EQ(53.0, c1(1)); TensorRef<simit_float,2> c2 = c.get(p2); ASSERT_EQ(0.0, c2(0)); ASSERT_EQ(0.0, c2(1)); }
/** * Helper function to check if path conflicts are all prefixes. */ static Status checkPathIsPrefixOf(const FieldRef& path, const FieldRefSet& conflictPaths) { for (FieldRefSet::const_iterator it = conflictPaths.begin(); it != conflictPaths.end(); ++it) { const FieldRef* conflictingPath = *it; // Conflicts are always prefixes (or equal to) the path, or vice versa if (path.numParts() > conflictingPath->numParts()) { string errMsg = stream() << "field at '" << conflictingPath->dottedField() << "' must be exactly specified, field at sub-path '" << path.dottedField() << "'found"; return Status(ErrorCodes::NotExactValueField, errMsg); } } return Status::OK(); }
static BSONElement findEqualityElement(const EqualityMatches& equalities, const FieldRef& path) { int parentPathPart; const BSONElement& parentEl = pathsupport::findParentEqualityElement(equalities, path, &parentPathPart); if (parentPathPart == static_cast<int>(path.numParts())) return parentEl; if (parentEl.type() != Object) return BSONElement(); StringData suffixStr = path.dottedSubstring(parentPathPart, path.numParts()); BSONMatchableDocument matchable(parentEl.Obj()); return extractKeyElementFromMatchable(matchable, suffixStr); }
TEST(System, vector_add) { Set points; FieldRef<simit_float> x = points.addField<simit_float>("x"); ElementRef p0 = points.add(); x.set(p0, 42.0); Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.runSafe(); SIMIT_EXPECT_FLOAT_EQ(84.0, (int)x.get(p0)); }
Status basicIsUpdatable(const FieldRef& field) { StringData firstPart = field.getPart(0); if (firstPart.compare("_id") == 0) { return Status(ErrorCodes::BadValue, "updated cannot affect the _id"); } return Status::OK(); }
bool LeafMatchExpression::matches( const BSONObj& doc, MatchDetails* details ) const { //log() << "e doc: " << doc << " path: " << _path << std::endl; FieldRef path; path.parse(_path); bool traversedArray = false; int32_t idxPath = 0; BSONElement e = getFieldDottedOrArray( doc, path, &idxPath, &traversedArray ); string rest = pathToString( path, idxPath+1 ); if ( e.type() != Array || traversedArray ) { return matchesSingleElement( e ); } BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement x = i.next(); bool found = false; if ( rest.size() == 0 ) { found = matchesSingleElement( x ); } else if ( x.isABSONObj() ) { BSONElement y = x.Obj().getField( rest ); found = matchesSingleElement( y ); } if ( found ) { if ( !_allHaveToMatch ) { if ( details && details->needRecord() ) { // this block doesn't have to be inside the _allHaveToMatch handler // but this matches the old semantics details->setElemMatchKey( x.fieldName() ); } return true; } } else if ( _allHaveToMatch ) { return false; } } return matchesSingleElement( e ); }
bool isPositional(const FieldRef& fieldRef, size_t* pos, size_t* count) { // 'count' is optional. size_t dummy; if (count == NULL) { count = &dummy; } *count = 0; size_t size = fieldRef.numParts(); for (size_t i = 0; i < size; i++) { StringData fieldPart = fieldRef.getPart(i); if ((fieldPart.size() == 1) && (fieldPart[0] == '$')) { if (*count == 0) *pos = i; (*count)++; } } return *count > 0; }
TEST(System, vector_dot_blocked) { Set points; FieldRef<simit_float,3> x = points.addField<simit_float,3>("x"); FieldRef<simit_float> z = points.addField<simit_float>("z"); ElementRef p0 = points.add(); ElementRef p1 = points.add(); ElementRef p2 = points.add(); x.set(p0, {1.0,2.0,3.0}); x.set(p1, {4.0,5.0,6.0}); x.set(p2, {7.0,8.0,9.0}); Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.runSafe(); SIMIT_EXPECT_FLOAT_EQ(285.0, (simit_float)z.get(p0)); }
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; } }
BSONElement getFieldDottedOrArray( const BSONObj& doc, const FieldRef& path, size_t* idxPath ) { if ( path.numParts() == 0 ) return doc.getField( "" ); BSONElement res; BSONObj curr = doc; bool stop = false; size_t partNum = 0; while ( partNum < path.numParts() && !stop ) { res = curr.getField( path.getPart( partNum ) ); switch ( res.type() ) { case EOO: stop = true; break; case Object: curr = res.Obj(); ++partNum; break; case Array: stop = true; break; default: if ( partNum+1 < path.numParts() ) { res = BSONElement(); } stop = true; } } *idxPath = partNum; return res; }
TEST(System, vector_add_large_system) { Set points; FieldRef<simit_float> x = points.addField<simit_float>("x"); std::vector<ElementRef> ps; for(size_t i = 0; i < 2557; ++i) { ps.push_back(points.add()); x.set(ps.back(), (simit_float)i); } Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.runSafe(); for(size_t i = 0; i < ps.size(); ++i) { SIMIT_EXPECT_FLOAT_EQ(i*2, (size_t)x.get(ps[i])); } }
Status isUpdatable(const FieldRef& field) { const size_t numParts = field.numParts(); if (numParts == 0) { return Status(ErrorCodes::EmptyFieldName, "An empty update path is not valid."); } for (size_t i = 0; i != numParts; ++i) { const StringData part = field.getPart(i); if (part.empty()) { return Status(ErrorCodes::EmptyFieldName, mongolutils::str::stream() << "The update path '" << field.dottedField() << "' contains an empty field name, which is not allowed."); } } return Status::OK(); }
TEST(System, vector_assign_blocked) { Set points; FieldRef<simit_float,2> x = points.addField<simit_float,2>("x"); ElementRef p0 = points.add(); ElementRef p1 = points.add(); x.set(p0, {1.0, 2.0}); x.set(p1, {3.0, 4.0}); Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.runSafe(); SIMIT_EXPECT_FLOAT_EQ(2.0, x.get(p0)(0)); SIMIT_EXPECT_FLOAT_EQ(4.0, x.get(p0)(1)); SIMIT_EXPECT_FLOAT_EQ(6.0, x.get(p1)(0)); SIMIT_EXPECT_FLOAT_EQ(8.0, x.get(p1)(1)); }
bool ArrayMatchingMatchExpression::matches( const BSONObj& doc, MatchDetails* details ) const { FieldRef path; path.parse(_path); bool traversedArray = false; int32_t idxPath = 0; BSONElement e = getFieldDottedOrArray( doc, path, &idxPath, &traversedArray ); string rest = pathToString( path, idxPath+1 ); if ( rest.size() == 0 ) { if ( e.type() == Array ) return matchesArray( e.Obj(), details ); return false; } if ( e.type() != Array ) return false; BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement x = i.next(); if ( ! x.isABSONObj() ) continue; BSONElement sub = x.Obj().getFieldDotted( rest ); if ( sub.type() != Array ) continue; if ( matchesArray( sub.Obj(), NULL ) ) { if ( details && details->needRecord() ) { // trying to match crazy semantics?? details->setElemMatchKey( x.fieldName() ); } return true; } } return false; }
bool TypeMatchExpression::_matches( const StringData& path, const MatchableDocument* doc, MatchDetails* details ) const { FieldRef fieldRef; fieldRef.parse( path ); bool traversedArray = false; size_t idxPath = 0; BSONElement e = doc->getFieldDottedOrArray( fieldRef, &idxPath, &traversedArray ); string rest = fieldRef.dottedField( idxPath + 1 ); if ( e.type() != Array ) { return matchesSingleElement( e ); } BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement x = i.next(); bool found = false; if ( rest.size() == 0 ) { found = matchesSingleElement( x ); } else if ( x.isABSONObj() ) { BSONMatchableDocument doc( x.Obj() ); found = _matches( rest, &doc, details ); } if ( found ) { if ( details && details->needRecord() ) { // this block doesn't have to be inside the _allHaveToMatch handler // but this matches the old semantics details->setElemMatchKey( x.fieldName() ); } return true; } } return false; }
TEST(System, gemv_assemble_from_points) { // Points Set points; FieldRef<simit_float> a = points.addField<simit_float>("a"); FieldRef<simit_float> b = points.addField<simit_float>("b"); FieldRef<simit_float> c = points.addField<simit_float>("c"); ElementRef p0 = points.add(); ElementRef p1 = points.add(); ElementRef p2 = points.add(); b.set(p0, 1.0); b.set(p1, 2.0); b.set(p2, 3.0); a.set(p0, 1.0); a.set(p1, 2.0); a.set(p2, 3.0); c.set(p0, 42.0); // Springs Set springs(points,points); springs.add(p0,p1); springs.add(p1,p2); // Compile program and bind arguments Function func = loadFunction(TEST_FILE_NAME, "main"); if (!func.defined()) FAIL(); func.bind("points", &points); func.bind("springs", &springs); func.runSafe(); // Check that outputs are correct ASSERT_EQ(1.0, c.get(p0)); ASSERT_EQ(4.0, c.get(p1)); ASSERT_EQ(0.0, c.get(p2)); }
void FieldRefSet::getConflicts(const FieldRef* toCheck, FieldRefSet* conflicts) const { // If the set is empty, there is no work to do. if (_fieldSet.empty()) return; StringData prefixStr = safeFirstPart(toCheck); FieldRef prefixField; prefixField.parse(prefixStr); FieldSet::iterator it = _fieldSet.lower_bound(&prefixField); // Now, iterate over all the present fields in the set that have the same prefix. while (it != _fieldSet.end() && safeFirstPart(*it) == prefixStr) { size_t common = (*it)->commonPrefixSize(*toCheck); if ((*it)->numParts() == common || toCheck->numParts() == common) { conflicts->_fieldSet.insert(*it); } ++it; } }
/** * Helper function to check if the current equality match paths conflict with a new path. */ static Status checkEqualityConflicts(const EqualityMatches& equalities, const FieldRef& path) { int parentPathPart = -1; const BSONElement& parentEl = findParentEqualityElement(equalities, path, &parentPathPart); if (parentEl.eoo()) return Status::OK(); string errMsg = "cannot infer query fields to set, "; StringData pathStr = path.dottedField(); StringData prefixStr = path.dottedSubstring(0, parentPathPart); StringData suffixStr = path.dottedSubstring(parentPathPart, path.numParts()); if (suffixStr.size() != 0) errMsg += stream() << "both paths '" << pathStr << "' and '" << prefixStr << "' are matched"; else errMsg += stream() << "path '" << pathStr << "' is matched twice"; return Status(ErrorCodes::NotSingleValueField, errMsg); }
FieldRef UpdateIndexData::getCanonicalIndexField(const FieldRef& path) { if (path.numParts() <= 1) return path; // The first part of the path must always be a valid field name, since it's not possible to // store a top-level array or '$' field name in a document. FieldRef buf(path.getPart(0)); for (size_t i = 1; i < path.numParts(); ++i) { auto pathComponent = path.getPart(i); if (pathComponent == "$"_sd) { continue; } if (FieldRef::isNumericPathComponentLenient(pathComponent)) { // Peek ahead to see if the next component is also all digits. This implies that the // update is attempting to create a numeric field name which would violate the // "ambiguous field name in array" constraint for multi-key indexes. Break early in this // case and conservatively return that this path affects the prefix of the consecutive // numerical path components. For instance, an input such as 'a.0.1.b.c' would return // the canonical index path of 'a'. if ((i + 1) < path.numParts() && FieldRef::isNumericPathComponentLenient(path.getPart(i + 1))) { break; } continue; } buf.appendPart(pathComponent); } return buf; }
bool FieldRefSet::insert(const FieldRef* toInsert, const FieldRef** conflict) { // We can determine if two fields conflict by checking their common prefix. // // If each field is exactly of the size of the common prefix, this means the fields are // the same. If one of the fields is greater than the common prefix and the other // isn't, the latter is a prefix of the former. And vice-versa. // // Example: // // inserted > | a a.c // exiting v | (0) (+1) // ----------------|------------------------ // a (0) | equal prefix < // a.b (+1) | prefix ^ * // // * Disjoint sub-trees // At each insertion, we only need to bother checking the fields in the set that have // at least some common prefix with the 'toInsert' field. StringData prefixStr = safeFirstPart(toInsert); FieldRef prefixField; prefixField.parse(prefixStr); FieldSet::iterator it = _fieldSet.lower_bound(&prefixField); // Now, iterate over all the present fields in the set that have the same prefix. while (it != _fieldSet.end() && safeFirstPart(*it) == prefixStr) { size_t common = (*it)->commonPrefixSize(*toInsert); if ((*it)->numParts() == common || toInsert->numParts() == common) { *conflict = *it; return false; } ++it; } _fieldSet.insert(it, toInsert); *conflict = NULL; return true; }