Introduce Delegation and DelegationList

DelegationList will become the payload structure of ForwardingHint
and LinkContent in a later commit.

refs #4055

Change-Id: If29a39c47b779aafdd4aebb104092403f28e6653
diff --git a/src/delegation-list.hpp b/src/delegation-list.hpp
new file mode 100644
index 0000000..9f74399
--- /dev/null
+++ b/src/delegation-list.hpp
@@ -0,0 +1,239 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2017 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+#ifndef NDN_DELEGATION_LIST_HPP
+#define NDN_DELEGATION_LIST_HPP
+
+#include "delegation.hpp"
+
+namespace ndn {
+
+/** \brief represents a list of Delegations
+ *  \sa https://named-data.net/doc/ndn-tlv/link.html
+ *
+ *  Delegations are stored in an std::vector, under the assumption that there is usually only a
+ *  small number of Delegations, so that copying is acceptable when they are modified.
+ */
+class DelegationList
+{
+public:
+  class Error : public tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what);
+
+    Error(const std::string& what, const std::exception& innerException);
+  };
+
+  /** \brief construct an empty DelegationList
+   */
+  DelegationList();
+
+  /** \brief decode a DelegationList
+   *  \sa wireDecode
+   */
+  explicit
+  DelegationList(const Block& block, bool wantSort = true);
+
+  /** \brief encode into wire format
+   *  \param encoder either an EncodingBuffer or an EncodingEstimator
+   *  \param type TLV-TYPE code, either Content (for \p Link) or ForwardingHint
+   *  \throw std::invalid_argument \p type is invalid
+   *  \throw Error there is no Delegation
+   */
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder, uint32_t type = tlv::ForwardingHint) const;
+
+  /** \brief decode a DelegationList
+   *  \param block either a Content block (from \p Link) or a ForwardingHint block
+   *  \param wantSort if true, delegations are sorted
+   *  \throw Error the block cannot be parsed as a list of Delegations
+   */
+  void
+  wireDecode(const Block& block, bool wantSort = true);
+
+  bool
+  isSorted() const
+  {
+    return m_isSorted;
+  }
+
+  using const_iterator = std::vector<Delegation>::const_iterator;
+
+  const_iterator
+  begin() const
+  {
+    return m_dels.begin();
+  }
+
+  const_iterator
+  end() const
+  {
+    return m_dels.end();
+  }
+
+  size_t
+  size() const
+  {
+    return m_dels.size();
+  }
+
+  /** \brief get the i-th delegation
+   *  \pre i < size()
+   */
+  const Delegation&
+  operator[](size_t i) const
+  {
+    BOOST_ASSERT(i < size());
+    return m_dels[i];
+  }
+
+  /** \brief get the i-th delegation
+   *  \throw std::out_of_range i >= size()
+   */
+  const Delegation&
+  at(size_t i) const
+  {
+    return m_dels.at(i);
+  }
+
+public: // modifiers
+  /** \brief sort the delegation list
+   *  \post isSorted() == true
+   *  \post Delegations are sorted in increasing preference order.
+   *
+   *  A DelegationList can be constructed as sorted or unsorted. In most cases, it is recommended
+   *  to use a sorted DelegationList. An unsorted DelegationList is useful for extracting the i-th
+   *  delegation from a received ForwardingHint or Link object.
+   *
+   *  This method turns an unsorted DelegationList into a sorted DelegationList.
+   *  If access to unsorted DelegationList is not needed, it is more efficient to sort the
+   *  DelegationList in wireDecode.
+   */
+  void
+  sort();
+
+  /** \brief what to do when inserting a duplicate name
+   */
+  enum InsertConflictResolution {
+    /** \brief existing delegation(s) with the same name are replaced with the new delegation
+     */
+    INS_REPLACE,
+
+    /** \brief multiple delegations with the same name are kept in the DelegationList
+     *  \note This is NOT RECOMMENDED by Link specification.
+     */
+    INS_APPEND,
+
+    /** \brief new delegation is not inserted if an existing delegation has the same name
+     */
+    INS_SKIP
+  };
+
+  /** \brief insert Delegation
+   *  \return whether inserted
+   */
+  bool
+  insert(uint64_t preference, const Name& name,
+         InsertConflictResolution onConflict = INS_REPLACE);
+
+  /** \brief insert Delegation
+   *  \return whether inserted
+   */
+  bool
+  insert(const Delegation& del, InsertConflictResolution onConflict = INS_REPLACE)
+  {
+    return this->insert(del.preference, del.name, onConflict);
+  }
+
+  /** \brief delete Delegation(s) with specified preference and name
+   *  \return count of erased Delegation(s)
+   */
+  size_t
+  erase(uint64_t preference, const Name& name)
+  {
+    return this->eraseImpl(preference, name);
+  }
+
+  /** \brief delete Delegation(s) with matching preference and name
+   *  \return count of erased Delegation(s)
+   */
+  size_t
+  erase(const Delegation& del)
+  {
+    return this->eraseImpl(del.preference, del.name);
+  }
+
+  /** \brief erase Delegation(s) with specified name
+   *  \return count of erased Delegation(s)
+   */
+  size_t
+  erase(const Name& name)
+  {
+    return this->eraseImpl(nullopt, name);
+  }
+
+private:
+  static bool
+  isValidTlvType(uint32_t type);
+
+  void
+  insertImpl(uint64_t preference, const Name& name);
+
+  size_t
+  eraseImpl(optional<uint64_t> preference, const Name& name);
+
+private:
+  bool m_isSorted;
+
+  /** \brief delegation container; its contents are sorted when \p m_isSorted is true
+   *  \note This container is a member field rather than a base class, in order to ensure contents
+   *        are sorted when \p m_isSorted is true.
+   *  \note A vector is chosen instead of a std::set, so that the container can be unsorted when
+   *        \p m_isSorted is false. This container is expected to have less than seven items, and
+   *        therefore the overhead of moving items during insertion and deletion is small.
+   */
+  std::vector<Delegation> m_dels;
+
+  friend bool operator==(const DelegationList&, const DelegationList&);
+};
+
+/** \brief compare whether two DelegationLists are equal
+ *  \note Order matters! If two DelegationLists contain the same Delegations but at least one is
+ *        unsorted, they may compare unequal if the Delegations appear in different order.
+ */
+bool
+operator==(const DelegationList& lhs, const DelegationList& rhs);
+
+inline bool
+operator!=(const DelegationList& lhs, const DelegationList& rhs)
+{
+  return !(lhs == rhs);
+}
+
+std::ostream&
+operator<<(std::ostream& os, const DelegationList& dl);
+
+} // namespace ndn
+
+#endif // NDN_DELEGATION_LIST_HPP