summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Morrow <acm@10gen.com>2013-08-19 10:23:30 -0400
committerAndrew Morrow <acm@10gen.com>2013-08-20 11:48:46 -0400
commitfce3b4ce8fe848c332657f3accf596e6b087bce9 (patch)
tree8d68d5d120efde72b66984687306e1aba5fffb3f /src
parenta8eab606724de2d23a6cafd069f4001a031ed909 (diff)
downloadmongo-fce3b4ce8fe848c332657f3accf596e6b087bce9.tar.gz
SERVER-4362 Implement xor support for $bit
Diffstat (limited to 'src')
-rw-r--r--src/mongo/db/ops/modifier_bit.cpp29
-rw-r--r--src/mongo/db/ops/modifier_bit_test.cpp136
-rw-r--r--src/mongo/util/safe_num-inl.h12
-rw-r--r--src/mongo/util/safe_num.cpp23
-rw-r--r--src/mongo/util/safe_num.h11
-rw-r--r--src/mongo/util/safe_num_test.cpp43
6 files changed, 242 insertions, 12 deletions
diff --git a/src/mongo/db/ops/modifier_bit.cpp b/src/mongo/db/ops/modifier_bit.cpp
index 30bbc22befa..bfcc5e23ceb 100644
--- a/src/mongo/db/ops/modifier_bit.cpp
+++ b/src/mongo/db/ops/modifier_bit.cpp
@@ -94,25 +94,30 @@ namespace mongo {
const StringData payloadFieldName = curOp.fieldName();
- const bool isAnd = (payloadFieldName == "and");
- const bool isOr = (payloadFieldName == "or");
-
- if (!(isAnd || isOr))
- return Status(
- ErrorCodes::BadValue,
- "Only 'and' and 'or' are supported $bit sub-operators");
-
if ((curOp.type() != mongo::NumberInt) &&
(curOp.type() != mongo::NumberLong))
return Status(
ErrorCodes::BadValue,
"Argument to $bit operation must be a NumberInt or NumberLong");
- const OpEntry entry = {
- SafeNum(curOp),
- isAnd ? &SafeNum::bitAnd : &SafeNum::bitOr
- };
+ SafeNumOp op = NULL;
+
+ if (payloadFieldName == "and") {
+ op = &SafeNum::bitAnd;
+ }
+ else if (payloadFieldName == "or") {
+ op = &SafeNum::bitOr;
+ }
+ else if (payloadFieldName == "xor") {
+ op = &SafeNum::bitXor;
+ }
+ else {
+ return Status(
+ ErrorCodes::BadValue,
+ "Only 'and', 'or', and 'xor' are supported $bit sub-operators");
+ }
+ const OpEntry entry = {SafeNum(curOp), op};
_ops.push_back(entry);
}
diff --git a/src/mongo/db/ops/modifier_bit_test.cpp b/src/mongo/db/ops/modifier_bit_test.cpp
index e3f9374563f..a29c8a152c9 100644
--- a/src/mongo/db/ops/modifier_bit_test.cpp
+++ b/src/mongo/db/ops/modifier_bit_test.cpp
@@ -116,6 +116,10 @@ namespace {
Mod mod(BSON("$bit" << BSON("a" << BSON("or" << static_cast<int>(1)))));
}
+ TEST(Init, ParsesXorInt) {
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<int>(1)))));
+ }
+
TEST(Init, ParsesAndLong) {
Mod mod(BSON("$bit" << BSON("a" << BSON("and" << static_cast<long long>(1)))));
}
@@ -124,6 +128,10 @@ namespace {
Mod mod(BSON("$bit" << BSON("a" << BSON("or" << static_cast<long long>(1)))));
}
+ TEST(Init, ParsesXorLong) {
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<long long>(1)))));
+ }
+
TEST(SimpleMod, PrepareOKTargetNotFound) {
Document doc(fromjson("{}"));
Mod mod(fromjson("{ $bit : { a : { and : 1 } } }"));
@@ -210,6 +218,24 @@ namespace {
ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc);
}
+ TEST(SimpleMod, ApplyAndLogEmptyDocumentXor) {
+ Document doc(fromjson("{}"));
+ Mod mod(fromjson("{ $bit : { a : { xor : 1 } } }"));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(mod.apply());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : 1 }"), doc);
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(mod.log(&logBuilder));
+ ASSERT_EQUALS(fromjson("{ $set : { a : 1 } }"), logDoc);
+ }
+
TEST(SimpleMod, ApplyAndLogSimpleDocumentAnd) {
Document doc(fromjson("{ a : 5 }"));
Mod mod(fromjson("{ $bit : { a : { and : 6 } } }"));
@@ -246,6 +272,24 @@ namespace {
ASSERT_EQUALS(fromjson("{ $set : { a : 7 } }"), logDoc);
}
+ TEST(SimpleMod, ApplyAndLogSimpleDocumentXor) {
+ Document doc(fromjson("{ a : 5 }"));
+ Mod mod(fromjson("{ $bit : { a : { xor : 6 } } }"));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(mod.apply());
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : 3 }"), doc);
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(mod.log(&logBuilder));
+ ASSERT_EQUALS(fromjson("{ $set : { a : 3 } }"), logDoc);
+ }
+
TEST(InPlace, IntToIntAndIsInPlace) {
Document doc(BSON("a" << static_cast<int>(1)));
Mod mod(BSON("$bit" << BSON("a" << BSON("and" << static_cast<int>(1)))));
@@ -274,6 +318,19 @@ namespace {
ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(1))), logDoc);
}
+ TEST(InPlace, IntToIntXorIsInPlace) {
+ Document doc(BSON("a" << static_cast<int>(1)));
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<int>(1)))));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(mod.log(&logBuilder));
+ ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(0))), logDoc);
+ }
+
TEST(InPlace, LongToLongAndIsInPlace) {
Document doc(BSON("a" << static_cast<long long>(1)));
Mod mod(BSON("$bit" << BSON("a" << BSON("and" << static_cast<long long>(1)))));
@@ -302,6 +359,19 @@ namespace {
ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<long long>(1))), logDoc);
}
+ TEST(InPlace, LongToLongXorIsInPlace) {
+ Document doc(BSON("a" << static_cast<long long>(1)));
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<long long>(1)))));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(mod.log(&logBuilder));
+ ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<long long>(0))), logDoc);
+ }
+
TEST(InPlace, IntToLongAndIsNotInPlace) {
Document doc(BSON("a" << static_cast<int>(1)));
Mod mod(BSON("$bit" << BSON("a" << BSON("and" << static_cast<long long>(1)))));
@@ -320,6 +390,15 @@ namespace {
ASSERT_FALSE(execInfo.noOp);
}
+ TEST(InPlace, IntToLongXorIsNotInPlace) {
+ Document doc(BSON("a" << static_cast<int>(1)));
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<long long>(1)))));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+ }
+
TEST(NoOp, IntAnd) {
Document doc(BSON("a" << static_cast<int>(0xABCD1234U)));
Mod mod(BSON("$bit" << BSON("a" << BSON("and" << static_cast<int>(0xFFFFFFFFU)))));
@@ -348,6 +427,20 @@ namespace {
ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(0xABCD1234U))), logDoc);
}
+ TEST(NoOp, IntXor) {
+ Document doc(BSON("a" << static_cast<int>(0xABCD1234U)));
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<int>(0x0U)))));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+ ASSERT_TRUE(execInfo.noOp);
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(mod.log(&logBuilder));
+ ASSERT_EQUALS(BSON("$set" << BSON("a" << static_cast<int>(0xABCD1234U))), logDoc);
+ }
+
TEST(NoOp, LongAnd) {
Document doc(BSON("a" << static_cast<long long>(0xABCD1234EF981234ULL)));
Mod mod(BSON("$bit" << BSON("a" << BSON("and" <<
@@ -378,6 +471,21 @@ namespace {
static_cast<long long>(0xABCD1234EF981234ULL))), logDoc);
}
+ TEST(NoOp, LongXor) {
+ Document doc(BSON("a" << static_cast<long long>(0xABCD1234EF981234ULL)));
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<long long>(0x0ULL)))));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+ ASSERT_TRUE(execInfo.noOp);
+
+ Document logDoc;
+ LogBuilder logBuilder(logDoc.root());
+ ASSERT_OK(mod.log(&logBuilder));
+ ASSERT_EQUALS(BSON("$set" << BSON("a" <<
+ static_cast<long long>(0xABCD1234EF981234ULL))), logDoc);
+ }
+
TEST(Upcasting, UpcastIntToLongAnd) {
Document doc(BSON("a" << static_cast<int>(1)));
Mod mod(BSON("$bit" << BSON("a" << BSON("and" << static_cast<long long>(1)))));
@@ -406,6 +514,20 @@ namespace {
ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType());
}
+ TEST(Upcasting, UpcastIntToLongXor) {
+ Document doc(BSON("a" << static_cast<int>(1)));
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<long long>(0)))));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(mod.apply());
+ ASSERT_FALSE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : 1 }"), doc);
+ ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType());
+ }
+
TEST(Upcasting, LongsStayLongsAnd) {
Document doc(BSON("a" << static_cast<long long>(1)));
Mod mod(BSON("$bit" << BSON("a" << BSON("and" << static_cast<int>(2)))));
@@ -434,6 +556,20 @@ namespace {
ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType());
}
+ TEST(Upcasting, LongsStayLongsXor) {
+ Document doc(BSON("a" << static_cast<long long>(1)));
+ Mod mod(BSON("$bit" << BSON("a" << BSON("xor" << static_cast<int>(1)))));
+
+ ModifierInterface::ExecInfo execInfo;
+ ASSERT_OK(mod.prepare(doc.root(), "", &execInfo));
+ ASSERT_FALSE(execInfo.noOp);
+
+ ASSERT_OK(mod.apply());
+ ASSERT_TRUE(doc.isInPlaceModeEnabled());
+ ASSERT_EQUALS(fromjson("{ a : 0 }"), doc);
+ ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType());
+ }
+
// The following tests are re-created from the previous $bit tests in updatetests.cpp. They
// are probably redundant with the tests above in various ways.
diff --git a/src/mongo/util/safe_num-inl.h b/src/mongo/util/safe_num-inl.h
index 1dc1dcbe785..18d6a1f641e 100644
--- a/src/mongo/util/safe_num-inl.h
+++ b/src/mongo/util/safe_num-inl.h
@@ -84,6 +84,18 @@ namespace mongo {
return *this = bitOr(rhs);
}
+ inline SafeNum SafeNum::bitXor(const SafeNum& rhs) const {
+ return xorInternal(*this, rhs);
+ }
+
+ inline SafeNum SafeNum::operator^(const SafeNum& rhs) const {
+ return bitXor(rhs);
+ }
+
+ inline SafeNum& SafeNum::operator^=(const SafeNum& rhs) {
+ return *this = bitXor(rhs);
+ }
+
inline bool SafeNum::isValid() const {
return _type != EOO;
}
diff --git a/src/mongo/util/safe_num.cpp b/src/mongo/util/safe_num.cpp
index 46b583bc5a5..98864cdb550 100644
--- a/src/mongo/util/safe_num.cpp
+++ b/src/mongo/util/safe_num.cpp
@@ -269,4 +269,27 @@ namespace mongo {
return SafeNum();
}
+ SafeNum SafeNum::xorInternal(const SafeNum& lhs, const SafeNum& rhs) {
+ const BSONType lType = lhs._type;
+ const BSONType rType = rhs._type;
+
+ if (lType == NumberInt && rType == NumberInt) {
+ return (lhs._value.int32Val ^ rhs._value.int32Val);
+ }
+
+ if (lType == NumberInt && rType == NumberLong) {
+ return (static_cast<long long int>(lhs._value.int32Val) ^ rhs._value.int64Val);
+ }
+
+ if (lType == NumberLong && rType == NumberInt) {
+ return (lhs._value.int64Val ^ static_cast<long long int>(rhs._value.int32Val));
+ }
+
+ if (lType == NumberLong && rType == NumberLong) {
+ return (lhs._value.int64Val ^ rhs._value.int64Val);
+ }
+
+ return SafeNum();
+ }
+
} // namespace mongo
diff --git a/src/mongo/util/safe_num.h b/src/mongo/util/safe_num.h
index 6e13225766e..fd75a23053a 100644
--- a/src/mongo/util/safe_num.h
+++ b/src/mongo/util/safe_num.h
@@ -121,6 +121,12 @@ namespace mutablebson {
SafeNum operator|(const SafeNum& rhs) const;
SafeNum& operator|=(const SafeNum& rhs);
+ // Bitwise 'xor' support
+ SafeNum bitXor(const SafeNum& rhs) const;
+ SafeNum operator^(const SafeNum& rhs) const;
+ SafeNum& operator^=(const SafeNum& rhs);
+
+
//
// output support
//
@@ -174,6 +180,11 @@ namespace mutablebson {
*/
static SafeNum orInternal(const SafeNum& lhs, const SafeNum& rhs);
+ /** Returns the bitwise 'xor' of lhs and rhs, taking into consideration their types. If
+ * the operation is invalid for the underlying types, returns an EOO instance.
+ */
+ static SafeNum xorInternal(const SafeNum& lhs, const SafeNum& rhs);
+
/**
* Extracts the value of 'snum' in a long format. It assumes 'snum' is an NumberInt
* or a NumberDouble.
diff --git a/src/mongo/util/safe_num_test.cpp b/src/mongo/util/safe_num_test.cpp
index 34192736ecf..e805d4ac293 100644
--- a/src/mongo/util/safe_num_test.cpp
+++ b/src/mongo/util/safe_num_test.cpp
@@ -265,4 +265,47 @@ namespace {
ASSERT_TRUE(expected.isIdentical(result_b_s));
}
+ TEST(BitXor, DoubleIsIgnored) {
+ const SafeNum val_int(static_cast<int>(1));
+ const SafeNum val_ll(static_cast<long long>(1));
+ const SafeNum val_double(1.0);
+ ASSERT_FALSE((val_int ^ val_double).isValid());
+ ASSERT_FALSE((val_double ^ val_int).isValid());
+ ASSERT_FALSE((val_ll ^ val_double).isValid());
+ ASSERT_FALSE((val_double ^ val_ll).isValid());
+ ASSERT_FALSE((val_double ^ val_double).isValid());
+ }
+
+ TEST(BitXor, 32and32) {
+ const SafeNum val1(static_cast<int>(0xE0F1U));
+ const SafeNum val2(static_cast<int>(0xDF01U));
+ const SafeNum result = val1 ^ val2;
+ const SafeNum expected(static_cast<int>(0x3FF0U));
+ ASSERT_EQUALS(mongo::NumberInt, result.type());
+ ASSERT_TRUE(expected.isIdentical(result));
+ }
+
+ TEST(BitXor, 64and64) {
+ const SafeNum val1(static_cast<long long>(0xE0F1E0F1E0F1ULL));
+ const SafeNum val2(static_cast<long long>(0xDF01DF01DF01ULL));
+ const SafeNum result = val1 ^ val2;
+ const SafeNum expected(static_cast<long long>(0x3FF03FF03FF0ULL));
+ ASSERT_EQUALS(mongo::NumberLong, result.type());
+ ASSERT_TRUE(expected.isIdentical(result));
+ }
+
+ TEST(BitXor, 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>(0x3FF0U));
+ 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));
+ }
+
} // unnamed namespace