void ArithmeticNode::updateExistingElement(mutablebson::Element* element, bool* noop) const { if (!element->isNumeric()) { mutablebson::Element idElem = mutablebson::findFirstChildNamed(element->getDocument().root(), "_id"); uasserted(ErrorCodes::TypeMismatch, str::stream() << "Cannot apply " << getModifierNameForOp(_op) << " to a value of non-numeric type. {" << idElem.toString() << "} has the field '" << element->getFieldName() << "' of non-numeric type " << typeName(element->getType())); } SafeNum originalValue = element->getValueSafeNum(); SafeNum valueToSet = _val; switch (_op) { case ArithmeticOp::kAdd: valueToSet += originalValue; break; case ArithmeticOp::kMultiply: valueToSet *= originalValue; break; } // If the updated value is identical to the original value, treat this as a no-op. Caveat: // if the found element is in a deserialized state, we can't do that. if (element->getValue().ok() && valueToSet.isIdentical(originalValue)) { *noop = true; } else { // This can fail if 'valueToSet' is not representable as a 64-bit integer. uassertStatusOK(element->setValueSafeNum(valueToSet)); } }
ModifierNode::ModifyResult BitNode::updateExistingElement( mutablebson::Element* element, std::shared_ptr<FieldRef> elementPath) const { if (!element->isIntegral()) { mutablebson::Element idElem = mutablebson::findFirstChildNamed(element->getDocument().root(), "_id"); uasserted(ErrorCodes::BadValue, str::stream() << "Cannot apply $bit to a value of non-integral type." << idElem.toString() << " has the field " << element->getFieldName() << " of non-integer type " << typeName(element->getType())); } SafeNum value = applyOpList(element->getValueSafeNum()); if (!value.isIdentical(element->getValueSafeNum())) { invariantOK(element->setValueSafeNum(value)); return ModifyResult::kNormalUpdate; } else { return ModifyResult::kNoOp; } }
const SafeNum val1(static_cast<int>(0xE0F1U)); const SafeNum val2(static_cast<int>(0xDF01U)); const SafeNum expected(static_cast<int>(0xC001U)); const SafeNum result = val1 & val2; ASSERT_EQUALS(mongo::NumberInt, result.type()); ASSERT_TRUE(expected.isIdentical(result)); } TEST(BitAnd, 64and64) { const SafeNum val1(static_cast<long long>(0xE0F1E0F1E0F1ULL)); const SafeNum val2(static_cast<long long>(0xDF01DF01DF01ULL)); const SafeNum expected(static_cast<long long>(0xC001C001C001ULL)); const SafeNum result = val1 & val2; ASSERT_EQUALS(mongo::NumberLong, result.type()); ASSERT_TRUE(expected.isIdentical(result)); } TEST(BitAnd, MixedSize) { const SafeNum val_small(static_cast<int>(0xE0F1U)); const SafeNum val_big(static_cast<long long>(0xDF01U)); const SafeNum expected(static_cast<long long>(0xC001U)); const SafeNum result_s_b = val_small & val_big; const SafeNum result_b_s = val_big & val_small; ASSERT_EQUALS(mongo::NumberLong, result_s_b.type()); ASSERT_TRUE(expected.isIdentical(result_s_b)); ASSERT_EQUALS(mongo::NumberLong, result_b_s.type()); ASSERT_TRUE(expected.isIdentical(result_b_s)); }