diff options
Diffstat (limited to 'src/mongo/db/query/interval.cpp')
| -rw-r--r-- | src/mongo/db/query/interval.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/src/mongo/db/query/interval.cpp b/src/mongo/db/query/interval.cpp new file mode 100644 index 00000000000..c42feefa2c2 --- /dev/null +++ b/src/mongo/db/query/interval.cpp @@ -0,0 +1,316 @@ +/** + * Copyright (C) 2013 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 <http://www.gnu.org/licenses/>. + * + * 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/query/interval.h" + +namespace mongo { + + Interval::Interval() + : _intervalData(BSONObj()), start(BSONElement()), startInclusive(false), end(BSONElement()), + endInclusive(false) { } + + Interval::Interval(BSONObj base, bool si, bool ei) { + init(base, si, ei); + } + + void Interval::init(BSONObj base, bool si, bool ei) { + verify(base.nFields() >= 2); + + _intervalData = base.getOwned(); + BSONObjIterator it(_intervalData); + start = it.next(); + end = it.next(); + startInclusive = si; + endInclusive = ei; + } + + bool Interval::isEmpty() const { + return _intervalData.nFields() == 0; + } + + bool Interval::isPoint() const { + return startInclusive && endInclusive && 0 == start.woCompare(end, false); + } + + bool Interval::isNull() const { + return (!startInclusive || !endInclusive) && 0 == start.woCompare(end, false); + } + + // + // Comparison + // + + bool Interval::equals(const Interval& other) const { + if (this->startInclusive != other.startInclusive) { + return false; + } + + if (this->endInclusive != other.endInclusive) { + return false; + } + + int res = this->start.woCompare(other.start, false); + if (res != 0) { + return false; + } + + res = this->end.woCompare(other.end, false); + if (res != 0) { + return false; + } + + return true; + } + + bool Interval::intersects(const Interval& other) const { + int res = this->start.woCompare(other.end, false); + if (res > 0) { + return false; + } + else if (res == 0 && (!this->startInclusive || !other.endInclusive)) { + return false; + } + + res = other.start.woCompare(this->end, false); + if (res > 0) { + return false; + } + else if (res == 0 && (!other.startInclusive || !this->endInclusive)) { + return false; + } + + return true; + } + + bool Interval::within(const Interval& other) const { + int res = this->start.woCompare(other.start, false); + if (res < 0) { + return false; + } + else if (res == 0 && this->startInclusive && !other.startInclusive) { + return false; + } + + res = this->end.woCompare(other.end, false); + if (res > 0) { + return false; + } + else if (res == 0 && this->endInclusive && !other.endInclusive) { + return false; + } + + return true; + } + + /** Returns true if the start of comes before the start of other */ + bool Interval::precedes(const Interval& other) const { + int res = this->start.woCompare(other.start, false); + if (res < 0) { + return true; + } + else if (res == 0 && this->startInclusive && !other.startInclusive) { + return true; + } + return false; + } + + + Interval::IntervalComparison Interval::compare(const Interval& other) const { + // + // Intersect cases + // + + if (this->intersects(other)) { + if (this->equals(other)) { + return INTERVAL_EQUALS; + } + if (this->within(other)) { + return INTERVAL_WITHIN; + } + if (other.within(*this)) { + return INTERVAL_CONTAINS; + } + if (this->precedes(other)) { + return INTERVAL_OVERLAPS_BEFORE; + } + return INTERVAL_OVERLAPS_AFTER; + } + + // + // Non-intersect cases + // + + if (this->precedes(other)) { + // It's not possible for both endInclusive and other.startInclusive to be true because + // the bounds would intersect. Refer to section on "Intersect cases" above. + if ((endInclusive || other.startInclusive) && 0 == end.woCompare(other.start, false)) { + return INTERVAL_PRECEDES_COULD_UNION; + } + return INTERVAL_PRECEDES; + } + + return INTERVAL_SUCCEEDS; + } + + // + // Mutation: Union and Intersection + // + + void Interval::intersect(const Interval& other, IntervalComparison cmp) { + if (cmp == INTERVAL_UNKNOWN) { + cmp = this->compare(other); + } + + BSONObjBuilder builder; + switch (cmp) { + + case INTERVAL_EQUALS: + case INTERVAL_WITHIN: + break; + + case INTERVAL_CONTAINS: + builder.append(other.start); + builder.append(other.end); + init(builder.obj(), other.startInclusive, other.endInclusive); + break; + + case INTERVAL_OVERLAPS_AFTER: + builder.append(start); + builder.append(other.end); + init(builder.obj(), startInclusive, other.endInclusive); + break; + + case INTERVAL_OVERLAPS_BEFORE: + builder.append(other.start); + builder.append(end); + init(builder.obj(), other.startInclusive, endInclusive); + break; + + case INTERVAL_PRECEDES: + case INTERVAL_SUCCEEDS: + *this = Interval(); + break; + + default: + verify(false); + } + } + + void Interval::combine(const Interval& other, IntervalComparison cmp) { + if (cmp == INTERVAL_UNKNOWN) { + cmp = this->compare(other); + } + + BSONObjBuilder builder; + switch (cmp) { + + case INTERVAL_EQUALS: + case INTERVAL_CONTAINS: + break; + + case INTERVAL_WITHIN: + builder.append(other.start); + builder.append(other.end); + init(builder.obj(), other.startInclusive, other.endInclusive); + break; + + case INTERVAL_OVERLAPS_AFTER: + case INTERVAL_SUCCEEDS: + builder.append(other.start); + builder.append(end); + init(builder.obj(), other.startInclusive, endInclusive); + break; + + case INTERVAL_OVERLAPS_BEFORE: + case INTERVAL_PRECEDES: + builder.append(start); + builder.append(other.end); + init(builder.obj(), startInclusive, other.endInclusive); + break; + + default: + verify(false); + } + } + + void Interval::reverse() { + std::swap(start, end); + std::swap(startInclusive, endInclusive); + } + + // + // Debug info + // + + // static + string Interval::cmpstr(IntervalComparison c) { + if (c == INTERVAL_EQUALS) { + return "INTERVAL_EQUALS"; + } + + // 'this' contains the other interval. + if (c == INTERVAL_CONTAINS) { + return "INTERVAL_CONTAINS"; + } + + // 'this' is contained by the other interval. + if (c == INTERVAL_WITHIN) { + return "INTERVAL_WITHIN"; + } + + // The two intervals intersect and 'this' is before the other interval. + if (c == INTERVAL_OVERLAPS_BEFORE) { + return "INTERVAL_OVERLAPS_BEFORE"; + } + + // The two intervals intersect and 'this is after the other interval. + if (c == INTERVAL_OVERLAPS_AFTER) { + return "INTERVAL_OVERLAPS_AFTER"; + } + + // There is no intersection. + if (c == INTERVAL_PRECEDES) { + return "INTERVAL_PRECEDES"; + } + + if (c == INTERVAL_PRECEDES_COULD_UNION) { + return "INTERVAL_PRECEDES_COULD_UNION"; + } + + if (c == INTERVAL_SUCCEEDS) { + return "INTERVAL_SUCCEEDS"; + } + + if (c == INTERVAL_UNKNOWN) { + return "INTERVAL_UNKNOWN"; + } + + return "NO IDEA DUDE"; + } + +} // namespace mongo |
