diff options
Diffstat (limited to 'src/mongo/util/safe_num.cpp')
-rw-r--r-- | src/mongo/util/safe_num.cpp | 482 |
1 files changed, 237 insertions, 245 deletions
diff --git a/src/mongo/util/safe_num.cpp b/src/mongo/util/safe_num.cpp index cb5d8500961..1350fca1c78 100644 --- a/src/mongo/util/safe_num.cpp +++ b/src/mongo/util/safe_num.cpp @@ -29,17 +29,17 @@ #include <boost/static_assert.hpp> #include "mongo/platform/basic.h" -#undef MONGO_PCH_WHITELISTED // for malloc/realloc/INFINITY pulled from bson +#undef MONGO_PCH_WHITELISTED // for malloc/realloc/INFINITY pulled from bson #include "mongo/bson/bsontypes.h" #include "mongo/util/safe_num.h" namespace mongo { - using std::ostringstream; +using std::ostringstream; - SafeNum::SafeNum(const BSONElement& element) { - switch (element.type()) { +SafeNum::SafeNum(const BSONElement& element) { + switch (element.type()) { case NumberInt: _type = NumberInt; _value.int32Val = element.Int(); @@ -54,12 +54,12 @@ namespace mongo { break; default: _type = EOO; - } } +} - std::string SafeNum::debugString() const { - ostringstream os; - switch (_type) { +std::string SafeNum::debugString() const { + ostringstream os; + switch (_type) { case NumberInt: os << "(NumberInt)" << _value.int32Val; break; @@ -74,61 +74,61 @@ namespace mongo { break; default: os << "(unknown type)"; - } - - return os.str(); } - std::ostream& operator<<(std::ostream& os, const SafeNum& snum) { - return os << snum.debugString(); - } + return os.str(); +} - // - // comparison support - // +std::ostream& operator<<(std::ostream& os, const SafeNum& snum) { + return os << snum.debugString(); +} - bool SafeNum::isEquivalent(const SafeNum& rhs) const { - if (!isValid() && !rhs.isValid()) { - return true; - } +// +// comparison support +// - // EOO is not equivalent to anything else. - if (!isValid() || !rhs.isValid()) { - return false; - } +bool SafeNum::isEquivalent(const SafeNum& rhs) const { + if (!isValid() && !rhs.isValid()) { + return true; + } - // If the types of either side are mixed, we'll try to find the shortest type we - // can upconvert to that would not sacrifice the accuracy in the process. + // EOO is not equivalent to anything else. + if (!isValid() || !rhs.isValid()) { + return false; + } - // If none of the sides is a double, compare them as long's. - if (_type != NumberDouble && rhs._type != NumberDouble) { - return getLongLong(*this) == getLongLong(rhs); - } + // If the types of either side are mixed, we'll try to find the shortest type we + // can upconvert to that would not sacrifice the accuracy in the process. - // If both sides are doubles, compare them as so. - if (_type == NumberDouble && rhs._type == NumberDouble) { - return _value.doubleVal == rhs._value.doubleVal; - } + // If none of the sides is a double, compare them as long's. + if (_type != NumberDouble && rhs._type != NumberDouble) { + return getLongLong(*this) == getLongLong(rhs); + } - // If we're mixing integers and doubles, we should be careful. Some integers are - // too big to be accuratelly represented in a double. If we're within a safe range - // we compare both sides as doubles. - const double lhsDouble = getDouble(*this); - const double rhsDouble = getDouble(rhs); - if (lhsDouble > -maxIntInDouble && lhsDouble < maxIntInDouble && - rhsDouble > -maxIntInDouble && rhsDouble < maxIntInDouble) { - return lhsDouble == rhsDouble; - } + // If both sides are doubles, compare them as so. + if (_type == NumberDouble && rhs._type == NumberDouble) { + return _value.doubleVal == rhs._value.doubleVal; + } - return false; + // If we're mixing integers and doubles, we should be careful. Some integers are + // too big to be accuratelly represented in a double. If we're within a safe range + // we compare both sides as doubles. + const double lhsDouble = getDouble(*this); + const double rhsDouble = getDouble(rhs); + if (lhsDouble > -maxIntInDouble && lhsDouble < maxIntInDouble && rhsDouble > -maxIntInDouble && + rhsDouble < maxIntInDouble) { + return lhsDouble == rhsDouble; } - bool SafeNum::isIdentical(const SafeNum& rhs) const { - if (_type != rhs._type) { - return false; - } + return false; +} + +bool SafeNum::isIdentical(const SafeNum& rhs) const { + if (_type != rhs._type) { + return false; + } - switch (_type) { + switch (_type) { case NumberInt: return _value.int32Val == rhs._value.int32Val; case NumberLong: @@ -136,25 +136,25 @@ namespace mongo { case NumberDouble: return _value.doubleVal == rhs._value.doubleVal; case EOO: - // EOO doesn't match anything, including itself. + // EOO doesn't match anything, including itself. default: return false; - } } +} - long long SafeNum::getLongLong(const SafeNum& snum) { - switch (snum._type) { +long long SafeNum::getLongLong(const SafeNum& snum) { + switch (snum._type) { case NumberInt: return snum._value.int32Val; case NumberLong: return snum._value.int64Val; default: return 0; - } } +} - double SafeNum::getDouble(const SafeNum& snum) { - switch (snum._type) { +double SafeNum::getDouble(const SafeNum& snum) { + switch (snum._type) { case NumberInt: return snum._value.int32Val; case NumberLong: @@ -163,247 +163,239 @@ namespace mongo { return snum._value.doubleVal; default: return 0.0; - } } +} - namespace { +namespace { - SafeNum addInt32Int32(int lInt32, int rInt32) { - // NOTE: Please see "Secure Coding in C and C++", Second Edition, page 264-265 for - // details on this algorithm (for an alternative resources, see - // - // https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false). - // - // We are using the "Downcast from a larger type" algorithm here. We always perform - // the arithmetic in 64-bit mode, which can never overflow for 32-bit - // integers. Then, if we fall within the allowable range of int, we downcast, - // otherwise, we retain the 64-bit result. +SafeNum addInt32Int32(int lInt32, int rInt32) { + // NOTE: Please see "Secure Coding in C and C++", Second Edition, page 264-265 for + // details on this algorithm (for an alternative resources, see + // + // https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false). + // + // We are using the "Downcast from a larger type" algorithm here. We always perform + // the arithmetic in 64-bit mode, which can never overflow for 32-bit + // integers. Then, if we fall within the allowable range of int, we downcast, + // otherwise, we retain the 64-bit result. - // This algorithm is only correct if sizeof(long long) > sizeof(int) - BOOST_STATIC_ASSERT(sizeof(long long) > sizeof(int)); + // This algorithm is only correct if sizeof(long long) > sizeof(int) + BOOST_STATIC_ASSERT(sizeof(long long) > sizeof(int)); - const long long int result = - static_cast<long long int>(lInt32) + - static_cast<long long int>(rInt32); + const long long int result = + static_cast<long long int>(lInt32) + static_cast<long long int>(rInt32); - if (result <= std::numeric_limits<int>::max() && - result >= std::numeric_limits<int>::min()) { - return SafeNum(static_cast<int>(result)); - } + if (result <= std::numeric_limits<int>::max() && result >= std::numeric_limits<int>::min()) { + return SafeNum(static_cast<int>(result)); + } - return SafeNum(result); - } + return SafeNum(result); +} - SafeNum addInt64Int64(long long lInt64, long long rInt64) { - // NOTE: Please see notes in addInt32Int32 above for references. In this case, since we - // have no larger integer size, if our precondition test detects overflow we must - // return an invalid SafeNum. Otherwise, the operation is safely performed by standard - // arithmetic. - if (((rInt64 > 0) && (lInt64 > (std::numeric_limits<long long>::max() - rInt64))) || - ((rInt64 < 0) && (lInt64 < (std::numeric_limits<long long>::min() - rInt64)))) { - return SafeNum(); - } +SafeNum addInt64Int64(long long lInt64, long long rInt64) { + // NOTE: Please see notes in addInt32Int32 above for references. In this case, since we + // have no larger integer size, if our precondition test detects overflow we must + // return an invalid SafeNum. Otherwise, the operation is safely performed by standard + // arithmetic. + if (((rInt64 > 0) && (lInt64 > (std::numeric_limits<long long>::max() - rInt64))) || + ((rInt64 < 0) && (lInt64 < (std::numeric_limits<long long>::min() - rInt64)))) { + return SafeNum(); + } - return SafeNum(lInt64 + rInt64); - } + return SafeNum(lInt64 + rInt64); +} - SafeNum addFloats(double lDouble, double rDouble) { - double sum = lDouble + rDouble; - return SafeNum(sum); - } +SafeNum addFloats(double lDouble, double rDouble) { + double sum = lDouble + rDouble; + return SafeNum(sum); +} - SafeNum mulInt32Int32(int lInt32, int rInt32) { - // NOTE: Please see "Secure Coding in C and C++", Second Edition, page 264-265 for - // details on this algorithm (for an alternative resources, see - // - // https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false). - // - // We are using the "Downcast from a larger type" algorithm here. We always perform - // the arithmetic in 64-bit mode, which can never overflow for 32-bit - // integers. Then, if we fall within the allowable range of int, we downcast, - // otherwise, we retain the 64-bit result. - - // This algorithm is only correct if sizeof(long long) >= (2 * sizeof(int)) - BOOST_STATIC_ASSERT(sizeof(long long) >= (2 * sizeof(int))); - - const long long int result = - static_cast<long long int>(lInt32) * - static_cast<long long int>(rInt32); - - if (result <= std::numeric_limits<int>::max() && - result >= std::numeric_limits<int>::min()) { - return SafeNum(static_cast<int>(result)); - } +SafeNum mulInt32Int32(int lInt32, int rInt32) { + // NOTE: Please see "Secure Coding in C and C++", Second Edition, page 264-265 for + // details on this algorithm (for an alternative resources, see + // + // https://www.securecoding.cert.org/confluence/display/seccode/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow?showComments=false). + // + // We are using the "Downcast from a larger type" algorithm here. We always perform + // the arithmetic in 64-bit mode, which can never overflow for 32-bit + // integers. Then, if we fall within the allowable range of int, we downcast, + // otherwise, we retain the 64-bit result. - return SafeNum(result); - } + // This algorithm is only correct if sizeof(long long) >= (2 * sizeof(int)) + BOOST_STATIC_ASSERT(sizeof(long long) >= (2 * sizeof(int))); + + const long long int result = + static_cast<long long int>(lInt32) * static_cast<long long int>(rInt32); + + if (result <= std::numeric_limits<int>::max() && result >= std::numeric_limits<int>::min()) { + return SafeNum(static_cast<int>(result)); + } - SafeNum mulInt64Int64(long long lInt64, long long rInt64) { - // NOTE: Please see notes in mulInt32Int32 above for references. In this case, - // since we have no larger integer size, if our precondition test detects overflow - // we must return an invalid SafeNum. Otherwise, the operation is safely performed - // by standard arithmetic. - - if (lInt64 > 0) { - if (rInt64 > 0) { - if (lInt64 > (std::numeric_limits<long long>::max() / rInt64)) { - return SafeNum(); - } - } - else { - if (rInt64 < (std::numeric_limits<long long>::min() / lInt64)) { - return SafeNum(); - } - } + return SafeNum(result); +} + +SafeNum mulInt64Int64(long long lInt64, long long rInt64) { + // NOTE: Please see notes in mulInt32Int32 above for references. In this case, + // since we have no larger integer size, if our precondition test detects overflow + // we must return an invalid SafeNum. Otherwise, the operation is safely performed + // by standard arithmetic. + + if (lInt64 > 0) { + if (rInt64 > 0) { + if (lInt64 > (std::numeric_limits<long long>::max() / rInt64)) { + return SafeNum(); } - else { - if (rInt64 > 0) { - if (lInt64 < (std::numeric_limits<long long>::min() / rInt64)) { - return SafeNum(); - } - } - else { - if ( (lInt64 != 0) && - (rInt64 < (std::numeric_limits<long long>::max() / lInt64))) { - return SafeNum(); - } - } + } else { + if (rInt64 < (std::numeric_limits<long long>::min() / lInt64)) { + return SafeNum(); } - - const long long result = lInt64 * rInt64; - return SafeNum(result); } - - SafeNum mulFloats(double lDouble, double rDouble) { - const double product = lDouble * rDouble; - return SafeNum(product); + } else { + if (rInt64 > 0) { + if (lInt64 < (std::numeric_limits<long long>::min() / rInt64)) { + return SafeNum(); + } + } else { + if ((lInt64 != 0) && (rInt64 < (std::numeric_limits<long long>::max() / lInt64))) { + return SafeNum(); + } } + } - } // namespace + const long long result = lInt64 * rInt64; + return SafeNum(result); +} - SafeNum SafeNum::addInternal(const SafeNum& lhs, const SafeNum& rhs) { - BSONType lType = lhs._type; - BSONType rType = rhs._type; +SafeNum mulFloats(double lDouble, double rDouble) { + const double product = lDouble * rDouble; + return SafeNum(product); +} - if (lType == NumberInt && rType == NumberInt) { - return addInt32Int32(lhs._value.int32Val, rhs._value.int32Val); - } +} // namespace - if (lType == NumberInt && rType == NumberLong) { - return addInt64Int64(lhs._value.int32Val, rhs._value.int64Val); - } +SafeNum SafeNum::addInternal(const SafeNum& lhs, const SafeNum& rhs) { + BSONType lType = lhs._type; + BSONType rType = rhs._type; - if (lType == NumberLong && rType == NumberInt) { - return addInt64Int64(lhs._value.int64Val, rhs._value.int32Val); - } + if (lType == NumberInt && rType == NumberInt) { + return addInt32Int32(lhs._value.int32Val, rhs._value.int32Val); + } - if (lType == NumberLong && rType == NumberLong) { - return addInt64Int64(lhs._value.int64Val, rhs._value.int64Val); - } + if (lType == NumberInt && rType == NumberLong) { + return addInt64Int64(lhs._value.int32Val, rhs._value.int64Val); + } - if ((lType == NumberInt || lType == NumberLong || lType == NumberDouble) && - (rType == NumberInt || rType == NumberLong || rType == NumberDouble)) { - return addFloats(getDouble(lhs), getDouble(rhs)); - } + if (lType == NumberLong && rType == NumberInt) { + return addInt64Int64(lhs._value.int64Val, rhs._value.int32Val); + } - return SafeNum(); + if (lType == NumberLong && rType == NumberLong) { + return addInt64Int64(lhs._value.int64Val, rhs._value.int64Val); } - SafeNum SafeNum::mulInternal(const SafeNum& lhs, const SafeNum& rhs) { - BSONType lType = lhs._type; - BSONType rType = rhs._type; + if ((lType == NumberInt || lType == NumberLong || lType == NumberDouble) && + (rType == NumberInt || rType == NumberLong || rType == NumberDouble)) { + return addFloats(getDouble(lhs), getDouble(rhs)); + } - if (lType == NumberInt && rType == NumberInt) { - return mulInt32Int32(lhs._value.int32Val, rhs._value.int32Val); - } + return SafeNum(); +} - if (lType == NumberInt && rType == NumberLong) { - return mulInt64Int64(lhs._value.int32Val, rhs._value.int64Val); - } +SafeNum SafeNum::mulInternal(const SafeNum& lhs, const SafeNum& rhs) { + BSONType lType = lhs._type; + BSONType rType = rhs._type; - if (lType == NumberLong && rType == NumberInt) { - return mulInt64Int64(lhs._value.int64Val, rhs._value.int32Val); - } + if (lType == NumberInt && rType == NumberInt) { + return mulInt32Int32(lhs._value.int32Val, rhs._value.int32Val); + } - if (lType == NumberLong && rType == NumberLong) { - return mulInt64Int64(lhs._value.int64Val, rhs._value.int64Val); - } + if (lType == NumberInt && rType == NumberLong) { + return mulInt64Int64(lhs._value.int32Val, rhs._value.int64Val); + } - if ((lType == NumberInt || lType == NumberLong || lType == NumberDouble) && - (rType == NumberInt || rType == NumberLong || rType == NumberDouble)) { - return mulFloats(getDouble(lhs), getDouble(rhs)); - } + if (lType == NumberLong && rType == NumberInt) { + return mulInt64Int64(lhs._value.int64Val, rhs._value.int32Val); + } - return SafeNum(); + if (lType == NumberLong && rType == NumberLong) { + return mulInt64Int64(lhs._value.int64Val, rhs._value.int64Val); } - SafeNum SafeNum::andInternal(const SafeNum& lhs, const SafeNum& rhs) { - const BSONType lType = lhs._type; - const BSONType rType = rhs._type; + if ((lType == NumberInt || lType == NumberLong || lType == NumberDouble) && + (rType == NumberInt || rType == NumberLong || rType == NumberDouble)) { + return mulFloats(getDouble(lhs), getDouble(rhs)); + } - if (lType == NumberInt && rType == NumberInt) { - return (lhs._value.int32Val & rhs._value.int32Val); - } + return SafeNum(); +} - if (lType == NumberInt && rType == NumberLong) { - return (static_cast<long long int>(lhs._value.int32Val) & rhs._value.int64Val); - } +SafeNum SafeNum::andInternal(const SafeNum& lhs, const SafeNum& rhs) { + const BSONType lType = lhs._type; + const BSONType rType = rhs._type; - if (lType == NumberLong && rType == NumberInt) { - return (lhs._value.int64Val & static_cast<long long int>(rhs._value.int32Val)); - } + if (lType == NumberInt && rType == NumberInt) { + return (lhs._value.int32Val & rhs._value.int32Val); + } - if (lType == NumberLong && rType == NumberLong) { - return (lhs._value.int64Val & rhs._value.int64Val); - } + if (lType == NumberInt && rType == NumberLong) { + return (static_cast<long long int>(lhs._value.int32Val) & rhs._value.int64Val); + } - return SafeNum(); + if (lType == NumberLong && rType == NumberInt) { + return (lhs._value.int64Val & static_cast<long long int>(rhs._value.int32Val)); } - SafeNum SafeNum::orInternal(const SafeNum& lhs, const SafeNum& rhs) { - const BSONType lType = lhs._type; - const BSONType rType = rhs._type; + if (lType == NumberLong && rType == NumberLong) { + return (lhs._value.int64Val & rhs._value.int64Val); + } - if (lType == NumberInt && rType == NumberInt) { - return (lhs._value.int32Val | rhs._value.int32Val); - } + return SafeNum(); +} - if (lType == NumberInt && rType == NumberLong) { - return (static_cast<long long int>(lhs._value.int32Val) | rhs._value.int64Val); - } +SafeNum SafeNum::orInternal(const SafeNum& lhs, const SafeNum& rhs) { + const BSONType lType = lhs._type; + const BSONType rType = rhs._type; - if (lType == NumberLong && rType == NumberInt) { - return (lhs._value.int64Val | static_cast<long long int>(rhs._value.int32Val)); - } + if (lType == NumberInt && rType == NumberInt) { + return (lhs._value.int32Val | rhs._value.int32Val); + } - if (lType == NumberLong && rType == NumberLong) { - return (lhs._value.int64Val | rhs._value.int64Val); - } + if (lType == NumberInt && rType == NumberLong) { + return (static_cast<long long int>(lhs._value.int32Val) | rhs._value.int64Val); + } - return SafeNum(); + if (lType == NumberLong && rType == NumberInt) { + return (lhs._value.int64Val | static_cast<long long int>(rhs._value.int32Val)); } - SafeNum SafeNum::xorInternal(const SafeNum& lhs, const SafeNum& rhs) { - const BSONType lType = lhs._type; - const BSONType rType = rhs._type; + if (lType == NumberLong && rType == NumberLong) { + return (lhs._value.int64Val | rhs._value.int64Val); + } - if (lType == NumberInt && rType == NumberInt) { - return (lhs._value.int32Val ^ rhs._value.int32Val); - } + return SafeNum(); +} - if (lType == NumberInt && rType == NumberLong) { - return (static_cast<long long int>(lhs._value.int32Val) ^ rhs._value.int64Val); - } +SafeNum SafeNum::xorInternal(const SafeNum& lhs, const SafeNum& rhs) { + const BSONType lType = lhs._type; + const BSONType rType = rhs._type; - if (lType == NumberLong && rType == NumberInt) { - return (lhs._value.int64Val ^ static_cast<long long int>(rhs._value.int32Val)); - } + if (lType == NumberInt && rType == NumberInt) { + return (lhs._value.int32Val ^ rhs._value.int32Val); + } - if (lType == NumberLong && rType == NumberLong) { - return (lhs._value.int64Val ^ rhs._value.int64Val); - } + if (lType == NumberInt && rType == NumberLong) { + return (static_cast<long long int>(lhs._value.int32Val) ^ rhs._value.int64Val); + } - return SafeNum(); + if (lType == NumberLong && rType == NumberInt) { + return (lhs._value.int64Val ^ static_cast<long long int>(rhs._value.int32Val)); } -} // namespace mongo + if (lType == NumberLong && rType == NumberLong) { + return (lhs._value.int64Val ^ rhs._value.int64Val); + } + + return SafeNum(); +} + +} // namespace mongo |