management: add Flags and Mask fields to faces/create and faces/update
refs #3731
Change-Id: I35fb84a875767649da0cb354be5e5c07de179198
diff --git a/src/encoding/nfd-constants.hpp b/src/encoding/nfd-constants.hpp
index 671bd2c..4c38bd9 100644
--- a/src/encoding/nfd-constants.hpp
+++ b/src/encoding/nfd-constants.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2014 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -64,6 +64,14 @@
/** \ingroup management
*/
+enum FaceFlagBit {
+ /** \brief bit that controls whether local fields are enabled on a face
+ */
+ BIT_LOCAL_FIELDS_ENABLED = 0
+};
+
+/** \ingroup management
+ */
enum LinkType : uint8_t {
LINK_TYPE_NONE = std::numeric_limits<uint8_t>::max(),
/** \brief link is point-to-point
diff --git a/src/encoding/tlv-nfd.hpp b/src/encoding/tlv-nfd.hpp
index 51cfc4f..0033f22 100644
--- a/src/encoding/tlv-nfd.hpp
+++ b/src/encoding/tlv-nfd.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -39,6 +39,7 @@
Origin = 111,
Cost = 106,
Flags = 108,
+ Mask = 112,
Strategy = 107,
ExpirationPeriod = 109,
diff --git a/src/management/nfd-control-command.cpp b/src/management/nfd-control-command.cpp
index 88db4f9..9853f9d 100644
--- a/src/management/nfd-control-command.cpp
+++ b/src/management/nfd-control-command.cpp
@@ -98,22 +98,37 @@
{
m_requestValidator
.required(CONTROL_PARAMETER_URI)
- .optional(CONTROL_PARAMETER_FACE_PERSISTENCY);
+ .optional(CONTROL_PARAMETER_FACE_PERSISTENCY)
+ .optional(CONTROL_PARAMETER_FLAGS)
+ .optional(CONTROL_PARAMETER_MASK);
m_responseValidator
- .required(CONTROL_PARAMETER_URI)
.required(CONTROL_PARAMETER_FACE_ID)
- .required(CONTROL_PARAMETER_FACE_PERSISTENCY);
+ .required(CONTROL_PARAMETER_FACE_PERSISTENCY)
+ .optional(CONTROL_PARAMETER_FLAGS)
+ .optional(CONTROL_PARAMETER_URI);
}
void
FaceCreateCommand::applyDefaultsToRequest(ControlParameters& parameters) const
{
+ parameters.setFaceId(0);
+
if (!parameters.hasFacePersistency()) {
parameters.setFacePersistency(FacePersistency::FACE_PERSISTENCY_PERSISTENT);
}
}
void
+FaceCreateCommand::validateRequest(const ControlParameters& parameters) const
+{
+ this->ControlCommand::validateRequest(parameters);
+
+ if (parameters.hasFlags() != parameters.hasMask()) {
+ BOOST_THROW_EXCEPTION(ArgumentError("Flags must be accompanied by Mask"));
+ }
+}
+
+void
FaceCreateCommand::validateResponse(const ControlParameters& parameters) const
{
this->ControlCommand::validateResponse(parameters);
@@ -127,12 +142,21 @@
: ControlCommand("faces", "update")
{
m_requestValidator
- .required(CONTROL_PARAMETER_FACE_ID)
- .optional(CONTROL_PARAMETER_FACE_PERSISTENCY);
+ .optional(CONTROL_PARAMETER_FACE_ID)
+ .optional(CONTROL_PARAMETER_FACE_PERSISTENCY)
+ .optional(CONTROL_PARAMETER_FLAGS)
+ .optional(CONTROL_PARAMETER_MASK);
m_responseValidator
- .required(CONTROL_PARAMETER_FACE_ID)
- .optional(CONTROL_PARAMETER_FACE_PERSISTENCY);
- m_responseValidator = m_requestValidator;
+ .optional(CONTROL_PARAMETER_FACE_ID)
+ .optional(CONTROL_PARAMETER_FACE_PERSISTENCY)
+ .optional(CONTROL_PARAMETER_FLAGS)
+ .optional(CONTROL_PARAMETER_MASK);
+}
+
+void
+FaceUpdateCommand::applyDefaultsToRequest(ControlParameters& parameters) const
+{
+ parameters.setFaceId(0);
}
void
@@ -140,8 +164,8 @@
{
this->ControlCommand::validateRequest(parameters);
- if (parameters.getFaceId() == 0) {
- BOOST_THROW_EXCEPTION(ArgumentError("FaceId must not be zero"));
+ if (parameters.hasFlags() != parameters.hasMask()) {
+ BOOST_THROW_EXCEPTION(ArgumentError("Flags must be accompanied by Mask"));
}
}
diff --git a/src/management/nfd-control-command.hpp b/src/management/nfd-control-command.hpp
index dfe876b..2f1c4e0 100644
--- a/src/management/nfd-control-command.hpp
+++ b/src/management/nfd-control-command.hpp
@@ -148,6 +148,9 @@
applyDefaultsToRequest(ControlParameters& parameters) const override;
virtual void
+ validateRequest(const ControlParameters& parameters) const override;
+
+ virtual void
validateResponse(const ControlParameters& parameters) const override;
};
@@ -163,6 +166,9 @@
FaceUpdateCommand();
virtual void
+ applyDefaultsToRequest(ControlParameters& parameters) const override;
+
+ virtual void
validateRequest(const ControlParameters& parameters) const override;
virtual void
diff --git a/src/management/nfd-control-parameters.cpp b/src/management/nfd-control-parameters.cpp
index f94148b..f68ca26 100644
--- a/src/management/nfd-control-parameters.cpp
+++ b/src/management/nfd-control-parameters.cpp
@@ -61,6 +61,9 @@
if (this->hasStrategy()) {
totalLength += prependNestedBlock(encoder, tlv::nfd::Strategy, m_strategy);
}
+ if (this->hasMask()) {
+ totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Mask, m_mask);
+ }
if (this->hasFlags()) {
totalLength += prependNonNegativeIntegerBlock(encoder, tlv::nfd::Flags, m_flags);
}
@@ -167,6 +170,12 @@
m_flags = static_cast<uint64_t>(readNonNegativeInteger(*val));
}
+ val = m_wire.find(tlv::nfd::Mask);
+ m_hasFields[CONTROL_PARAMETER_MASK] = val != m_wire.elements_end();
+ if (this->hasMask()) {
+ m_mask = static_cast<uint64_t>(readNonNegativeInteger(*val));
+ }
+
val = m_wire.find(tlv::nfd::Strategy);
m_hasFields[CONTROL_PARAMETER_STRATEGY] = val != m_wire.elements_end();
if (this->hasStrategy()) {
@@ -192,6 +201,79 @@
}
}
+bool
+ControlParameters::hasFlagBit(size_t bit) const
+{
+ if (bit >= 64) {
+ BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+ }
+
+ if (!hasMask()) {
+ return false;
+ }
+
+ return getMask() & (1 << bit);
+}
+
+bool
+ControlParameters::getFlagBit(size_t bit) const
+{
+ if (bit >= 64) {
+ BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+ }
+
+ if (!hasFlags()) {
+ return false;
+ }
+
+ return getFlags() & (1 << bit);
+}
+
+ControlParameters&
+ControlParameters::setFlagBit(size_t bit, bool value, bool wantMask/* = true*/)
+{
+ if (bit >= 64) {
+ BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+ }
+
+ uint64_t flags = hasFlags() ? getFlags() : 0;
+ if (value) {
+ flags |= (1 << bit);
+ }
+ else {
+ flags &= ~(1 << bit);
+ }
+ setFlags(flags);
+
+ if (wantMask) {
+ uint64_t mask = hasMask() ? getMask() : 0;
+ mask |= (1 << bit);
+ setMask(mask);
+ }
+
+ return *this;
+}
+
+ControlParameters&
+ControlParameters::unsetFlagBit(size_t bit)
+{
+ if (bit >= 64) {
+ BOOST_THROW_EXCEPTION(std::out_of_range("bit must be within range [0, 64)"));
+ }
+
+ uint64_t mask = hasMask() ? getMask() : 0;
+ mask &= ~(1 << bit);
+ if (mask == 0) {
+ unsetMask();
+ unsetFlags();
+ }
+ else {
+ setMask(mask);
+ }
+
+ return *this;
+}
+
std::ostream&
operator<<(std::ostream& os, const ControlParameters& parameters)
{
@@ -222,7 +304,15 @@
}
if (parameters.hasFlags()) {
- os << "Flags: " << parameters.getFlags() << ", ";
+ std::ios_base::fmtflags osFlags = os.flags();
+ os << "Flags: " << std::showbase << std::hex << parameters.getFlags() << ", ";
+ os.flags(osFlags);
+ }
+
+ if (parameters.hasMask()) {
+ std::ios_base::fmtflags osFlags = os.flags();
+ os << "Mask: " << std::showbase << std::hex << parameters.getMask() << ", ";
+ os.flags(osFlags);
}
if (parameters.hasStrategy()) {
diff --git a/src/management/nfd-control-parameters.hpp b/src/management/nfd-control-parameters.hpp
index ed077a7..92f40b8 100644
--- a/src/management/nfd-control-parameters.hpp
+++ b/src/management/nfd-control-parameters.hpp
@@ -41,6 +41,7 @@
CONTROL_PARAMETER_ORIGIN,
CONTROL_PARAMETER_COST,
CONTROL_PARAMETER_FLAGS,
+ CONTROL_PARAMETER_MASK,
CONTROL_PARAMETER_STRATEGY,
CONTROL_PARAMETER_EXPIRATION_PERIOD,
CONTROL_PARAMETER_FACE_PERSISTENCY,
@@ -55,6 +56,7 @@
"Origin",
"Cost",
"Flags",
+ "Mask",
"Strategy",
"ExpirationPeriod",
"FacePersistency"
@@ -62,6 +64,7 @@
/**
* \ingroup management
+ * \deprecated use Flags+Mask fields instead
*/
enum LocalControlFeature {
LOCAL_CONTROL_FEATURE_INCOMING_FACE_ID = 1,
@@ -69,10 +72,10 @@
};
/**
- * @ingroup management
- * @brief represents parameters in a ControlCommand request or response
- * @sa http://redmine.named-data.net/projects/nfd/wiki/ControlCommand#ControlParameters
- * @details This type is copyable because it's an abstraction of a TLV type.
+ * \ingroup management
+ * \brief represents parameters in a ControlCommand request or response
+ * \sa http://redmine.named-data.net/projects/nfd/wiki/ControlCommand#ControlParameters
+ * \details This type is copyable because it's an abstraction of a TLV type.
*/
class ControlParameters : public ndn::mgmt::ControlParameters
{
@@ -103,7 +106,6 @@
wireDecode(const Block& wire) final;
public: // getters & setters
-
bool
hasName() const
{
@@ -194,12 +196,18 @@
return *this;
}
+ /**
+ * \deprecated use Flags+Mask fields instead
+ */
bool
hasLocalControlFeature() const
{
return m_hasFields[CONTROL_PARAMETER_LOCAL_CONTROL_FEATURE];
}
+ /**
+ * \deprecated use Flags+Mask fields instead
+ */
LocalControlFeature
getLocalControlFeature() const
{
@@ -207,6 +215,9 @@
return m_localControlFeature;
}
+ /**
+ * \deprecated use Flags+Mask fields instead
+ */
ControlParameters&
setLocalControlFeature(LocalControlFeature localControlFeature)
{
@@ -216,6 +227,9 @@
return *this;
}
+ /**
+ * \deprecated use Flags+Mask fields instead
+ */
ControlParameters&
unsetLocalControlFeature()
{
@@ -315,6 +329,36 @@
}
bool
+ hasMask() const
+ {
+ return m_hasFields[CONTROL_PARAMETER_MASK];
+ }
+
+ uint64_t
+ getMask() const
+ {
+ BOOST_ASSERT(this->hasMask());
+ return m_mask;
+ }
+
+ ControlParameters&
+ setMask(uint64_t mask)
+ {
+ m_wire.reset();
+ m_mask = mask;
+ m_hasFields[CONTROL_PARAMETER_MASK] = true;
+ return *this;
+ }
+
+ ControlParameters&
+ unsetMask()
+ {
+ m_wire.reset();
+ m_hasFields[CONTROL_PARAMETER_MASK] = false;
+ return *this;
+ }
+
+ bool
hasStrategy() const
{
return m_hasFields[CONTROL_PARAMETER_STRATEGY];
@@ -410,6 +454,38 @@
return m_hasFields;
}
+public: // Flags and Mask helpers
+ /**
+ * \return whether bit is enabled in Mask
+ * \param bit bit position within range [0, 64) (least significant bit is 0)
+ */
+ bool
+ hasFlagBit(size_t bit) const;
+
+ /**
+ * \return bit at a position in Flags
+ * \param bit bit position within range [0, 64) (least significant bit is 0)
+ */
+ bool
+ getFlagBit(size_t bit) const;
+
+ /**
+ * \brief set a bit in Flags
+ * \param bit bit position within range [0, 64) (least significant bit is 0)
+ * \param value new value in Flags
+ * \param wantMask if true, enable the bit in Mask
+ */
+ ControlParameters&
+ setFlagBit(size_t bit, bool value, bool wantMask = true);
+
+ /**
+ * \brief disable a bit in Mask
+ * \param bit bit position within range [0, 64) (least significant bit is 0)
+ * \post If all bits are disabled, Flags and Mask fields are deleted.
+ */
+ ControlParameters&
+ unsetFlagBit(size_t bit);
+
private: // fields
std::vector<bool> m_hasFields;
@@ -420,6 +496,7 @@
uint64_t m_origin;
uint64_t m_cost;
uint64_t m_flags;
+ uint64_t m_mask;
Name m_strategy;
time::milliseconds m_expirationPeriod;
FacePersistency m_facePersistency;
diff --git a/tests/unit-tests/management/nfd-control-command.t.cpp b/tests/unit-tests/management/nfd-control-command.t.cpp
index 67696b2..95857d4 100644
--- a/tests/unit-tests/management/nfd-control-command.t.cpp
+++ b/tests/unit-tests/management/nfd-control-command.t.cpp
@@ -51,26 +51,40 @@
ControlParameters p4;
p4.setUri("tcp4://192.0.2.1")
- .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+ .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+ .setFlags(0x3)
+ .setMask(0x1);
BOOST_CHECK_NO_THROW(command.validateRequest(p4));
+ ControlParameters p5;
+ p5.setUri("tcp4://192.0.2.1")
+ .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+ .setFlags(0x1);
+ BOOST_CHECK_THROW(command.validateRequest(p5), ControlCommand::ArgumentError);
+
p4.unsetFacePersistency();
BOOST_CHECK_NO_THROW(command.validateRequest(p4));
command.applyDefaultsToRequest(p4);
BOOST_REQUIRE(p4.hasFacePersistency());
BOOST_CHECK_EQUAL(p4.getFacePersistency(), ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
- ControlParameters p5;
- p5.setUri("tcp4://192.0.2.1")
- .setFaceId(4)
- .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
- BOOST_CHECK_NO_THROW(command.validateResponse(p5));
-
ControlParameters p6;
- p6.setUri("tcp4://192.0.2.1:6363");
- Name n6;
- BOOST_CHECK_NO_THROW(n6 = command.getRequestName("/PREFIX", p6));
- BOOST_CHECK(Name("ndn:/PREFIX/faces/create").isPrefixOf(n6));
+ p6.setFaceId(4)
+ .setUri("tcp4://192.0.2.1")
+ .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p6));
+
+ ControlParameters p7;
+ p7.setUri("tcp4://192.0.2.1:6363");
+ Name n7;
+ BOOST_CHECK_NO_THROW(n7 = command.getRequestName("/PREFIX", p7));
+ BOOST_CHECK(Name("ndn:/PREFIX/faces/create").isPrefixOf(n7));
+
+ ControlParameters p8;
+ p8.setFaceId(5)
+ .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERMANENT)
+ .setFlags(0x2);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p8));
}
BOOST_AUTO_TEST_CASE(FaceUpdate)
@@ -79,8 +93,8 @@
ControlParameters p1;
p1.setFaceId(0);
- BOOST_CHECK_THROW(command.validateRequest(p1), ControlCommand::ArgumentError);
- BOOST_CHECK_THROW(command.validateResponse(p1), ControlCommand::ArgumentError);
+ BOOST_CHECK_NO_THROW(command.validateRequest(p1));
+ BOOST_CHECK_NO_THROW(command.validateResponse(p1));
p1.setFaceId(1);
BOOST_CHECK_NO_THROW(command.validateRequest(p1));
@@ -88,26 +102,32 @@
ControlParameters p2;
p2.setFaceId(1)
- .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+ .setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT)
+ .setFlags(0x0)
+ .setMask(0x1);
BOOST_CHECK_NO_THROW(command.validateRequest(p2));
BOOST_CHECK_NO_THROW(command.validateResponse(p2));
+ p2.unsetFaceId();
+ BOOST_CHECK_NO_THROW(command.validateRequest(p2));
+
+ p2.unsetMask();
+ BOOST_CHECK_THROW(command.validateRequest(p2), ControlCommand::ArgumentError);
+
ControlParameters p3;
- p3.setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+ p3.setFaceId(1)
+ .setName("/ndn/name");
BOOST_CHECK_THROW(command.validateRequest(p3), ControlCommand::ArgumentError);
BOOST_CHECK_THROW(command.validateResponse(p3), ControlCommand::ArgumentError);
ControlParameters p4;
p4.setFaceId(1)
- .setName("/ndn/name");
+ .setUri("tcp4://192.0.2.1");
BOOST_CHECK_THROW(command.validateRequest(p4), ControlCommand::ArgumentError);
BOOST_CHECK_THROW(command.validateResponse(p4), ControlCommand::ArgumentError);
ControlParameters p5;
- p5.setFaceId(1)
- .setUri("tcp4://192.0.2.1");
- BOOST_CHECK_THROW(command.validateRequest(p5), ControlCommand::ArgumentError);
- BOOST_CHECK_THROW(command.validateResponse(p5), ControlCommand::ArgumentError);
+ BOOST_CHECK_NO_THROW(command.validateResponse(p5));
}
BOOST_AUTO_TEST_CASE(FaceDestroy)
diff --git a/tests/unit-tests/management/nfd-control-parameters.t.cpp b/tests/unit-tests/management/nfd-control-parameters.t.cpp
index 5a3a864..e12d675 100644
--- a/tests/unit-tests/management/nfd-control-parameters.t.cpp
+++ b/tests/unit-tests/management/nfd-control-parameters.t.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2013-2015 Regents of the University of California.
+ * Copyright (c) 2013-2016 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -34,18 +34,21 @@
ControlParameters parameters;
parameters.setUri("tcp4://192.0.2.1:6363");
parameters.setFacePersistency(ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+ parameters.setFlags(1);
+ parameters.setMask(1);
Block wire = parameters.wireEncode();
ControlParameters decoded(wire);
BOOST_CHECK_EQUAL(decoded.getUri(), "tcp4://192.0.2.1:6363");
BOOST_CHECK_EQUAL(decoded.getFacePersistency(), ndn::nfd::FacePersistency::FACE_PERSISTENCY_PERSISTENT);
+ BOOST_CHECK_EQUAL(decoded.getFlags(), 1);
+ BOOST_CHECK_EQUAL(decoded.getMask(), 1);
BOOST_CHECK_EQUAL(decoded.hasName(), false);
BOOST_CHECK_EQUAL(decoded.hasFaceId(), false);
BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
BOOST_CHECK_EQUAL(decoded.hasCost(), false);
- BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
}
@@ -89,6 +92,7 @@
BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
+ BOOST_CHECK_EQUAL(decoded.hasMask(), false);
BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
BOOST_CHECK_EQUAL(decoded.hasFacePersistency(), false);
@@ -112,6 +116,7 @@
BOOST_CHECK_EQUAL(decoded.hasOrigin(), false);
BOOST_CHECK_EQUAL(decoded.hasCost(), false);
BOOST_CHECK_EQUAL(decoded.hasFlags(), false);
+ BOOST_CHECK_EQUAL(decoded.hasMask(), false);
BOOST_CHECK_EQUAL(decoded.hasExpirationPeriod(), false);
BOOST_CHECK_EQUAL(decoded.hasFacePersistency(), false);
}
@@ -138,10 +143,128 @@
BOOST_CHECK_EQUAL(decoded.hasUri(), false);
BOOST_CHECK_EQUAL(decoded.hasLocalControlFeature(), false);
+ BOOST_CHECK_EQUAL(decoded.hasMask(), false);
BOOST_CHECK_EQUAL(decoded.hasStrategy(), false);
BOOST_CHECK_EQUAL(decoded.hasFacePersistency(), false);
}
+BOOST_AUTO_TEST_CASE(FlagsAndMask)
+{
+ ControlParameters p;
+
+ BOOST_CHECK(!p.hasFlags());
+ BOOST_CHECK(!p.hasMask());
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(!p.getFlagBit(0));
+
+ // Set bit 2 to true (change Mask)
+ p.setFlagBit(2, true);
+ // 2^2 = 4
+ BOOST_CHECK_EQUAL(p.getFlags(), 4);
+ BOOST_CHECK_EQUAL(p.getMask(), 4);
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+ BOOST_CHECK(!p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 3 to true (no change to Mask)
+ p.setFlagBit(3, true, false);
+ // 2^3 + 2^2 = 12
+ BOOST_CHECK_EQUAL(p.getFlags(), 12);
+ // 2^2 = 4
+ BOOST_CHECK_EQUAL(p.getMask(), 4);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+
+ // Set bit 1 to false (change Mask)
+ p.setFlagBit(1, false);
+ // 2^3 + 2^2 = 12
+ BOOST_CHECK_EQUAL(p.getFlags(), 12);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(p.getFlagBit(2));
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 2 to false (change Mask)
+ p.setFlagBit(2, false);
+ // 2^3 = 8
+ BOOST_CHECK_EQUAL(p.getFlags(), 8);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+ BOOST_CHECK(p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+
+ // Set bit 0 to true (change Mask)
+ p.setFlagBit(0, true);
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 + 2^0 = 7
+ BOOST_CHECK_EQUAL(p.getMask(), 7);
+ BOOST_CHECK(p.hasFlagBit(0));
+ BOOST_CHECK(p.getFlagBit(0));
+
+ // Unset bit 0
+ p.unsetFlagBit(0);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(p.hasFlagBit(1));
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(p.getFlagBit(0));
+
+ // Unset bit 3 (already unset in Mask, so no change)
+ p.unsetFlagBit(3);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^2 + 2^1 = 6
+ BOOST_CHECK_EQUAL(p.getMask(), 6);
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(p.getFlagBit(3));
+
+ // Unset bit 2
+ p.unsetFlagBit(2);
+ BOOST_REQUIRE(p.hasFlags());
+ BOOST_REQUIRE(p.hasMask());
+ // 2^3 + 2^0 = 9
+ BOOST_CHECK_EQUAL(p.getFlags(), 9);
+ // 2^1 = 2
+ BOOST_CHECK_EQUAL(p.getMask(), 2);
+ BOOST_CHECK(!p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+
+ // Unset bit 1
+ // Flags and Mask fields will be deleted as Mask is now 0
+ p.unsetFlagBit(1);
+ BOOST_CHECK(!p.hasFlags());
+ BOOST_CHECK(!p.hasMask());
+ BOOST_CHECK(!p.hasFlagBit(3));
+ BOOST_CHECK(!p.getFlagBit(3));
+ BOOST_CHECK(!p.hasFlagBit(2));
+ BOOST_CHECK(!p.getFlagBit(2));
+ BOOST_CHECK(!p.hasFlagBit(1));
+ BOOST_CHECK(!p.getFlagBit(1));
+ BOOST_CHECK(!p.hasFlagBit(0));
+ BOOST_CHECK(!p.getFlagBit(0));
+
+ BOOST_CHECK_THROW(p.setFlagBit(64, true), std::out_of_range);
+ BOOST_CHECK_THROW(p.unsetFlagBit(64), std::out_of_range);
+}
+
BOOST_AUTO_TEST_SUITE_END()
} // namespace tests