iblt: rewrite encode, decode, and operator==

refs #4825

Change-Id: I207dc56cc39a6c66bd3165745d2e00c3779eedbd
diff --git a/PSync/detail/iblt.cpp b/PSync/detail/iblt.cpp
index 0739b04..55f9a66 100644
--- a/PSync/detail/iblt.cpp
+++ b/PSync/detail/iblt.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  The University of Memphis
+ * Copyright (c) 2014-2024,  The University of Memphis
  *
  * This file is part of PSync.
  * See AUTHORS.md for complete list of PSync authors and contributors.
@@ -52,6 +52,9 @@
 
 namespace be = boost::endian;
 
+constexpr size_t ENTRY_SIZE = sizeof(HashTableEntry::count) + sizeof(HashTableEntry::keySum) +
+                              sizeof(HashTableEntry::keyCheck);
+
 bool
 HashTableEntry::isPure() const
 {
@@ -86,19 +89,21 @@
 void
 IBLT::initialize(const ndn::name::Component& ibltName)
 {
-  const auto& values = extractValueFromName(ibltName);
-
-  if (3 * m_hashTable.size() != values.size()) {
+  auto decompressed = decompress(m_compressionScheme, ibltName.value_bytes());
+  if (decompressed->size() != ENTRY_SIZE * m_hashTable.size()) {
     NDN_THROW(Error("Received IBF cannot be decoded!"));
   }
 
-  for (size_t i = 0; i < m_hashTable.size(); i++) {
-    HashTableEntry& entry = m_hashTable.at(i);
-    if (values[i * 3] != 0) {
-      entry.count = values[i * 3];
-      entry.keySum = values[(i * 3) + 1];
-      entry.keyCheck = values[(i * 3) + 2];
-    }
+  const uint8_t* input = decompressed->data();
+  for (auto& entry : m_hashTable) {
+    entry.count = be::endian_load<int32_t, sizeof(int32_t), be::order::big>(input);
+    input += sizeof(entry.count);
+
+    entry.keySum = be::endian_load<uint32_t, sizeof(uint32_t), be::order::big>(input);
+    input += sizeof(entry.keySum);
+
+    entry.keyCheck = be::endian_load<uint32_t, sizeof(uint32_t), be::order::big>(input);
+    input += sizeof(entry.keyCheck);
   }
 }
 
@@ -182,75 +187,23 @@
 void
 IBLT::appendToName(ndn::Name& name) const
 {
-  constexpr size_t unitSize = sizeof(m_hashTable[0].count) +
-                              sizeof(m_hashTable[0].keySum) +
-                              sizeof(m_hashTable[0].keyCheck);
+  std::vector<uint8_t> buffer(ENTRY_SIZE * m_hashTable.size());
+  uint8_t* output = buffer.data();
+  for (const auto& entry : m_hashTable) {
+    be::endian_store<int32_t, sizeof(int32_t), be::order::big>(output, entry.count);
+    output += sizeof(entry.count);
 
-  size_t tableSize = unitSize * m_hashTable.size();
-  std::vector<uint8_t> table(tableSize);
+    be::endian_store<uint32_t, sizeof(uint32_t), be::order::big>(output, entry.keySum);
+    output += sizeof(entry.keySum);
 
-  for (size_t i = 0; i < m_hashTable.size(); i++) {
-    uint32_t count    = be::native_to_big(static_cast<uint32_t>(m_hashTable[i].count));
-    uint32_t keySum   = be::native_to_big(static_cast<uint32_t>(m_hashTable[i].keySum));
-    uint32_t keyCheck = be::native_to_big(static_cast<uint32_t>(m_hashTable[i].keyCheck));
-
-    std::memcpy(&table[i * unitSize], &count, sizeof(count));
-    std::memcpy(&table[(i * unitSize) + 4], &keySum, sizeof(keySum));
-    std::memcpy(&table[(i * unitSize) + 8], &keyCheck, sizeof(keyCheck));
+    be::endian_store<uint32_t, sizeof(uint32_t), be::order::big>(output, entry.keyCheck);
+    output += sizeof(entry.keyCheck);
   }
 
-  auto compressed = compress(m_compressionScheme, table);
+  auto compressed = compress(m_compressionScheme, buffer);
   name.append(ndn::name::Component(std::move(compressed)));
 }
 
-std::vector<uint32_t>
-IBLT::extractValueFromName(const ndn::name::Component& ibltName) const
-{
-  auto decompressedBuf = decompress(m_compressionScheme, ibltName.value_bytes());
-
-  if (decompressedBuf->size() % 4 != 0) {
-    NDN_THROW(Error("Received IBF cannot be decompressed correctly!"));
-  }
-
-  size_t n = decompressedBuf->size() / 4;
-  std::vector<uint32_t> values(n, 0);
-
-  for (size_t i = 0; i < n; i++) {
-    uint32_t t;
-    std::memcpy(&t, &(*decompressedBuf)[i * 4], sizeof(t));
-    values[i] = be::big_to_native(t);
-  }
-
-  return values;
-}
-
-bool
-operator==(const IBLT& iblt1, const IBLT& iblt2)
-{
-  auto iblt1HashTable = iblt1.getHashTable();
-  auto iblt2HashTable = iblt2.getHashTable();
-  if (iblt1HashTable.size() != iblt2HashTable.size()) {
-    return false;
-  }
-
-  size_t N = iblt1HashTable.size();
-
-  for (size_t i = 0; i < N; i++) {
-    if (iblt1HashTable[i].count != iblt2HashTable[i].count ||
-        iblt1HashTable[i].keySum != iblt2HashTable[i].keySum ||
-        iblt1HashTable[i].keyCheck != iblt2HashTable[i].keyCheck)
-      return false;
-  }
-
-  return true;
-}
-
-bool
-operator!=(const IBLT& iblt1, const IBLT& iblt2)
-{
-  return !(iblt1 == iblt2);
-}
-
 std::ostream&
 operator<<(std::ostream& os, const IBLT& iblt)
 {
diff --git a/PSync/detail/iblt.hpp b/PSync/detail/iblt.hpp
index 1e8b35b..a7f5bf0 100644
--- a/PSync/detail/iblt.hpp
+++ b/PSync/detail/iblt.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2014-2022,  The University of Memphis
+ * Copyright (c) 2014-2024,  The University of Memphis
  *
  * This file is part of PSync.
  * See AUTHORS.md for complete list of PSync authors and contributors.
@@ -50,23 +50,37 @@
 
 #include <ndn-cxx/name.hpp>
 
+#include <boost/operators.hpp>
+
 #include <set>
 #include <string>
 
 namespace psync::detail {
 
-class HashTableEntry
+class HashTableEntry : private boost::equality_comparable<HashTableEntry>
 {
 public:
-  int32_t count;
-  uint32_t keySum;
-  uint32_t keyCheck;
-
   bool
   isPure() const;
 
   bool
   isEmpty() const;
+
+private: // non-member operators
+  // NOTE: the following "hidden friend" operators are available via
+  //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
+
+  friend bool
+  operator==(const HashTableEntry& lhs, const HashTableEntry& rhs) noexcept
+  {
+    return lhs.count == rhs.count && lhs.keySum == rhs.keySum && lhs.keyCheck == rhs.keyCheck;
+  }
+
+public:
+  int32_t count = 0;
+  uint32_t keySum = 0;
+  uint32_t keyCheck = 0;
 };
 
 inline constexpr size_t N_HASH = 3;
@@ -77,7 +91,7 @@
  *
  * Used by Partial Sync (PartialProducer) and Full Sync (Full Producer)
  */
-class IBLT
+class IBLT : private boost::equality_comparable<IBLT>
 {
 public:
   class Error : public std::runtime_error
@@ -143,22 +157,21 @@
   void
   appendToName(ndn::Name& name) const;
 
-  /**
-   * @brief Extracts IBLT from name component
-   *
-   * Converts the name into a uint8_t vector which is then decoded to a
-   * a uint32_t vector.
-   *
-   * @param ibltName IBLT represented as a Name Component
-   * @return a uint32_t vector representing the hash table of the IBLT
-   */
-  std::vector<uint32_t>
-  extractValueFromName(const ndn::name::Component& ibltName) const;
-
 private:
   void
   update(int plusOrMinus, uint32_t key);
 
+private: // non-member operators
+  // NOTE: the following "hidden friend" operators are available via
+  //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
+
+  friend bool
+  operator==(const IBLT& lhs, const IBLT& rhs)
+  {
+    return lhs.m_hashTable == rhs.m_hashTable;
+  }
+
 private:
   std::vector<HashTableEntry> m_hashTable;
   static constexpr int INSERT = 1;
@@ -166,12 +179,6 @@
   CompressionScheme m_compressionScheme;
 };
 
-bool
-operator==(const IBLT& iblt1, const IBLT& iblt2);
-
-bool
-operator!=(const IBLT& iblt1, const IBLT& iblt2);
-
 std::ostream&
 operator<<(std::ostream& os, const IBLT& iblt);