lp: Packet and Fields

refs #2879

Change-Id: Ie450eb58d1176179efed8d1706d30ea4978b7b02
diff --git a/src/lp/detail/field-decl.hpp b/src/lp/detail/field-decl.hpp
new file mode 100644
index 0000000..e6fd4aa
--- /dev/null
+++ b/src/lp/detail/field-decl.hpp
@@ -0,0 +1,150 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_CXX_LP_DETAIL_FIELD_DECL_HPP
+#define NDN_CXX_LP_DETAIL_FIELD_DECL_HPP
+
+#include "../../common.hpp"
+
+#include "../field.hpp"
+#include "../sequence.hpp"
+#include "../cache-policy.hpp"
+#include "../nack.hpp"
+#include "../tlv.hpp"
+
+#include <boost/concept/requires.hpp>
+
+namespace ndn {
+namespace lp {
+namespace detail {
+
+template<typename TlvType, typename T>
+struct DecodeHelper
+{
+  static
+  BOOST_CONCEPT_REQUIRES(((WireDecodable<T>)), (T))
+  decode(const Block& wire)
+  {
+    if (wire.type() != TlvType::value) {
+      throw ndn::tlv::Error("Unexpected TLV type " + std::to_string(wire.type()));
+    }
+
+    T type;
+    type.wireDecode(wire);
+    return type;
+  }
+};
+
+template<typename TlvType>
+struct DecodeHelper<TlvType, uint64_t>
+{
+  static uint64_t
+  decode(const Block& wire)
+  {
+    if (wire.type() != TlvType::value) {
+      throw ndn::tlv::Error("Unexpected TLV type " + std::to_string(wire.type()));
+    }
+
+    return readNonNegativeInteger(wire);
+  }
+};
+
+template<typename TlvType>
+struct DecodeHelper<TlvType, std::pair<Buffer::const_iterator, Buffer::const_iterator>>
+{
+  static std::pair<Buffer::const_iterator, Buffer::const_iterator>
+  decode(const Block& wire)
+  {
+    if (wire.type() != TlvType::value) {
+      throw ndn::tlv::Error("Unexpected TLV type " + std::to_string(wire.type()));
+    }
+
+    if (wire.value_size() == 0) {
+      throw ndn::tlv::Error(std::to_string(wire.type()) + " must not be empty");
+    }
+
+    return std::make_pair(wire.value_begin(), wire.value_end());
+  }
+};
+
+template<typename encoding::Tag TAG, typename TlvType, typename T>
+struct EncodeHelper
+{
+  static
+  BOOST_CONCEPT_REQUIRES(((WireEncodable<T>)), (size_t))
+  encode(EncodingImpl<TAG>& encoder, const T& value)
+  {
+    return value.wireEncode(encoder);
+  }
+};
+
+template<typename encoding::Tag TAG, typename TlvType>
+struct EncodeHelper<TAG, TlvType, uint64_t>
+{
+  static size_t
+  encode(EncodingImpl<TAG>& encoder, const uint64_t value)
+  {
+    return prependNonNegativeIntegerBlock(encoder, TlvType::value, value);
+  }
+};
+
+template<typename encoding::Tag TAG, typename TlvType>
+struct EncodeHelper<TAG, TlvType, std::pair<Buffer::const_iterator, Buffer::const_iterator>>
+{
+  static size_t
+  encode(EncodingImpl<TAG>& encoder, const std::pair<Buffer::const_iterator, Buffer::const_iterator>& value)
+  {
+    size_t length = 0;
+    length += encoder.prependRange(value.first, value.second);
+    length += encoder.prependVarNumber(length);
+    length += encoder.prependVarNumber(TlvType::value);
+    return length;
+  }
+};
+
+template<typename LOCATION, typename VALUE, uint64_t TYPE, bool REPEATABLE = false>
+class FieldDecl
+{
+public:
+  typedef LOCATION FieldLocation;
+  typedef VALUE ValueType;
+  typedef std::integral_constant<uint64_t, TYPE> TlvType;
+  typedef std::integral_constant<bool, REPEATABLE> IsRepeatable;
+
+  static ValueType
+  decode(const Block& wire)
+  {
+    return DecodeHelper<TlvType, ValueType>::decode(wire);
+  }
+
+  template<typename encoding::Tag TAG, typename T>
+  static size_t
+  encode(EncodingImpl<TAG>& encoder, const T& value)
+  {
+    return EncodeHelper<TAG, TlvType, T>::encode(encoder, value);
+  }
+};
+
+} // namespace detail
+} // namespace lp
+} // namesapce ndn
+
+#endif // NDN_CXX_LP_DETAIL_FIELD_DECL_HPP
\ No newline at end of file
diff --git a/src/lp/detail/field-info.cpp b/src/lp/detail/field-info.cpp
new file mode 100644
index 0000000..fadd858
--- /dev/null
+++ b/src/lp/detail/field-info.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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.
+ */
+
+#include "field-info.hpp"
+
+#include <boost/mpl/for_each.hpp>
+#include <boost/bind.hpp>
+
+namespace ndn {
+namespace lp {
+namespace detail {
+
+struct ExtractFieldInfo
+{
+  typedef void result_type;
+
+  template<typename T>
+  void
+  operator()(FieldInfo* info, T)
+  {
+    if (T::TlvType::value != info->tlvType) {
+      return;
+    }
+    info->isRecognized = true;
+    info->canIgnore = false;
+    info->isRepeatable = T::IsRepeatable::value;
+    info->locationSortOrder = getLocationSortOrder<typename T::FieldLocation>();
+  }
+};
+
+FieldInfo::FieldInfo()
+  : tlvType(0)
+  , isRecognized(false)
+  , canIgnore(false)
+  , isRepeatable(false)
+  , locationSortOrder(getLocationSortOrder<field_location_tags::Header>())
+{
+}
+
+FieldInfo::FieldInfo(uint64_t tlv)
+  : tlvType(tlv)
+  , isRecognized(false)
+  , canIgnore(false)
+  , isRepeatable(false)
+  , locationSortOrder(getLocationSortOrder<field_location_tags::Header>())
+{
+  boost::mpl::for_each<FieldSet>(boost::bind(ExtractFieldInfo(), this, _1));
+  if (!isRecognized) {
+    canIgnore = tlv::HEADER3_MIN <= tlvType && tlvType <= tlv::HEADER3_MAX &&
+                (tlvType & 0x01) == 0x01;
+  }
+}
+
+} // namespace detail
+} // namespace lp
+} // namespace ndn
\ No newline at end of file
diff --git a/src/lp/detail/field-info.hpp b/src/lp/detail/field-info.hpp
new file mode 100644
index 0000000..2ee0e10
--- /dev/null
+++ b/src/lp/detail/field-info.hpp
@@ -0,0 +1,97 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_CXX_LP_DETAIL_FIELD_INFO_HPP
+#define NDN_CXX_LP_DETAIL_FIELD_INFO_HPP
+
+#include "../../common.hpp"
+
+#include "../fields.hpp"
+
+namespace ndn {
+namespace lp {
+namespace detail {
+
+class FieldInfo
+{
+public:
+  FieldInfo();
+
+  explicit
+  FieldInfo(uint64_t tlv);
+
+public:
+  /**
+   * \brief TLV-TYPE of the field; 0 if field does not exist
+   */
+  uint64_t tlvType;
+
+  /**
+   * \brief is this field known
+   */
+  bool isRecognized;
+
+  /**
+   * \brief can this unknown field be ignored
+   */
+  bool canIgnore;
+
+  /**
+   * \brief is the field repeatable
+   */
+  bool isRepeatable;
+
+  /**
+   * \brief sort order of field_location_tag
+   */
+  int locationSortOrder;
+};
+
+template<typename TAG>
+inline int
+getLocationSortOrder();
+
+template<>
+inline int
+getLocationSortOrder<field_location_tags::Header>()
+{
+  return 1;
+}
+
+template<>
+inline int
+getLocationSortOrder<field_location_tags::Fragment>()
+{
+  return 2;
+}
+
+inline bool
+compareFieldSortOrder(const FieldInfo& first, const FieldInfo& second)
+{
+  return (first.locationSortOrder < second.locationSortOrder) ||
+         (first.locationSortOrder == second.locationSortOrder && first.tlvType < second.tlvType);
+}
+
+} // namespace detail
+} // namespace lp
+} // namespace ndn
+
+#endif // NDN_CXX_LP_DETAIL_FIELD_INFO_HPP
\ No newline at end of file
diff --git a/src/lp/field.hpp b/src/lp/field.hpp
new file mode 100644
index 0000000..199fb16
--- /dev/null
+++ b/src/lp/field.hpp
@@ -0,0 +1,82 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_CXX_LP_FIELD_HPP
+#define NDN_CXX_LP_FIELD_HPP
+
+#include "../common.hpp"
+#include "../encoding/encoding-buffer.hpp"
+
+#include <boost/type_traits.hpp>
+
+namespace ndn {
+namespace lp {
+
+/**
+ * \brief indicates where a field may occur
+ */
+namespace field_location_tags {
+
+class Base
+{
+};
+
+/**
+ * \brief a header field
+ */
+class Header : public Base
+{
+};
+
+/**
+ * \brief the Fragment field
+ */
+class Fragment : public Base
+{
+};
+
+} // namespace field_location_tags
+
+/**
+ * \brief concept check for fields
+ */
+template<class X>
+struct Field
+{
+  BOOST_CONCEPT_ASSERT((boost::is_base_of<field_location_tags::Base, typename X::FieldLocation>));
+  BOOST_CONCEPT_ASSERT((boost::DefaultConstructible<typename X::ValueType>));
+  BOOST_CONCEPT_ASSERT((boost::CopyConstructible<typename X::ValueType>));
+  BOOST_CONCEPT_ASSERT((boost::is_same<typename X::TlvType::value_type, uint64_t>));
+  BOOST_CONCEPT_ASSERT((boost::is_same<typename X::IsRepeatable::value_type, bool>));
+  BOOST_CONCEPT_USAGE(Field)
+  {
+    Block wire;
+    X j;
+    typename X::ValueType decoded = j.decode(wire);
+    EncodingBuffer enc;
+    j.encode(enc, decoded);
+  }
+};
+
+} // namespace lp
+} // namespace ndn
+
+#endif // NDN_CXX_LP_FIELD_HPP
\ No newline at end of file
diff --git a/src/lp/fields.hpp b/src/lp/fields.hpp
new file mode 100644
index 0000000..ad656be
--- /dev/null
+++ b/src/lp/fields.hpp
@@ -0,0 +1,97 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_CXX_LP_FIELDS_HPP
+#define NDN_CXX_LP_FIELDS_HPP
+
+#include "../common.hpp"
+
+#include "tlv.hpp"
+#include "detail/field-decl.hpp"
+#include "field.hpp"
+
+#include <boost/mpl/set.hpp>
+
+namespace ndn {
+namespace lp {
+
+typedef detail::FieldDecl<field_location_tags::Header,
+                          Sequence,
+                          tlv::Sequence> SequenceField;
+BOOST_CONCEPT_ASSERT((Field<SequenceField>));
+
+typedef detail::FieldDecl<field_location_tags::Header,
+                          uint64_t,
+                          tlv::FragIndex> FragIndexField;
+BOOST_CONCEPT_ASSERT((Field<FragIndexField>));
+
+typedef detail::FieldDecl<field_location_tags::Header,
+                          uint64_t,
+                          tlv::FragCount> FragCountField;
+BOOST_CONCEPT_ASSERT((Field<FragCountField>));
+
+typedef detail::FieldDecl<field_location_tags::Header,
+                          NackHeader,
+                          tlv::Nack> NackField;
+BOOST_CONCEPT_ASSERT((Field<NackField>));
+
+typedef detail::FieldDecl<field_location_tags::Header,
+                          uint64_t,
+                          tlv::NextHopFaceId> NextHopFaceIdField;
+BOOST_CONCEPT_ASSERT((Field<NextHopFaceIdField>));
+
+typedef detail::FieldDecl<field_location_tags::Header,
+                          CachePolicy,
+                          tlv::CachePolicy> CachePolicyField;
+BOOST_CONCEPT_ASSERT((Field<CachePolicyField>));
+
+typedef detail::FieldDecl<field_location_tags::Header,
+                          uint64_t,
+                          tlv::IncomingFaceId> IncomingFaceIdField;
+BOOST_CONCEPT_ASSERT((Field<IncomingFaceIdField>));
+
+/**
+ * The value of the wire encoded field is the data between the provided iterators. During
+ * encoding, the data is copied from the Buffer into the wire buffer.
+ */
+typedef detail::FieldDecl<field_location_tags::Fragment,
+                          std::pair<Buffer::const_iterator, Buffer::const_iterator>,
+                          tlv::Fragment> FragmentField;
+BOOST_CONCEPT_ASSERT((Field<FragmentField>));
+
+/**
+ * \brief set of all field declarations
+ */
+typedef boost::mpl::set<
+  FragmentField,
+  SequenceField,
+  FragIndexField,
+  FragCountField,
+  NackField,
+  NextHopFaceIdField,
+  CachePolicyField,
+  IncomingFaceIdField
+  > FieldSet;
+
+} // namespace lp
+} // namespace ndn
+
+#endif // NDN_CXX_LP_FIELDS_HPP
\ No newline at end of file
diff --git a/src/lp/packet.cpp b/src/lp/packet.cpp
new file mode 100644
index 0000000..20de690
--- /dev/null
+++ b/src/lp/packet.cpp
@@ -0,0 +1,129 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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.
+ */
+
+#include "packet.hpp"
+#include "detail/field-info.hpp"
+
+#include <boost/range/adaptor/reversed.hpp>
+
+namespace ndn {
+namespace lp {
+
+Packet::Packet()
+  : m_wire(Block(tlv::LpPacket))
+{
+}
+
+Packet::Packet(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+template<encoding::Tag TAG>
+size_t
+Packet::wireEncode(EncodingImpl<TAG>& encoder) const
+{
+  if (m_wire.hasWire()) {
+    return m_wire.size();
+  }
+
+  size_t length = 0;
+
+  for (const Block& element : boost::adaptors::reverse(m_wire.elements())) {
+    length += encoder.prependBlock(element);
+  }
+
+  length += encoder.prependVarNumber(length);
+  length += encoder.prependVarNumber(tlv::LpPacket);
+
+  return length;
+}
+
+template size_t
+Packet::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& encoder) const;
+
+template size_t
+Packet::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& encoder) const;
+
+const Block&
+Packet::wireEncode() const
+{
+  if (m_wire.hasWire()) {
+    return m_wire;
+  }
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
+}
+
+void
+Packet::wireDecode(const Block& wire)
+{
+  if (wire.type() == ndn::tlv::Interest || wire.type() == ndn::tlv::Data) {
+    m_wire = Block(tlv::LpPacket);
+    add<FragmentField>(make_pair(wire.begin(), wire.end()));
+    return;
+  }
+
+  wire.parse();
+
+  bool isFirst = true;
+  detail::FieldInfo prev;
+  for (const Block& element : wire.elements()) {
+    detail::FieldInfo info(element.type());
+
+    if (!info.isRecognized && !info.canIgnore) {
+      throw Error("unknown field cannot be ignored");
+    }
+
+    if (!isFirst) {
+      if (info.tlvType == prev.tlvType && !info.isRepeatable) {
+        throw Error("non-repeatable field cannot be repeated");
+      }
+
+      else if (info.tlvType != prev.tlvType && !detail::compareFieldSortOrder(prev, info)) {
+        throw Error("fields are not in correct sort order");
+      }
+    }
+
+    isFirst = false;
+    prev = info;
+  }
+
+  m_wire = wire;
+}
+
+bool
+Packet::comparePos(const Block& first, const uint64_t second)
+{
+  detail::FieldInfo firstInfo(first.type());
+  detail::FieldInfo secondInfo(second);
+  return detail::compareFieldSortOrder(firstInfo, secondInfo);
+}
+
+} // namespace lp
+} // namespace ndn
\ No newline at end of file
diff --git a/src/lp/packet.hpp b/src/lp/packet.hpp
new file mode 100644
index 0000000..bf0cb01
--- /dev/null
+++ b/src/lp/packet.hpp
@@ -0,0 +1,224 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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_CXX_LP_PACKET_HPP
+#define NDN_CXX_LP_PACKET_HPP
+
+#include "fields.hpp"
+
+namespace ndn {
+namespace lp {
+
+class Packet
+{
+public:
+  class Error : public ndn::tlv::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : ndn::tlv::Error(what)
+    {
+    }
+  };
+
+  Packet();
+
+  explicit
+  Packet(const Block& wire);
+
+  /**
+   * \brief append packet to encoder
+   */
+  template<encoding::Tag TAG>
+  size_t
+  wireEncode(EncodingImpl<TAG>& encoder) const;
+
+  /**
+   * \brief encode packet into wire format
+   */
+  const Block&
+  wireEncode() const;
+
+  /**
+   * \brief decode packet from wire format
+   */
+  void
+  wireDecode(const Block& wire);
+
+public: // field access
+  /**
+   * \return true if FIELD occurs one or more times
+   * \detail This is equivalent to count()>0
+   */
+  template<typename FIELD>
+  bool
+  has() const
+  {
+    return count<FIELD>() > 0;
+  }
+
+  /**
+   * \return number of occurrences of FIELD
+   */
+  template<typename FIELD>
+  size_t
+  count() const
+  {
+    m_wire.parse();
+
+    return std::count_if(m_wire.elements_begin(), m_wire.elements_end(),
+                         [] (const Block& block) {
+                           return block.type() == FIELD::TlvType::value; });
+  }
+
+  /**
+   * \return value of index-th occurrence of FIELD
+   * \throw std::out_of_range if index>=count()
+   */
+  template<typename FIELD>
+  typename FIELD::ValueType
+  get(size_t index = 0) const
+  {
+    m_wire.parse();
+
+    size_t count = 0;
+    for (const Block& element : m_wire.elements()) {
+      if (element.type() != FIELD::TlvType::value) {
+        continue;
+      }
+      if (count++ == index) {
+        return FIELD::decode(element);
+      }
+    }
+
+    throw std::out_of_range("Index out of range");
+  }
+
+  /**
+   * \return values of all occurrences of FIELD
+   */
+  template<typename FIELD>
+  std::vector<typename FIELD::ValueType>
+  list() const
+  {
+    std::vector<typename FIELD::ValueType> output;
+
+    m_wire.parse();
+
+    for (const Block& element : m_wire.elements()) {
+      if (element.type() != FIELD::TlvType::value) {
+        continue;
+      }
+      output.push_back(FIELD::decode(element));
+    }
+
+    return output;
+  }
+
+  /**
+   * \brief remove all occurrences of FIELD, and add a FIELD with value
+   * \detail This equivalent to clear() followed by add(value)
+   */
+  template<typename FIELD>
+  Packet&
+  set(const typename FIELD::ValueType& value)
+  {
+    clear<FIELD>();
+    return add<FIELD>(value);
+  }
+
+  /**
+   * \brief add a FIELD with value
+   * \throw std::length_error if field already exists and is not repeatable
+   */
+  template<typename FIELD>
+  Packet&
+  add(const typename FIELD::ValueType& value)
+  {
+    if (!FIELD::IsRepeatable::value && has<FIELD>()) {
+      throw std::length_error("Field cannot be repeated");
+    }
+
+    EncodingEstimator estimator;
+    size_t estimatedSize = FIELD::encode(estimator, value);
+    EncodingBuffer buffer(estimatedSize, 0);
+    FIELD::encode(buffer, value);
+    Block block = buffer.block();
+
+    Block::element_const_iterator pos = std::lower_bound(m_wire.elements_begin(),
+                                                         m_wire.elements_end(),
+                                                         FIELD::TlvType::value,
+                                                         comparePos);
+    m_wire.insert(pos, block);
+
+    return *this;
+  }
+
+  /**
+   * \brief remove the index-th occurrence of FIELD
+   * \throw std::out_of_range if index>=count()
+   */
+  template<typename FIELD>
+  Packet&
+  remove(size_t index = 0)
+  {
+    m_wire.parse();
+
+    size_t count = 0;
+    for (Block::element_const_iterator it = m_wire.elements_begin(); it != m_wire.elements_end();
+         ++it) {
+      if (it->type() == FIELD::TlvType::value) {
+        if (count == index) {
+          m_wire.erase(it);
+          return *this;
+        }
+        count++;
+      }
+    }
+
+    throw std::out_of_range("Index out of range");
+  }
+
+  /**
+   * \brief remove all occurrences of FIELD
+   */
+  template<typename FIELD>
+  Packet&
+  clear()
+  {
+    m_wire.parse();
+    m_wire.remove(FIELD::TlvType::value);
+    return *this;
+  }
+
+private:
+  static bool
+  comparePos(const Block& first, const uint64_t second);
+
+private:
+  mutable Block m_wire;
+};
+
+} // namespace lp
+} // namespace ndn
+
+#endif // NDN_CXX_LP_PACKET_HPP
\ No newline at end of file
diff --git a/src/lp/sequence.hpp b/src/lp/sequence.hpp
new file mode 100644
index 0000000..ccfa1bc
--- /dev/null
+++ b/src/lp/sequence.hpp
@@ -0,0 +1,40 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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.
+ *
+ * @author Eric Newberry <enewberry@email.arizona.edu>
+ */
+
+#ifndef NDN_CXX_LP_SEQUENCE_HPP
+#define NDN_CXX_LP_SEQUENCE_HPP
+
+#include "../common.hpp"
+
+namespace ndn {
+namespace lp {
+
+/**
+ * \brief represents a sequence number
+ */
+typedef uint64_t Sequence;
+
+} // namespace lp
+} // namespace ndn
+
+#endif // NDN_CXX_LP_SEQUENCE_HPP
\ No newline at end of file
diff --git a/src/lp/tlv.hpp b/src/lp/tlv.hpp
index b07b793..3231660 100644
--- a/src/lp/tlv.hpp
+++ b/src/lp/tlv.hpp
@@ -17,8 +17,6 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Eric Newberry <enewberry@email.arizona.edu>
  */
 
 #ifndef NDN_CXX_LP_TLV_HPP
@@ -45,6 +43,28 @@
   IncomingFaceId = 817
 };
 
+enum {
+  /**
+   * \brief lower bound of 1-octet header field
+   */
+  HEADER1_MIN = 81,
+
+  /**
+   * \brief upper bound of 1-octet header field
+   */
+  HEADER1_MAX = 99,
+
+  /**
+   * \brief lower bound of 3-octet header field
+   */
+  HEADER3_MIN = 800,
+
+  /**
+   * \brief upper bound of 3-octet header field
+   */
+  HEADER3_MAX = 959
+};
+
 } // namespace tlv
 } // namespace lp
 } // namespace ndn
diff --git a/tests/unit-tests/lp/packet.t.cpp b/tests/unit-tests/lp/packet.t.cpp
new file mode 100644
index 0000000..f532214
--- /dev/null
+++ b/tests/unit-tests/lp/packet.t.cpp
@@ -0,0 +1,339 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2015 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.
+ */
+
+#include "lp/packet.hpp"
+
+#include "boost-test.hpp"
+
+namespace ndn {
+namespace lp {
+namespace tests {
+
+BOOST_AUTO_TEST_SUITE(LpPacket)
+
+BOOST_AUTO_TEST_CASE(FieldAccess)
+{
+  Packet packet;
+
+  BOOST_CHECK(!packet.has<FragIndexField>());
+  BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+  BOOST_CHECK_NO_THROW(packet.set<FragIndexField>(1234));
+  BOOST_CHECK(packet.has<FragIndexField>());
+  BOOST_CHECK_THROW(packet.add<FragIndexField>(5678), std::length_error);
+  BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+  BOOST_CHECK_EQUAL(1234, packet.get<FragIndexField>(0));
+  BOOST_CHECK_THROW(packet.get<FragIndexField>(1), std::out_of_range);
+  BOOST_CHECK_THROW(packet.remove<FragIndexField>(1), std::out_of_range);
+  BOOST_CHECK_NO_THROW(packet.remove<FragIndexField>(0));
+  BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+  BOOST_CHECK_NO_THROW(packet.add<FragIndexField>(832));
+  std::vector<uint64_t> fragIndexes;
+  BOOST_REQUIRE_NO_THROW(fragIndexes = packet.list<FragIndexField>());
+  BOOST_CHECK_EQUAL(1, fragIndexes.size());
+  BOOST_CHECK_EQUAL(832, fragIndexes.at(0));
+  BOOST_CHECK_NO_THROW(packet.clear<FragIndexField>());
+  BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+}
+
+/// \todo test field access methods with a REPEATABLE field
+
+BOOST_AUTO_TEST_CASE(EncodeFragment)
+{
+  static const uint8_t expectedBlock[] = {
+    0x64, 0x04, // LpPacket
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+  };
+
+  Buffer buf(2);
+  buf[0] = 0x03;
+  buf[1] = 0xe8;
+
+  Packet packet;
+  BOOST_CHECK_NO_THROW(packet.add<FragmentField>(std::make_pair(buf.begin(), buf.end())));
+  Block wire;
+  BOOST_REQUIRE_NO_THROW(wire = packet.wireEncode());
+  BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+                                wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeSubTlv)
+{
+  static const uint8_t expectedBlock[] = {
+    0x64, 0x09, // LpPacket
+          0xfd, 0x03, 0x20, 0x05, // Nack
+                0xfd, 0x03, 0x21, 0x01, // NackReason
+                      0x64,
+  };
+
+  NackHeader nack;
+  nack.setReason(NackReason::DUPLICATE);
+
+  Packet packet;
+  BOOST_CHECK_NO_THROW(packet.add<NackField>(nack));
+  Block wire;
+  BOOST_REQUIRE_NO_THROW(wire = packet.wireEncode());
+  BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+                                wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(EncodeSortOrder)
+{
+  static const uint8_t expectedBlock[] = {
+    0x64, 0x0a, // LpPacket
+          0x52, 0x01, // FragIndex
+                0x00,
+          0x53, 0x01, // FragCount
+                0x01,
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+  };
+
+  Buffer frag(2);
+  frag[0] = 0x03;
+  frag[1] = 0xe8;
+
+  Packet packet;
+  BOOST_CHECK_NO_THROW(packet.add<FragmentField>(std::make_pair(frag.begin(), frag.end())));
+  BOOST_CHECK_NO_THROW(packet.add<FragIndexField>(0));
+  BOOST_CHECK_NO_THROW(packet.add<FragCountField>(1));
+  Block wire;
+  BOOST_REQUIRE_NO_THROW(wire = packet.wireEncode());
+  BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+                                wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeNormal)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x0a, // LpPacket
+          0x52, 0x01, // FragIndex
+                0x00,
+          0x53, 0x01, // FragCount
+                0x01,
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+  BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+  BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+  BOOST_CHECK_EQUAL(1, packet.count<FragCountField>());
+  Buffer::const_iterator first, last;
+  BOOST_REQUIRE_NO_THROW(std::tie(first, last) = packet.get<FragmentField>(0));
+  BOOST_CHECK_EQUAL(2, last - first);
+  BOOST_CHECK_EQUAL(0x03, *first);
+  BOOST_CHECK_EQUAL(0xe8, *(last - 1));
+  BOOST_CHECK_EQUAL(0, packet.get<FragIndexField>(0));
+  BOOST_CHECK_EQUAL(1, packet.get<FragCountField>(0));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeIdle)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x06, // LpPacket
+          0x52, 0x01, // FragIndex
+                0x00,
+          0x53, 0x01, // FragCount
+                0x01,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+  BOOST_CHECK_EQUAL(0, packet.count<FragmentField>());
+  BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+  BOOST_CHECK_EQUAL(1, packet.count<FragCountField>());
+  BOOST_CHECK_EQUAL(0, packet.get<FragIndexField>(0));
+  BOOST_CHECK_EQUAL(1, packet.get<FragCountField>(0));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeFragment)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x04, // LpPacket
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+  BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+  BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+  Buffer::const_iterator first, last;
+  BOOST_REQUIRE_NO_THROW(std::tie(first, last) = packet.get<FragmentField>(0));
+  BOOST_CHECK_EQUAL(2, last - first);
+  BOOST_CHECK_EQUAL(0x03, *first);
+  BOOST_CHECK_EQUAL(0xe8, *(last - 1));
+}
+
+BOOST_AUTO_TEST_CASE(DecodeEmpty)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x00, // LpPacket
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+  BOOST_CHECK_EQUAL(0, packet.count<FragmentField>());
+  BOOST_CHECK_EQUAL(0, packet.count<FragIndexField>());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeRepeatedNonRepeatableHeader)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x06, // LpPacket
+          0x52, 0x01, // FragIndex
+                0x00,
+          0x52, 0x01, // FragIndex
+                0x01,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeRepeatedFragment)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x08, // LpPacket
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+          0x50, 0x02, // Fragment
+                0x03, 0xe9,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeWrongOrderAmongHeaders)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x0a, // LpPacket
+          0x53, 0x01, // FragCount
+                0x01,
+          0x52, 0x01, // FragIndex
+                0x00,
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeWrongOrderFragment)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x0a, // LpPacket
+          0x52, 0x01, // FragIndex
+                0x00,
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+          0x53, 0x01, // FragCount
+                0x01,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeIgnoredHeader)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x0c, // LpPacket
+          0x52, 0x01, // FragIndex
+                0x00,
+          0xfd, 0x03, 0x23, 0x01, // unknown TLV-TYPE 803 (ignored)
+                0x02,
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+  BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+  BOOST_CHECK_EQUAL(1, packet.count<FragIndexField>());
+}
+
+BOOST_AUTO_TEST_CASE(DecodeUnrecognizedHeader)
+{
+  static const uint8_t inputBlock[] = {
+    0x64, 0x0c, // LpPacket
+          0x52, 0x01, // FragIndex
+                0x00,
+          0xfd, 0x03, 0x22, 0x01, // unknown TLV-TYPE 802 (cannot ignore)
+                0x02,
+          0x50, 0x02, // Fragment
+                0x03, 0xe8,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_THROW(packet.wireDecode(wire), Packet::Error);
+}
+
+BOOST_AUTO_TEST_CASE(DecodeBareNetworkLayerPacket)
+{
+  static const uint8_t inputBlock[] = {
+    0x05, 0x0a, // Interest
+          0x07, 0x02, // Name
+                0x03, 0xe8,
+          0x0a, 0x04, // Nonce
+                0x01, 0x02, 0x03, 0x04,
+  };
+
+  Packet packet;
+  Block wire(inputBlock, sizeof(inputBlock));
+  BOOST_CHECK_NO_THROW(packet.wireDecode(wire));
+  BOOST_CHECK_EQUAL(1, packet.count<FragmentField>());
+
+  static const uint8_t expectedBlock[] = {
+    0x64, 0x0e, // LpPacket
+          0x50, 0x0c, // Fragment
+                0x05, 0x0a, // Interest
+                      0x07, 0x02, // Name
+                            0x03, 0xe8,
+                      0x0a, 0x04, // Nonce
+                            0x01, 0x02, 0x03, 0x04,
+  };
+
+  Block encoded;
+  BOOST_CHECK_NO_THROW(encoded = packet.wireEncode());
+  BOOST_CHECK_EQUAL_COLLECTIONS(expectedBlock, expectedBlock + sizeof(expectedBlock),
+                                encoded.begin(), encoded.end());
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace tests
+} // namespace lp
+} // namespace ndn
\ No newline at end of file