face: piggyback Ack field with fixed-width encoding

refs #4403

Change-Id: I4b0123d20aea058581774d467452553724fddc61
diff --git a/daemon/face/lp-reliability.cpp b/daemon/face/lp-reliability.cpp
index 4ff2ac5..5a4621d 100644
--- a/daemon/face/lp-reliability.cpp
+++ b/daemon/face/lp-reliability.cpp
@@ -168,11 +168,10 @@
 
   while (!m_ackQueue.empty()) {
     lp::Sequence ackSeq = m_ackQueue.front();
-    // Ack Size = Ack Type (3 octets) + Ack Length (1 octet) + Value (1, 2, 4, or 8 octets)
-    ssize_t ackSize = tlv::sizeOfVarNumber(lp::tlv::Ack) +
-                      tlv::sizeOfVarNumber(
-                        tlv::sizeOfNonNegativeInteger(std::numeric_limits<lp::Sequence>::max())) +
-                      tlv::sizeOfNonNegativeInteger(ackSeq);
+    // Ack size = Ack TLV-TYPE (3 octets) + TLV-LENGTH (1 octet) + uint64_t (8 octets)
+    const ssize_t ackSize = tlv::sizeOfVarNumber(lp::tlv::Ack) +
+                            tlv::sizeOfVarNumber(sizeof(lp::Sequence)) +
+                            sizeof(lp::Sequence);
 
     if (ackSize > remainingSpace) {
       break;
diff --git a/tests/daemon/face/lp-reliability.t.cpp b/tests/daemon/face/lp-reliability.t.cpp
index 907cdc3..1c937ac 100644
--- a/tests/daemon/face/lp-reliability.t.cpp
+++ b/tests/daemon/face/lp-reliability.t.cpp
@@ -32,6 +32,7 @@
 #include "dummy-transport.hpp"
 
 #include <cstring>
+#include <unordered_set>
 
 namespace nfd {
 namespace face {
@@ -763,33 +764,41 @@
 
 BOOST_AUTO_TEST_CASE(PiggybackAcksMtu)
 {
-  // This test case tests for piggybacking Acks when there is an MTU on the link.
+  // MTU is 1500, payload has 60 octets plus 6 octets for LpPacket and Fragment TL and 10 octets for
+  // TxSequence, leaving 1426 octets for piggybacking. Each Ack header is 12 octets, so each
+  // LpPacket can carry 118 Acks, and it takes 9 LpPackets for 1000 Acks.
 
   transport->setMtu(1500);
 
+  std::unordered_set<lp::Sequence> expectedAcks;
   for (lp::Sequence i = 1000; i < 2000; i++) {
     reliability->m_ackQueue.push(i);
+    expectedAcks.insert(i);
   }
 
-  BOOST_CHECK(!reliability->m_ackQueue.empty());
-
-  for (uint32_t i = 0; i < 5; i++) {
+  for (uint32_t i = 1; i <= 9; i++) {
     lp::Packet pkt = makeFrag(i, 60);
     linkService->sendLpPackets({pkt});
 
-    BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), i + 1);
+    BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), i);
     lp::Packet sentPkt(transport->sentPackets.back().packet);
     BOOST_CHECK_EQUAL(getPktNo(sentPkt), i);
     BOOST_CHECK(sentPkt.has<lp::AckField>());
+
+    for (lp::Sequence ack : sentPkt.list<lp::AckField>()) {
+      BOOST_CHECK_EQUAL(expectedAcks.erase(ack), 1);
+    }
   }
 
   BOOST_CHECK(reliability->m_ackQueue.empty());
+  BOOST_CHECK(expectedAcks.empty());
 }
 
 BOOST_AUTO_TEST_CASE(PiggybackAcksMtuNoSpace)
 {
-  // This test case tests for piggybacking Acks when there is an MTU on the link, resulting in no
-  // space for Acks (and negative remainingSpace in the piggyback function).
+  // MTU is 250, payload has 230 octets plus 4 octets for LpPacket and Fragment TL and 10 octets for
+  // TxSequence, leaving 6 octets for piggybacking. Each Ack header is 12 octets, so there's no room
+  // to piggyback any Ack in LpPacket.
 
   transport->setMtu(250);
 
@@ -797,17 +806,15 @@
     reliability->m_ackQueue.push(i);
   }
 
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 100);
-
-  lp::Packet pkt = makeFrag(1, 240);
+  lp::Packet pkt = makeFrag(1, 230);
   linkService->sendLpPackets({pkt});
 
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 100);
-
   BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
   lp::Packet sentPkt(transport->sentPackets.back().packet);
   BOOST_CHECK_EQUAL(getPktNo(sentPkt), 1);
   BOOST_CHECK(!sentPkt.has<lp::AckField>());
+
+  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 100);
 }
 
 BOOST_AUTO_TEST_CASE(StartIdleAckTimer)
@@ -848,188 +855,78 @@
 
 BOOST_AUTO_TEST_CASE(IdleAckTimer)
 {
-  // T+1ms
-  advanceClocks(time::milliseconds(1), 1);
-
-  for (lp::Sequence i = 1000; i < 2000; i++) {
+  // T+0ms: populate ack queue and start idle ack timer
+  std::unordered_set<lp::Sequence> expectedAcks;
+  for (lp::Sequence i = 1000; i < 1500; i++) {
     reliability->m_ackQueue.push(i);
+    expectedAcks.insert(i);
   }
-
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
   BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
   reliability->startIdleAckTimer();
   BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
 
-  // T+5ms
+  // T+4ms: idle ack timer has not yet expired, no IDLE packet generated
   advanceClocks(time::milliseconds(1), 4);
   BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
+  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 500);
   BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 1000);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 1999);
+  BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 1499);
   BOOST_CHECK_EQUAL(transport->sentPackets.size(), 0);
 
-  // T+6ms
+  // T+5ms: idle ack timer expires, IDLE packet generated
   advanceClocks(time::milliseconds(1), 1);
-
   BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
   BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
   BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 1);
-  lp::Packet sentPkt1(transport->sentPackets.back().packet);
 
-  BOOST_CHECK(!sentPkt1.has<lp::TxSequenceField>());
-  BOOST_CHECK_EQUAL(sentPkt1.count<lp::AckField>(), 1000);
-
-  for (lp::Sequence i = 10000; i < 11000; i++) {
-    reliability->m_ackQueue.push(i);
+  lp::Packet sentPkt(transport->sentPackets.back().packet);
+  BOOST_CHECK(!sentPkt.has<lp::TxSequenceField>());
+  for (lp::Sequence ack : sentPkt.list<lp::AckField>()) {
+    BOOST_CHECK_EQUAL(expectedAcks.erase(ack), 1);
   }
-
-  reliability->startIdleAckTimer();
-  BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 10000);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 10999);
-
-  // T+10ms
-  advanceClocks(time::milliseconds(1), 4);
-  BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(transport->sentPackets.size(), 1);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
-
-  // T+11ms
-  advanceClocks(time::milliseconds(1), 1);
-
-  BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
-  BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 2);
-  lp::Packet sentPkt2(transport->sentPackets.back().packet);
-
-  BOOST_REQUIRE_EQUAL(sentPkt2.count<lp::AckField>(), 1000);
-  BOOST_CHECK(!sentPkt2.has<lp::TxSequenceField>());
-
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
-  reliability->startIdleAckTimer();
-
-  // T+16ms
-  advanceClocks(time::milliseconds(1), 5);
-
-  BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(transport->sentPackets.size(), 2);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+  BOOST_CHECK(expectedAcks.empty());
 }
 
 BOOST_AUTO_TEST_CASE(IdleAckTimerMtu)
 {
   transport->setMtu(1500);
 
-  // T+1ms
-  advanceClocks(time::milliseconds(1), 1);
-
-  for (lp::Sequence i = 1000; i < 2000; i++) {
+  // T+0ms: populate ack queue and start idle ack timer
+  std::unordered_set<lp::Sequence> expectedAcks;
+  for (lp::Sequence i = 1000; i < 1500; i++) {
     reliability->m_ackQueue.push(i);
+    expectedAcks.insert(i);
   }
-
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
   BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
   reliability->startIdleAckTimer();
   BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
 
-  // T+5ms
+  // T+4ms: idle ack timer has not yet expired, no IDLE packet generated
   advanceClocks(time::milliseconds(1), 4);
   BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
+  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 500);
+  BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 1000);
+  BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 1499);
   BOOST_CHECK_EQUAL(transport->sentPackets.size(), 0);
 
-  // T+6ms
+  // T+5ms: idle ack timer expires, IDLE packets generated
   advanceClocks(time::milliseconds(1), 1);
-
   BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
+  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
 
-  for (lp::Sequence i = 5000; i < 6000; i++) {
-    reliability->m_ackQueue.push(i);
-  }
-
-  reliability->startIdleAckTimer();
-  BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-
-  // given Ack of size 6 and MTU of 1500, 249 Acks/IDLE packet
+  // MTU is 1500. LpPacket TL occupies 4 octets. Each Ack header is 12 octets. There are room for
+  // 124 Acks per LpPacket, and it takes 5 LpPackets to carry 500 Acks.
   BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 5);
-  for (size_t i = 0; i < 4; i++) {
+  for (size_t i = 0; i < 5; i++) {
     lp::Packet sentPkt(transport->sentPackets[i].packet);
     BOOST_CHECK(!sentPkt.has<lp::TxSequenceField>());
-    BOOST_CHECK_EQUAL(sentPkt.count<lp::AckField>(), 249);
-  }
-  lp::Packet sentPkt(transport->sentPackets[4].packet);
-  BOOST_CHECK(!sentPkt.has<lp::TxSequenceField>());
-  BOOST_CHECK_LE(sentPkt.count<lp::AckField>(), 249);
-
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 5000);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 5999);
-
-  // T+10ms
-  advanceClocks(time::milliseconds(1), 4);
-  BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(transport->sentPackets.size(), 5);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
-
-  // T+11ms
-  advanceClocks(time::milliseconds(1), 1);
-
-  BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
-
-  for (lp::Sequence i = 100000; i < 101000; i++) {
-    reliability->m_ackQueue.push(i);
+    BOOST_CHECK_EQUAL(sentPkt.count<lp::AckField>(), i == 4 ? 4 : 124);
+    for (lp::Sequence ack : sentPkt.list<lp::AckField>()) {
+      BOOST_CHECK_EQUAL(expectedAcks.erase(ack), 1);
+    }
   }
 
-  reliability->startIdleAckTimer();
-  BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-
-  // given Ack of size 6 and MTU of 1500, 249 Acks/IDLE packet
-  BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 10);
-  for (size_t i = 5; i < 9; i++) {
-    lp::Packet sentPkt(transport->sentPackets[i].packet);
-    BOOST_CHECK(!sentPkt.has<lp::TxSequenceField>());
-    BOOST_CHECK_EQUAL(sentPkt.count<lp::AckField>(), 249);
-  }
-  sentPkt.wireDecode(transport->sentPackets[9].packet);
-  BOOST_CHECK(!sentPkt.has<lp::TxSequenceField>());
-  BOOST_CHECK_LE(sentPkt.count<lp::AckField>(), 249);
-
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.front(), 100000);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.back(), 100999);
-  BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-
-  // T+15ms
-  advanceClocks(time::milliseconds(1), 4);
-  BOOST_CHECK(reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(transport->sentPackets.size(), 10);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 1000);
-
-  // T+16ms
-  advanceClocks(time::milliseconds(1), 1);
-
-  // given Ack of size 8 and MTU of 1500, approx 187 Acks/IDLE packet
-  BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
-  BOOST_REQUIRE_EQUAL(transport->sentPackets.size(), 16);
-  for (size_t i = 10; i < 15; i++) {
-    lp::Packet sentPkt(transport->sentPackets[i].packet);
-    BOOST_CHECK(!sentPkt.has<lp::TxSequenceField>());
-    BOOST_CHECK_EQUAL(sentPkt.count<lp::AckField>(), 187);
-  }
-  sentPkt.wireDecode(transport->sentPackets[15].packet);
-  BOOST_CHECK(!sentPkt.has<lp::TxSequenceField>());
-  BOOST_CHECK_LE(sentPkt.count<lp::AckField>(), 187);
-
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
-  reliability->startIdleAckTimer();
-
-  // T+21ms
-  advanceClocks(time::milliseconds(1), 5);
-
-  BOOST_CHECK(!reliability->m_isIdleAckTimerRunning);
-  BOOST_CHECK_EQUAL(transport->sentPackets.size(), 16);
-  BOOST_CHECK_EQUAL(reliability->m_ackQueue.size(), 0);
+  BOOST_CHECK(expectedAcks.empty());
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestLpReliability