interest: implement HopLimit encoding/decoding
Refs: #4806
Change-Id: I42ad15221575001d66747aa9f7eafcf3f00acf47
diff --git a/ndn-cxx/interest.cpp b/ndn-cxx/interest.cpp
index a984242..a25147b 100644
--- a/ndn-cxx/interest.cpp
+++ b/ndn-cxx/interest.cpp
@@ -98,7 +98,7 @@
#endif // NDN_CXX_HAVE_TESTS
}
- if (hasApplicationParameters()) {
+ if (getHopLimit() || hasApplicationParameters()) {
return encode03(encoder);
}
else {
@@ -133,7 +133,7 @@
}
// Nonce
- uint32_t nonce = getNonce(); // if nonce was unset, getNonce generates a random nonce
+ uint32_t nonce = getNonce(); // if nonce was unset, this generates a fresh nonce
totalLength += encoder.prependByteArrayBlock(tlv::Nonce, reinterpret_cast<uint8_t*>(&nonce), sizeof(nonce));
// Selectors
@@ -183,7 +183,11 @@
totalLength += encoder.prependBlock(b);
});
- // HopLimit: not yet supported
+ // HopLimit
+ if (getHopLimit()) {
+ uint8_t hopLimit = *getHopLimit();
+ totalLength += encoder.prependByteArrayBlock(tlv::HopLimit, &hopLimit, sizeof(hopLimit));
+ }
// InterestLifetime
if (getInterestLifetime() != DEFAULT_INTEREST_LIFETIME) {
@@ -192,7 +196,7 @@
}
// Nonce
- uint32_t nonce = getNonce(); // if nonce was unset, getNonce generates a random nonce
+ uint32_t nonce = getNonce(); // if nonce was unset, this generates a fresh nonce
totalLength += encoder.prependByteArrayBlock(tlv::Nonce, reinterpret_cast<uint8_t*>(&nonce), sizeof(nonce));
// ForwardingHint
@@ -229,10 +233,10 @@
EncodingEstimator estimator;
size_t estimatedSize = wireEncode(estimator);
- EncodingBuffer buffer(estimatedSize, 0);
- wireEncode(buffer);
+ EncodingBuffer encoder(estimatedSize, 0);
+ wireEncode(encoder);
- const_cast<Interest*>(this)->wireDecode(buffer.block());
+ const_cast<Interest*>(this)->wireDecode(encoder.block());
return m_wire;
}
@@ -248,9 +252,7 @@
if (!decode02()) {
decode03();
- if (!hasNonce()) {
- setNonce(getNonce());
- }
+ getNonce(); // force generation of nonce
}
m_isCanBePrefixSet = true; // don't trigger warning from decoded packet
@@ -351,9 +353,10 @@
m_name = std::move(tempName);
m_selectors = Selectors().setMaxSuffixComponents(1); // CanBePrefix=0
+ m_forwardingHint = {};
m_nonce.reset();
m_interestLifetime = DEFAULT_INTEREST_LIFETIME;
- m_forwardingHint = {};
+ m_hopLimit.reset();
m_parameters.clear();
int lastElement = 1; // last recognized element index, in spec order
@@ -417,7 +420,7 @@
if (element->value_size() != 1) {
NDN_THROW(Error("HopLimit element is malformed"));
}
- // TLV-VALUE is ignored
+ m_hopLimit = *element->value();
lastElement = 7;
break;
}
@@ -536,10 +539,20 @@
if (digestIndex == -2) {
NDN_THROW(std::invalid_argument("Name cannot have more than one ParametersSha256DigestComponent"));
}
- m_name = name;
- if (hasApplicationParameters()) {
- addOrReplaceParametersDigestComponent();
+ if (name != m_name) {
+ m_name = name;
+ if (hasApplicationParameters()) {
+ addOrReplaceParametersDigestComponent();
+ }
+ m_wire.reset();
}
+ return *this;
+}
+
+Interest&
+Interest::setForwardingHint(const DelegationList& value)
+{
+ m_forwardingHint = value;
m_wire.reset();
return *this;
}
@@ -547,8 +560,9 @@
uint32_t
Interest::getNonce() const
{
- if (!m_nonce) {
+ if (!hasNonce()) {
m_nonce = random::generateWord32();
+ m_wire.reset();
}
return *m_nonce;
}
@@ -556,8 +570,10 @@
Interest&
Interest::setNonce(uint32_t nonce)
{
- m_nonce = nonce;
- m_wire.reset();
+ if (nonce != m_nonce) {
+ m_nonce = nonce;
+ m_wire.reset();
+ }
return *this;
}
@@ -567,12 +583,11 @@
if (!hasNonce())
return;
- uint32_t oldNonce = getNonce();
- uint32_t newNonce = oldNonce;
- while (newNonce == oldNonce)
- newNonce = random::generateWord32();
+ uint32_t oldNonce = *m_nonce;
+ while (m_nonce == oldNonce)
+ m_nonce = random::generateWord32();
- setNonce(newNonce);
+ m_wire.reset();
}
Interest&
@@ -581,16 +596,20 @@
if (lifetime < 0_ms) {
NDN_THROW(std::invalid_argument("InterestLifetime must be >= 0"));
}
- m_interestLifetime = lifetime;
- m_wire.reset();
+ if (lifetime != m_interestLifetime) {
+ m_interestLifetime = lifetime;
+ m_wire.reset();
+ }
return *this;
}
Interest&
-Interest::setForwardingHint(const DelegationList& value)
+Interest::setHopLimit(optional<uint8_t> hopLimit)
{
- m_forwardingHint = value;
- m_wire.reset();
+ if (hopLimit != m_hopLimit) {
+ m_hopLimit = hopLimit;
+ m_wire.reset();
+ }
return *this;
}
diff --git a/ndn-cxx/interest.hpp b/ndn-cxx/interest.hpp
index 9c91c2e..432f68c 100644
--- a/ndn-cxx/interest.hpp
+++ b/ndn-cxx/interest.hpp
@@ -283,6 +283,19 @@
Interest&
setInterestLifetime(time::milliseconds lifetime);
+ optional<uint8_t>
+ getHopLimit() const
+ {
+ return m_hopLimit;
+ }
+
+ /** @brief Set the Interest's hop limit.
+ *
+ * Use `setHopLimit(nullopt)` to remove any hop limit from the Interest.
+ */
+ Interest&
+ setHopLimit(optional<uint8_t> hopLimit);
+
bool
hasApplicationParameters() const noexcept
{
@@ -539,9 +552,10 @@
Name m_name;
Selectors m_selectors; // NDN Packet Format v0.2 only
mutable bool m_isCanBePrefixSet = false;
+ DelegationList m_forwardingHint;
mutable optional<uint32_t> m_nonce;
time::milliseconds m_interestLifetime;
- DelegationList m_forwardingHint;
+ optional<uint8_t> m_hopLimit;
// Stores the "Interest parameters", i.e., all maybe-unrecognized non-critical TLV
// elements that appear at the end of the Interest, starting from ApplicationParameters.
diff --git a/tests/unit/interest.t.cpp b/tests/unit/interest.t.cpp
index e536755..e7354e9 100644
--- a/tests/unit/interest.t.cpp
+++ b/tests/unit/interest.t.cpp
@@ -55,12 +55,13 @@
Interest i;
BOOST_CHECK_EQUAL(i.hasWire(), false);
BOOST_CHECK_EQUAL(i.getName(), "/");
+ BOOST_CHECK_EQUAL(i.hasSelectors(), false);
BOOST_CHECK_EQUAL(i.getCanBePrefix(), true);
BOOST_CHECK_EQUAL(i.getMustBeFresh(), false);
BOOST_CHECK_EQUAL(i.getForwardingHint().empty(), true);
BOOST_CHECK_EQUAL(i.hasNonce(), false);
BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
- BOOST_CHECK_EQUAL(i.hasSelectors(), false);
+ BOOST_CHECK(i.getHopLimit() == nullopt);
BOOST_CHECK_EQUAL(i.hasApplicationParameters(), false);
BOOST_CHECK_EQUAL(i.getApplicationParameters().isValid(), false);
BOOST_CHECK_EQUAL(i.isParametersDigestValid(), true);
@@ -94,8 +95,10 @@
Interest i2(wire1);
BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
BOOST_CHECK(i2.getSelectors().empty());
+ BOOST_CHECK_EQUAL(i2.getForwardingHint().empty(), true);
BOOST_CHECK_EQUAL(i2.getNonce(), 1);
BOOST_CHECK_EQUAL(i2.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(i2.getHopLimit() == nullopt);
BOOST_CHECK_EQUAL(i2.hasApplicationParameters(), false);
BOOST_CHECK_EQUAL(i2.isParametersDigestValid(), true);
@@ -135,9 +138,10 @@
Interest i2(wire1);
BOOST_CHECK_EQUAL(i2.getName(), "/local/ndn/prefix");
BOOST_CHECK_EQUAL(i2.getMinSuffixComponents(), 1);
+ BOOST_CHECK_EQUAL(i2.getForwardingHint(), DelegationList({{1, "/A"}}));
BOOST_CHECK_EQUAL(i2.getNonce(), 1);
BOOST_CHECK_EQUAL(i2.getInterestLifetime(), 1000_ms);
- BOOST_CHECK_EQUAL(i2.getForwardingHint(), DelegationList({{1, "/A"}}));
+ BOOST_CHECK(i2.getHopLimit() == nullopt);
BOOST_CHECK_EQUAL(i2.hasApplicationParameters(), false);
BOOST_CHECK_EQUAL(i2.isParametersDigestValid(), true);
@@ -220,6 +224,7 @@
// BOOST_CHECK_EQUAL(i2.getForwardingHint().empty(), true);
// BOOST_CHECK_EQUAL(i2.getNonce(), 1);
// BOOST_CHECK_EQUAL(i2.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+// BOOST_CHECK(i2.getHopLimit() == nullopt);
// BOOST_CHECK_EQUAL(i2.hasApplicationParameters(), false);
// BOOST_CHECK_EQUAL(i2.getApplicationParameters().isValid(), false);
// BOOST_CHECK_EQUAL(i2.getPublisherPublicKeyLocator().empty(), true);
@@ -261,6 +266,7 @@
BOOST_CHECK_EQUAL(i2.getForwardingHint().empty(), true);
BOOST_CHECK_EQUAL(i2.getNonce(), 1);
BOOST_CHECK_EQUAL(i2.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(i2.getHopLimit() == nullopt);
BOOST_CHECK_EQUAL(i2.hasApplicationParameters(), true);
BOOST_CHECK_EQUAL(i2.getApplicationParameters(), "2404C0C1C2C3"_block);
BOOST_CHECK_EQUAL(i2.getPublisherPublicKeyLocator().empty(), true);
@@ -269,7 +275,7 @@
BOOST_AUTO_TEST_CASE(Full)
{
const uint8_t WIRE[] = {
- 0x05, 0x59, // Interest
+ 0x05, 0x5c, // Interest
0x07, 0x36, // Name
0x08, 0x05, 0x6c, 0x6f, 0x63, 0x61, 0x6c, // GenericNameComponent
0x08, 0x03, 0x6e, 0x64, 0x6e, // GenericNameComponent
@@ -288,8 +294,10 @@
0x08, 0x01, 0x48,
0x0a, 0x04, // Nonce
0x4a, 0xcb, 0x1e, 0x4c,
- 0x0c, 0x02, // Interest Lifetime
+ 0x0c, 0x02, // InterestLifetime
0x76, 0xa1,
+ 0x22, 0x01, // HopLimit
+ 0xdc,
0x24, 0x04, // ApplicationParameters
0xc0, 0xc1, 0xc2, 0xc3
};
@@ -301,6 +309,7 @@
i1.setForwardingHint(DelegationList({{15893, "/H"}}));
i1.setNonce(0x4c1ecb4a);
i1.setInterestLifetime(30369_ms);
+ i1.setHopLimit(220);
i1.setApplicationParameters("2404C0C1C2C3"_block);
i1.setMinSuffixComponents(1); // v0.2-only elements will not be encoded
i1.setExclude(Exclude().excludeAfter(name::Component("J"))); // v0.2-only elements will not be encoded
@@ -318,6 +327,7 @@
BOOST_CHECK_EQUAL(i2.hasNonce(), true);
BOOST_CHECK_EQUAL(i2.getNonce(), 0x4c1ecb4a);
BOOST_CHECK_EQUAL(i2.getInterestLifetime(), 30369_ms);
+ BOOST_CHECK_EQUAL(*i2.getHopLimit(), 220);
BOOST_CHECK_EQUAL(i2.getApplicationParameters(), "2404C0C1C2C3"_block);
BOOST_CHECK_EQUAL(i2.getMinSuffixComponents(), -1); // Default because minSuffixComponents was not encoded
BOOST_CHECK_EQUAL(i2.getExclude().empty(), true); // Exclude was not encoded
@@ -359,6 +369,7 @@
i.setForwardingHint({{10309, "/F"}});
i.setNonce(0x03d645a8);
i.setInterestLifetime(18554_ms);
+ i.setHopLimit(64);
i.setPublisherPublicKeyLocator(Name("/K"));
i.setApplicationParameters("2404A0A1A2A3"_block);
}
@@ -378,6 +389,7 @@
BOOST_CHECK_EQUAL(i.getForwardingHint().empty(), true);
BOOST_CHECK_EQUAL(i.hasNonce(), true); // a random nonce is generated
BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(i.getHopLimit() == nullopt);
BOOST_CHECK_EQUAL(i.getPublisherPublicKeyLocator().empty(), true);
BOOST_CHECK_EQUAL(i.hasApplicationParameters(), false);
BOOST_CHECK_EQUAL(i.getApplicationParameters().isValid(), false);
@@ -398,6 +410,7 @@
BOOST_CHECK_EQUAL(i.getForwardingHint().empty(), true);
BOOST_CHECK_EQUAL(i.hasNonce(), true); // a random nonce is generated
BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
+ BOOST_CHECK(i.getHopLimit() == nullopt);
BOOST_CHECK_EQUAL(i.hasApplicationParameters(), false);
BOOST_CHECK_EQUAL(i.getApplicationParameters().isValid(), false);
}
@@ -414,17 +427,19 @@
BOOST_CHECK_EQUAL(i.hasNonce(), true);
BOOST_CHECK_EQUAL(i.getNonce(), 0x4c1ecb4a);
BOOST_CHECK_EQUAL(i.getInterestLifetime(), 30369_ms);
- // HopLimit=214 is not stored
+ BOOST_CHECK_EQUAL(*i.getHopLimit(), 214);
BOOST_CHECK_EQUAL(i.hasApplicationParameters(), false);
BOOST_CHECK_EQUAL(i.getApplicationParameters().isValid(), false);
// encode without modification: retain original wire encoding
BOOST_CHECK_EQUAL(i.wireEncode().value_size(), 49);
- // modify then re-encode as v0.2 format
+ // modify then re-encode as v0.3 format: unrecognized elements are discarded
i.setName("/J");
BOOST_CHECK_EQUAL(i.wireEncode(),
- "0520 0703(08014A) 09021200 0A044ACB1E4C 0C0276A1 1E0B(1F09 1E023E15 0703080148)"_block);
+ "0523 0703(08014A) "
+ "2100 1200 1E0B(1F09 1E023E15 0703080148) "
+ "0A044ACB1E4C 0C0276A1 2201D6"_block);
}
BOOST_AUTO_TEST_CASE(FullWithParameters)
@@ -440,30 +455,29 @@
BOOST_CHECK_EQUAL(i.hasNonce(), true);
BOOST_CHECK_EQUAL(i.getNonce(), 0x4c1ecb4a);
BOOST_CHECK_EQUAL(i.getInterestLifetime(), 30369_ms);
- // HopLimit=214 is not stored
+ BOOST_CHECK_EQUAL(*i.getHopLimit(), 214);
BOOST_CHECK_EQUAL(i.hasApplicationParameters(), true);
BOOST_CHECK_EQUAL(i.getApplicationParameters(), "2404C0C1C2C3"_block);
// encode without modification: retain original wire encoding
BOOST_CHECK_EQUAL(i.wireEncode().value_size(), 91);
- // modify then re-encode as v0.3 format:
- // - unrecognized elements after ApplicationParameters are preserved, the rest are discarded
- // - HopLimit is dropped (encoding not implemented)
+ // modify then re-encode as v0.3 format: unrecognized elements
+ // after ApplicationParameters are preserved, the rest are discarded
i.setName("/J");
BOOST_CHECK_EQUAL(i.isParametersDigestValid(), true);
BOOST_CHECK_EQUAL(i.wireEncode(),
- "054A 0725(08014A 0220F16DB273F40436A852063F864D5072B01EAD53151F5A688EA1560492BEBEDD05) "
+ "054D 0725(08014A 0220F16DB273F40436A852063F864D5072B01EAD53151F5A688EA1560492BEBEDD05) "
"2100 1200 1E0B(1F09 1E023E15 0703080148) "
- "0A044ACB1E4C 0C0276A1 2404C0C1C2C3 FC00"_block);
+ "0A044ACB1E4C 0C0276A1 2201D6 2404C0C1C2C3 FC00"_block);
// modify ApplicationParameters: unrecognized elements are preserved
i.setApplicationParameters("2402CAFE"_block);
BOOST_CHECK_EQUAL(i.isParametersDigestValid(), true);
BOOST_CHECK_EQUAL(i.wireEncode(),
- "0548 0725(08014A 02205FDA67967EE302FC457E41B7D3D51BA6A9379574D193FD88F64954BF16C2927A) "
+ "054B 0725(08014A 02205FDA67967EE302FC457E41B7D3D51BA6A9379574D193FD88F64954BF16C2927A) "
"2100 1200 1E0B(1F09 1E023E15 0703080148) "
- "0A044ACB1E4C 0C0276A1 2402CAFE FC00"_block);
+ "0A044ACB1E4C 0C0276A1 2201D6 2402CAFE FC00"_block);
}
BOOST_AUTO_TEST_CASE(CriticalElementOutOfOrder)
@@ -501,7 +515,7 @@
"2201D6 2200 2404C0C1C2C3 22020101"_block);
BOOST_CHECK_EQUAL(i.getName(),
"/I/params-sha256=ff9100e04eaadcf30674d98026a051ba25f56b69bfa026dcccd72c6ea0f7315a");
- // HopLimit=214 is not stored
+ BOOST_CHECK_EQUAL(*i.getHopLimit(), 214);
BOOST_CHECK_EQUAL(i.hasApplicationParameters(), true);
BOOST_CHECK_EQUAL(i.getApplicationParameters(), "2404C0C1C2C3"_block);
@@ -510,6 +524,7 @@
"2100 1200 0A044ACB1E4C 0C0276A1 2201D6 2404C0C1C2C3 2401EE"_block);
BOOST_CHECK_EQUAL(i.getName(),
"/I/params-sha256=ff9100e04eaadcf30674d98026a051ba25f56b69bfa026dcccd72c6ea0f7315a");
+ BOOST_CHECK_EQUAL(*i.getHopLimit(), 214);
BOOST_CHECK_EQUAL(i.hasApplicationParameters(), true);
BOOST_CHECK_EQUAL(i.getApplicationParameters(), "2404C0C1C2C3"_block);
}
@@ -625,12 +640,14 @@
BOOST_AUTO_TEST_CASE_EXPECTED_FAILURES(MatchesInterest, 1)
BOOST_AUTO_TEST_CASE(MatchesInterest)
{
- Interest interest("/A");
- interest.setCanBePrefix(true)
+ Interest interest;
+ interest.setName("/A")
+ .setCanBePrefix(true)
.setMustBeFresh(true)
.setForwardingHint({{1, "/H"}})
.setNonce(2228)
- .setInterestLifetime(5_s);
+ .setInterestLifetime(5_s)
+ .setHopLimit(90);
Interest other;
BOOST_CHECK_EQUAL(interest.matchesInterest(other), false);
@@ -652,6 +669,9 @@
other.setInterestLifetime(3_s);
BOOST_CHECK_EQUAL(interest.matchesInterest(other), true);
+
+ other.setHopLimit(31);
+ BOOST_CHECK_EQUAL(interest.matchesInterest(other), true);
}
BOOST_AUTO_TEST_CASE(SetName)
@@ -765,8 +785,7 @@
BOOST_CHECK_THROW(Interest("/A", -1_ms), std::invalid_argument);
BOOST_CHECK_NO_THROW(Interest("/A", 0_ms));
- Interest i("/local/ndn/prefix");
- i.setNonce(1);
+ Interest i;
BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
BOOST_CHECK_THROW(i.setInterestLifetime(-1_ms), std::invalid_argument);
BOOST_CHECK_EQUAL(i.getInterestLifetime(), DEFAULT_INTEREST_LIFETIME);
@@ -774,6 +793,19 @@
BOOST_CHECK_EQUAL(i.getInterestLifetime(), 0_ms);
i.setInterestLifetime(1_ms);
BOOST_CHECK_EQUAL(i.getInterestLifetime(), 1_ms);
+
+ i = Interest("/B", 15_s);
+ BOOST_CHECK_EQUAL(i.getInterestLifetime(), 15_s);
+}
+
+BOOST_AUTO_TEST_CASE(SetHopLimit)
+{
+ Interest i;
+ BOOST_CHECK(i.getHopLimit() == nullopt);
+ i.setHopLimit(42);
+ BOOST_CHECK(i.getHopLimit() == 42);
+ i.setHopLimit(nullopt);
+ BOOST_CHECK(i.getHopLimit() == nullopt);
}
BOOST_AUTO_TEST_CASE(SetApplicationParameters)
@@ -886,6 +918,15 @@
BOOST_CHECK_EQUAL(a == b, true);
BOOST_CHECK_EQUAL(a != b, false);
+ // compare ForwardingHint
+ a.setForwardingHint({{1, "/H"}});
+ BOOST_CHECK_EQUAL(a == b, false);
+ BOOST_CHECK_EQUAL(a != b, true);
+
+ b.setForwardingHint({{1, "/H"}});
+ BOOST_CHECK_EQUAL(a == b, true);
+ BOOST_CHECK_EQUAL(a != b, false);
+
// compare Nonce
a.setNonce(100);
BOOST_CHECK_EQUAL(a == b, false);
@@ -904,12 +945,12 @@
BOOST_CHECK_EQUAL(a == b, true);
BOOST_CHECK_EQUAL(a != b, false);
- // compare ForwardingHint
- a.setForwardingHint({{1, "/H"}});
+ // compare HopLimit
+ a.setHopLimit(255);
BOOST_CHECK_EQUAL(a == b, false);
BOOST_CHECK_EQUAL(a != b, true);
- b.setForwardingHint({{1, "/H"}});
+ b.setHopLimit(255);
BOOST_CHECK_EQUAL(a == b, true);
BOOST_CHECK_EQUAL(a != b, false);