Created inital design for file-manifest structure

Change-Id: Ie578aea9764fb586101dbcb77e41cc6e319f390b
diff --git a/src/file-manifest.cpp b/src/file-manifest.cpp
new file mode 100644
index 0000000..7352699
--- /dev/null
+++ b/src/file-manifest.cpp
@@ -0,0 +1,244 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+* Copyright (c) 2016 Regents of the University of California.
+*
+* This file is part of the nTorrent codebase.
+*
+* nTorrent 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.
+*
+* nTorrent 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 nTorrent, e.g., in COPYING.md file. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+* See AUTHORS for complete list of nTorrent authors and contributors.
+*/
+#include "file-manifest.hpp"
+
+#include <boost/assert.hpp>
+#include <boost/range/adaptors.hpp>
+#include <boost/throw_exception.hpp>
+
+using std::vector;
+
+namespace ndn {
+namespace ntorrent {
+
+BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FileManifest>));
+BOOST_CONCEPT_ASSERT((WireEncodable<FileManifest>));
+BOOST_CONCEPT_ASSERT((WireDecodable<FileManifest>));
+static_assert(std::is_base_of<Data::Error, FileManifest::Error>::value,
+                "FileManifest::Error should inherit from Data::Error");
+
+void
+FileManifest::wireDecode(const Block& wire)
+{
+  Data::wireDecode(wire);
+  this->decodeContent();
+}
+
+const Block&
+FileManifest::wireEncode()
+{
+  encodeContent();
+  return Data::wireEncode();
+}
+
+template<ndn::encoding::Tag TAG>
+size_t
+FileManifest::encodeContent(ndn::EncodingImpl<TAG>& encoder) const {
+  // FileManifestName ::= NAME-TYPE TLV-LENGTH
+  //                  Name
+
+  // ManifestContent ::= CONTENT-TYPE TLV-LENGTH
+  //                 DataPacketName*
+  //                 CatalogPrefix
+  //                 DataPacketSize
+  //                 FileManifestPtr?
+
+  // DataPacketName ::= NAME-TYPE TLV-LENGTH
+  //                Name
+
+  // CatalogPrefix ::= NAME-TYPE TLV-LENGTH
+  //               Name
+
+  // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
+  //                nonNegativeInteger
+
+  // FileManifestPtr ::= NAME-TYPE TLV-LENGTH
+  //                 Name
+
+  size_t totalLength = 0;
+
+  vector<Name> suffixCatalog;
+  suffixCatalog.reserve(m_catalog.size());
+  for (auto name: m_catalog) {
+    if (!m_catalogPrefix.isPrefixOf(name)) {
+      BOOST_THROW_EXCEPTION(Error(name.toUri() + "does not have the prefix"
+                                               + m_catalogPrefix.toUri()));
+    }
+    name = name.getSubName(m_catalogPrefix.size());
+    if (name.empty()) {
+      BOOST_THROW_EXCEPTION(Error("Manifest cannot include empty string"));
+    }
+    suffixCatalog.push_back(name);
+  }
+
+  for (const auto& name : suffixCatalog |  boost::adaptors::reversed) {
+    totalLength += name.wireEncode(encoder);
+  }
+
+  totalLength += m_catalogPrefix.wireEncode(encoder);
+
+  totalLength += prependNonNegativeIntegerBlock(encoder, tlv::Content, m_dataPacketSize);
+
+  if (nullptr != m_submanifestPtr) {
+    totalLength += m_submanifestPtr->wireEncode(encoder);
+  }
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  totalLength += encoder.prependVarNumber(tlv::Content);
+  return totalLength;
+
+}
+
+// MANIPULATORS
+void
+FileManifest::push_back(const Name& name)
+{
+  BOOST_ASSERT(name != m_catalogPrefix);
+  BOOST_ASSERT(m_catalogPrefix.isPrefixOf(name));
+  m_catalog.push_back(name);
+}
+
+bool
+FileManifest::remove(const ndn::Name& name) {
+  auto it = std::find(m_catalog.begin(), m_catalog.end(), name);
+  if (m_catalog.end() == it) {
+    return false;
+  }
+  m_catalog.erase(it);
+  return true;
+}
+
+void FileManifest::encodeContent() {
+  // Name
+  //     <file_name>/ImplicitDigest
+  // Content
+  //     MetaData
+  //     DataPtr*
+  //     ManifestPointer?
+  //
+  // DataPtr := HashValue
+  // ManifestPtr := HashValue
+  // HashValue   := OCTET[32]
+
+  // MetaData    := Property*
+  // Property    := DataSize | Signature
+  onChanged();
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = encodeContent(estimator);
+
+  EncodingBuffer buffer(estimatedSize, 0);
+  encodeContent(buffer);
+
+  setContentType(tlv::ContentType_Blob);
+  setContent(buffer.block());
+}
+
+void
+FileManifest::decodeContent() {
+  // ManifestContent ::= CONTENT-TYPE TLV-LENGTH
+  //                 DataPacketName*
+  //                 CatalogPrefix
+  //                 DataPacketSize
+  //                 FileManifestPtr?
+
+
+  // DataPacketName ::= NAME-TYPE TLV-LENGTH
+  //                Name
+
+  // CatalogPrefix ::= NAME-TYPE TLV-LENGTH
+  //               Name
+
+  // DataPacketSize ::= CONTENT-TYPE TLV-LENGTH
+  //                nonNegativeInteger
+
+  // FileManifestPtr ::= NAME-TYPE TLV-LENGTH
+  //                 Name
+
+  if (getContentType() != tlv::ContentType_Blob) {
+    BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob"));
+  }
+
+  const Block& content = Data::getContent();
+  content.parse();
+
+  auto element = content.elements_begin();
+
+  if (content.elements_end() == element) {
+    BOOST_THROW_EXCEPTION(Error("FileManifest with empty content"));
+  }
+
+  element->parse();
+  Name name(*element);
+
+  // Submanifest ptr
+  if (!name.empty()) {
+    m_submanifestPtr = std::make_shared<Name>(name);
+    ++element;
+  }
+
+  // DataPacketSize
+  m_dataPacketSize = readNonNegativeInteger(*element);
+  ++element;
+
+  // CatalogPrefix
+  m_catalogPrefix = Name(*element);
+  element++;
+
+  // Catalog
+  m_catalog.clear();
+  for (; element != content.elements_end(); ++element) {
+    element->parse();
+    name = m_catalogPrefix;
+    name.append(Name(*element));
+    if (name == m_catalogPrefix) {
+      BOOST_THROW_EXCEPTION(Error("Empty name included in a FileManifest"));
+    }
+    push_back(name);
+  }
+}
+
+bool operator==(const FileManifest& lhs, const FileManifest& rhs) {
+  return lhs.name()             == rhs.name()
+      && lhs.data_packet_size() == rhs.data_packet_size()
+      && (lhs.submanifest_ptr() == rhs.submanifest_ptr() /* shallow  equality */
+         || ( nullptr != lhs.submanifest_ptr()
+           && nullptr != rhs.submanifest_ptr()
+           && *rhs.submanifest_ptr() == *lhs.submanifest_ptr()
+         )
+      )
+      && lhs.catalog()          == rhs.catalog();
+}
+
+bool operator!=(const FileManifest& lhs, const FileManifest& rhs) {
+  return lhs.name()             != rhs.name()
+      || lhs.data_packet_size() != rhs.data_packet_size()
+      || (lhs.submanifest_ptr() != rhs.submanifest_ptr() /* shallow  equality */
+         && (nullptr == lhs.submanifest_ptr()
+         || nullptr == rhs.submanifest_ptr()
+         || *rhs.submanifest_ptr() != *lhs.submanifest_ptr()
+        )
+      )
+      || lhs.catalog()          != rhs.catalog();
+}
+
+}  // end ntorrent
+}  // end ndn
\ No newline at end of file
diff --git a/src/file-manifest.hpp b/src/file-manifest.hpp
new file mode 100644
index 0000000..0a46574
--- /dev/null
+++ b/src/file-manifest.hpp
@@ -0,0 +1,244 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+* Copyright (c) 2016 Regents of the University of California.
+*
+* This file is part of the nTorrent codebase.
+*
+* nTorrent 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.
+*
+* nTorrent 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 nTorrent, e.g., in COPYING.md file. If not, see
+* <http://www.gnu.org/licenses/>.
+*
+* See AUTHORS for complete list of nTorrent authors and contributors.
+*/
+#ifndef INCLUDED_FILE_MANIFEST_HPP
+#define INCLUDED_FILE_MANIFEST_HPP
+
+#include <cstring>
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <ndn-cxx/data.hpp>
+#include <ndn-cxx/name.hpp>
+
+namespace ndn {
+namespace ntorrent {
+
+class FileManifest : public Data {
+/**
+* \class FileManifest
+*
+* \brief A value semantic type for File manifests
+*
+*/
+ public:
+  // TYPES
+  class Error : public Data::Error
+  {
+  public:
+    explicit
+    Error(const std::string& what)
+      : Data::Error(what)
+    {
+    }
+  };
+
+ public:
+  // CREATORS
+  FileManifest() = delete;
+
+  ~FileManifest() = default;
+  /// Destroy this object
+
+  FileManifest(const Name&              name,
+               size_t                   dataPacketSize,
+               const Name&              commonPrefix,
+               const std::vector<Name>& catalog = std::vector<Name>(),
+               std::shared_ptr<Name>    subManifestPtr = nullptr);
+  /**
+  * \brief Creates a new FileManifest with the specified values
+  *
+  * @param name The Name of this FileManifest
+  * @param dataPacketSize The size (except the last) of each data packet in this FileManifest
+  * @param commonPrefix The common prefix used for all named in the catalog
+  * @param catalog The collection of Names for this FileManifest
+  * @param subManifestPtr (optional) The Name for the sub-manifest in this for this file
+  */
+
+  FileManifest(const Name&           name,
+               size_t                dataPacketSize,
+               const Name&           catalogPrefix,
+               std::vector<Name>&&   catalog,
+               std::shared_ptr<Name> subManifestPtr = nullptr);
+  /**
+  * \brief Creates a new FileManifest with the specified values
+  *
+  * @param name The Name of this FileManifest
+  * @param dataPacketSize The size (except the last) of each data packet in this FileManifest
+  * @param catalogPrefix the common prefix used for all named in the catalog
+  * @param catalog The collection of Names for this FileManifest
+  * @param subManifestPtr (optional) The Name for the sub-manifest in this for this file
+  */
+
+  explicit
+  FileManifest(const Block& block);
+
+  FileManifest(const FileManifest& other) = default;
+  /// Creates a new FileManifest with same  value as the specified 'other'
+
+  // ACCESSORS
+  const Name&
+  name() const;
+  /// Returns the 'name' of this FileManifest
+
+  const Name&
+  catalog_prefix() const;
+  /// Returns the 'catalogPrefix' of this FileManfiest.
+
+  size_t
+  data_packet_size() const;
+  /// Returns the 'data_packet_size' of this FileManifest
+
+  std::shared_ptr<Name>
+  submanifest_ptr() const;
+  /// Returns the 'submanifest_ptr' of this FileManifest, or 'nullptr' is none exists
+
+  const std::vector<Name>&
+  catalog() const;
+  /// Returns an unmodifiable reference to the 'catalog' of this FileManifest
+
+ private:
+  template<encoding::Tag TAG>
+  size_t
+  encodeContent(EncodingImpl<TAG>& encoder) const;
+  /// Encodes the contents of this object and append the contents to the 'encoder'
+
+ public:
+  // MANIPULATORS
+  FileManifest&
+  operator=(const FileManifest& rhs) = default;
+  /// Assigns the value of the specific 'rhs' object to this object.
+
+  void
+  push_back(const Name& name);
+  /// Appends a Name to the catalog
+
+  bool
+  remove(const Name& name);
+  /// If 'name' in catalog, removes first instance and returns 'true', otherwise returns 'false'.
+
+  void
+  wireDecode(const Block& wire);
+  /**
+  * \brief Decodes the wire and assign its contents to this FileManifest
+  *
+  * @param wire the write to be decoded
+  * @throws Error if decoding fails
+  */
+  const Block&
+  wireEncode();
+  /// Encodes this FileManifest into the wire format and returns a Block with the contents
+
+private:
+  void
+  decodeContent();
+  /// Decodes the contents of this Data packet, assigning its contents to this FileManifest.
+
+  void
+  encodeContent();
+  /// Encodes the contents of this FileManifest into the content section of its Data packet.
+
+// DATA
+ private:
+  size_t                 m_dataPacketSize;
+  Name                   m_catalogPrefix;
+  std::vector<Name>      m_catalog;
+  std::shared_ptr<Name>  m_submanifestPtr;
+};
+
+/// Non-member functions
+bool operator==(const FileManifest& lhs, const FileManifest& rhs);
+/// Returns 'true' if 'lhs' and 'rhs' have the same value, 'false' otherwise.
+
+bool operator!=(const FileManifest& lhs, const FileManifest& rhs);
+/// Returns 'true' if 'lhs' and 'rhs' have different values, and 'false' otherwise.
+
+inline
+FileManifest::FileManifest(
+               const Name&              name,
+               size_t                   dataPacketSize,
+               const Name&              catalogPrefix,
+               const std::vector<Name>& catalog,
+               std::shared_ptr<Name>    subManifestPtr)
+: Data(name)
+, m_dataPacketSize(dataPacketSize)
+, m_catalogPrefix(catalogPrefix)
+, m_catalog(catalog)
+, m_submanifestPtr(subManifestPtr)
+{
+}
+
+inline
+FileManifest::FileManifest(
+               const Name&           name,
+               size_t                dataPacketSize,
+               const Name&           catalogPrefix,
+               std::vector<Name>&&   catalog,
+               std::shared_ptr<Name> subManifestPtr)
+: Data(name)
+, m_dataPacketSize(dataPacketSize)
+, m_catalogPrefix(catalogPrefix)
+, m_catalog(catalog)
+, m_submanifestPtr(subManifestPtr)
+{
+}
+
+inline
+FileManifest::FileManifest(const Block& block)
+{
+  wireDecode(block);
+}
+
+inline const Name&
+FileManifest::name() const
+{
+  return getName();
+}
+
+inline size_t
+FileManifest::data_packet_size() const
+{
+  return m_dataPacketSize;
+}
+
+inline const Name&
+FileManifest::catalog_prefix() const
+{
+  return m_catalogPrefix;
+}
+
+inline const std::vector<Name>&
+FileManifest::catalog() const
+{
+  return m_catalog;
+}
+
+inline std::shared_ptr<Name>
+FileManifest::submanifest_ptr() const
+{
+  return m_submanifestPtr;
+}
+
+}  // end ntorrent
+}  // end ndn
+
+#endif // INCLUDED_FILE_MANIFEST_HPP