diff options
author | Andrew Morrow <acm@10gen.com> | 2013-08-19 10:23:30 -0400 |
---|---|---|
committer | Andrew Morrow <acm@10gen.com> | 2013-08-20 11:48:46 -0400 |
commit | fce3b4ce8fe848c332657f3accf596e6b087bce9 (patch) | |
tree | 8d68d5d120efde72b66984687306e1aba5fffb3f /src | |
parent | a8eab606724de2d23a6cafd069f4001a031ed909 (diff) | |
download | mongo-fce3b4ce8fe848c332657f3accf596e6b087bce9.tar.gz |
SERVER-4362 Implement xor support for $bit
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/ops/modifier_bit.cpp | 29 | ||||
-rw-r--r-- | src/mongo/db/ops/modifier_bit_test.cpp | 136 | ||||
-rw-r--r-- | src/mongo/util/safe_num-inl.h | 12 | ||||
-rw-r--r-- | src/mongo/util/safe_num.cpp | 23 | ||||
-rw-r--r-- | src/mongo/util/safe_num.h | 11 | ||||
-rw-r--r-- | src/mongo/util/safe_num_test.cpp | 43 |
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 |