encoding: Optimized Data packet encoding, preparation for memory-efficient signing operation

Change-Id: I6eb1f8acef917970790d1f228ade6212c45181fa
refs: #1172
diff --git a/src/common.cpp b/src/common.cpp
deleted file mode 100644
index 7665dc8..0000000
--- a/src/common.cpp
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-/**
- * Copyright (C) 2013 Regents of the University of California.
- * @author: Jeff Thompson <jefft0@remap.ucla.edu>
- * See COPYING for copyright and distribution information.
- */
-
-#include "common.hpp"
-
-#include "util/time.hpp"
-
-using namespace std;
-
-namespace ndn {
-
-string 
-toHex(const vector<uint8_t>& array) 
-{
-  if (!&array)
-    return "";
-  
-  ostringstream result;
-  result.flags(ios::hex | ios::uppercase);
-  for (size_t i = 0; i < array.size(); ++i) {
-    uint8_t x = array[i];
-    if (x < 16)
-      result << '0';
-    result << (unsigned int)x;
-  }
-
-  return result.str();
-}
-
-MillisecondsSince1970
-getNow()
-{
-  return getNowMilliseconds();
-}
-
-
-}
-
diff --git a/src/common.hpp b/src/common.hpp
index 8d26a14..281da0b 100644
--- a/src/common.hpp
+++ b/src/common.hpp
@@ -98,27 +98,8 @@
 
 using boost::noncopyable;
 
-/**
- * A time interval represented as the number of milliseconds.
- */
-typedef int64_t Milliseconds;
-   
-/**
- * The calendar time represented as the number of milliseconds since 1/1/1970.
- */
-typedef int64_t MillisecondsSince1970;
-
-/**
- * Return the hex representation of the bytes in array.
- * @param array The array of bytes.
- * @return Hex string.
- */
-std::string 
-toHex(const std::vector<uint8_t>& array);
-
-MillisecondsSince1970
-getNow();
-
 }
 
+#include "util/time.hpp"
+
 #endif
diff --git a/src/data.cpp b/src/data.cpp
deleted file mode 100644
index 00a915c..0000000
--- a/src/data.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
-/**
- * Copyright (C) 2013 Regents of the University of California.
- * @author: Jeff Thompson <jefft0@remap.ucla.edu>
- * See COPYING for copyright and distribution information.
- */
-
-#include "common.hpp"
-
-#include "data.hpp"
-
-using namespace std;
-
-namespace ndn {
-
-const Block& 
-Data::wireEncode() const
-{
-  if (wire_.hasWire())
-    return wire_;
-
-  // Data ::= DATA-TLV TLV-LENGTH
-  //            Name
-  //            MetaInfo
-  //            Content
-  //            Signature
-  
-  wire_ = Block(Tlv::Data);
-
-  // Name
-  wire_.push_back(getName().wireEncode());
-
-  // MetaInfo
-  wire_.push_back(getMetaInfo().wireEncode());
-
-  // Content
-  wire_.push_back(getContent());
-
-  if (!signature_) {
-    throw Error("Requested wire format, but data packet has not been signed yet");
-  }
-
-  ///////////////
-  // Signature //
-  ///////////////
-  
-  // SignatureInfo
-  wire_.push_back(signature_.getInfo());
-  
-  // SignatureValue
-  wire_.push_back(signature_.getValue());
-  
-  wire_.encode();
-  return wire_;  
-}
-  
-/**
- * Decode the input using a particular wire format and update this Data. 
- * @param input The input byte array to be decoded.
- */
-void
-Data::wireDecode(const Block &wire)
-{
-  wire_ = wire;
-  wire_.parse();
-
-  // Data ::= DATA-TLV TLV-LENGTH
-  //            Name
-  //            MetaInfo
-  //            Content
-  //            Signature
-    
-  // Name
-  name_.wireDecode(wire_.get(Tlv::Name));
-
-  // MetaInfo
-  metaInfo_.wireDecode(wire_.get(Tlv::MetaInfo));
-
-  // Content
-  content_ = wire_.get(Tlv::Content);
-
-  ///////////////
-  // Signature //
-  ///////////////
-  
-  // SignatureInfo
-  signature_.setInfo(wire_.get(Tlv::SignatureInfo));
-  
-  // SignatureValue
-  signature_.setValue(wire_.get(Tlv::SignatureValue));
-}
-
-std::ostream&
-operator << (std::ostream &os, const Data &data)
-{
-  os << "Name: " << data.getName() << "\n";
-  os << "MetaInfo: " << data.getMetaInfo() << "\n";
-  os << "Content: (size: " << data.getContent().value_size() << ")\n";
-  os << "Signature: (type: " << data.getSignature().getType() <<
-    ", value_length: "<< data.getSignature().getValue().value_size() << ")";
-  os << std::endl;
-
-  return os;
-}
-
-
-}
diff --git a/src/data.hpp b/src/data.hpp
index f1b64a3..4c09811 100644
--- a/src/data.hpp
+++ b/src/data.hpp
@@ -37,25 +37,32 @@
   Data(const Name& name);
   
   /**
-   * @brief The virtual destructor.
+   * @brief The destructor
    */
-  inline virtual
+  inline
   ~Data();
   
   /**
-   * @brief Encode this Data for a wire format.
-   * @return The encoded byte array.
+   * @brief Fast encoding or block size estimation
    */
-  const Block& 
-  wireEncode() const;
+  template<bool T>
+  inline size_t
+  wireEncode(EncodingImpl<T> &block, bool unsignedPortion = false) const;
   
   /**
-   * @brief Decode the input using a particular wire format and update this Data. 
-   * @param input The input byte array to be decoded.
+   * @brief Encode to a wire format
    */
-  void
+  inline const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode from the wire format
+   */
+  inline void 
   wireDecode(const Block &wire);
 
+  ////////////////////////////////////////////////////////////////////  
+  
   inline const Name& 
   getName() const;
   
@@ -68,6 +75,8 @@
   inline void
   setName(const Name& name);
 
+  //
+  
   inline const MetaInfo& 
   getMetaInfo() const;
   
@@ -79,13 +88,20 @@
   inline void
   setMetaInfo(const MetaInfo& metaInfo);
 
+  //
+  
+  ///////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////
   ///////////////////////////////////////////////////////////////
   // MetaInfo proxy methods
+
   inline uint32_t 
   getContentType() const;
   
   inline void 
   setContentType(uint32_t type);
+
+  //
   
   inline Milliseconds 
   getFreshnessPeriod() const;
@@ -93,11 +109,18 @@
   inline void 
   setFreshnessPeriod(Milliseconds freshnessPeriod);
 
+  //
+
   inline const name::Component&
   getFinalBlockId() const;
 
   inline void
   setFinalBlockId(const name::Component& finalBlockId);
+
+  //
+  ///////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////
   
   /**
    * @brief Get content Block
@@ -121,6 +144,8 @@
 
   inline void
   setContent(const ConstBufferPtr &contentValue);
+
+  //
   
   inline const Signature&
   getSignature() const;
@@ -131,10 +156,12 @@
    */
   inline void
   setSignature(const Signature& signature);
-
+  
   inline void
   setSignatureValue(const Block &value);
 
+  ///////////////////////////////////////////////////////////////
+  
   inline uint64_t
   getIncomingFaceId() const;
 
@@ -149,25 +176,25 @@
   onChanged();
 
 private:
-  Name name_;
-  MetaInfo metaInfo_;
-  mutable Block content_;
-  Signature signature_;
+  Name m_name;
+  MetaInfo m_metaInfo;
+  mutable Block m_content;
+  Signature m_signature;
 
-  mutable Block wire_;
+  mutable Block m_wire;
 
   uint64_t m_incomingFaceId;
 };
 
 inline
 Data::Data()
-  : content_(Tlv::Content) // empty content
+  : m_content(Tlv::Content) // empty content
 {
 }
 
 inline
 Data::Data(const Name& name)
-  : name_(name)
+  : m_name(name)
 {
 }
 
@@ -176,80 +203,179 @@
 {
 }
 
+template<bool T>
+inline size_t
+Data::wireEncode(EncodingImpl<T> &block, bool unsignedPortion/* = false*/) const
+{
+  size_t total_len = 0;
+
+  // Data ::= DATA-TLV TLV-LENGTH
+  //            Name
+  //            MetaInfo
+  //            Content
+  //            Signature
+
+  // (reverse encoding)
+
+  if (!unsignedPortion && !m_signature)
+    {
+      throw Error("Requested wire format, but data packet has not been signed yet");
+    }
+
+  if (!unsignedPortion)
+    {
+      // SignatureValue
+      total_len += prependBlock(block, m_signature.getValue());
+    }
+
+  // SignatureInfo
+  total_len += prependBlock(block, m_signature.getInfo());
+  
+  // Content
+  total_len += prependBlock(block, getContent());
+  
+  // MetaInfo
+  total_len += getMetaInfo().wireEncode(block);
+
+  // Name
+  total_len += getName().wireEncode(block);
+
+  if (!unsignedPortion)
+    {
+      total_len += block.prependVarNumber (total_len);
+      total_len += block.prependVarNumber (Tlv::Data);
+    }
+  return total_len;
+}
+
+inline const Block &
+Data::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+  
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  const_cast<Data*>(this)->wireDecode(buffer.block());
+  return m_wire;
+}
+  
+/**
+ * Decode the input using a particular wire format and update this Data. 
+ * @param input The input byte array to be decoded.
+ */
+void
+Data::wireDecode(const Block &wire)
+{
+  m_wire = wire;
+  m_wire.parse();
+
+  // Data ::= DATA-TLV TLV-LENGTH
+  //            Name
+  //            MetaInfo
+  //            Content
+  //            Signature
+    
+  // Name
+  m_name.wireDecode(m_wire.get(Tlv::Name));
+
+  // MetaInfo
+  m_metaInfo.wireDecode(m_wire.get(Tlv::MetaInfo));
+
+  // Content
+  m_content = m_wire.get(Tlv::Content);
+
+  ///////////////
+  // Signature //
+  ///////////////
+  
+  // SignatureInfo
+  m_signature.setInfo(m_wire.get(Tlv::SignatureInfo));
+  
+  // SignatureValue
+  Block::element_const_iterator val = m_wire.find(Tlv::SignatureValue);
+  if (val != m_wire.elements_end())
+    m_signature.setValue(*val);
+}
+
 inline const Name& 
 Data::getName() const
 {
-  return name_;
+  return m_name;
 }
   
 inline void
 Data::setName(const Name& name)
 { 
   onChanged();
-  name_ = name; 
+  m_name = name; 
 }
   
 inline const MetaInfo& 
 Data::getMetaInfo() const
 {
-  return metaInfo_;
+  return m_metaInfo;
 }
   
 inline void
 Data::setMetaInfo(const MetaInfo& metaInfo) 
 { 
   onChanged();
-  metaInfo_ = metaInfo; 
+  m_metaInfo = metaInfo; 
 }
 
 inline uint32_t 
 Data::getContentType() const
 {
-  return metaInfo_.getType();
+  return m_metaInfo.getType();
 }
   
 inline void 
 Data::setContentType(uint32_t type)
 {
   onChanged();
-  metaInfo_.setType(type);
+  m_metaInfo.setType(type);
 }
   
 inline Milliseconds 
 Data::getFreshnessPeriod() const
 {
-  return metaInfo_.getFreshnessPeriod();
+  return m_metaInfo.getFreshnessPeriod();
 }
   
 inline void 
 Data::setFreshnessPeriod(Milliseconds freshnessPeriod)
 {
   onChanged();
-  metaInfo_.setFreshnessPeriod(freshnessPeriod);
+  m_metaInfo.setFreshnessPeriod(freshnessPeriod);
 }
 
 inline const name::Component&
 Data::getFinalBlockId() const
 {
-  return metaInfo_.getFinalBlockId();
+  return m_metaInfo.getFinalBlockId();
 }
 
 inline void
 Data::setFinalBlockId(const name::Component& finalBlockId)
 {
   onChanged();
-  metaInfo_.setFinalBlockId(finalBlockId);
+  m_metaInfo.setFinalBlockId(finalBlockId);
 }
 
 inline const Block& 
 Data::getContent() const
 {
-  if (content_.empty())
-    content_ = dataBlock(Tlv::Content, reinterpret_cast<const uint8_t*>(0), 0);
+  if (m_content.empty())
+    m_content = dataBlock(Tlv::Content, reinterpret_cast<const uint8_t*>(0), 0);
 
-  if (!content_.hasWire())
-      content_.encode();
-  return content_;
+  if (!m_content.hasWire())
+      m_content.encode();
+  return m_content;
 }
 
 inline void
@@ -257,7 +383,7 @@
 {
   onChanged();
 
-  content_ = dataBlock(Tlv::Content, content, contentLength);
+  m_content = dataBlock(Tlv::Content, content, contentLength);
 }
 
 inline void
@@ -265,7 +391,7 @@
 {
   onChanged();
 
-  content_ = Block(Tlv::Content, contentValue); // not real a wire encoding yet
+  m_content = Block(Tlv::Content, contentValue); // not real a wire encoding yet
 }
   
 inline void
@@ -274,30 +400,30 @@
   onChanged();
 
   if (content.type() == Tlv::Content)
-    content_ = content;
+    m_content = content;
   else {
-    content_ = Block(Tlv::Content, content);
+    m_content = Block(Tlv::Content, content);
   }
 }
 
 inline const Signature&
 Data::getSignature() const
 {
-  return signature_;
+  return m_signature;
 }
   
 inline void
 Data::setSignature(const Signature& signature) 
 {
   onChanged();
-  signature_ = signature;
+  m_signature = signature;
 }
 
 inline void
 Data::setSignatureValue(const Block &value)
 {
   onChanged();
-  signature_.setValue(value);
+  m_signature.setValue(value);
 }
 
 inline uint64_t
@@ -320,11 +446,21 @@
   // !!!Note!!! Signature is not invalidated and it is responsibility of
   // the application to do proper re-signing if necessary
   
-  wire_.reset();
+  m_wire.reset();
 }
 
-std::ostream&
-operator << (std::ostream &os, const Data &data);
+inline std::ostream&
+operator << (std::ostream &os, const Data &data)
+{
+  os << "Name: " << data.getName() << "\n";
+  os << "MetaInfo: " << data.getMetaInfo() << "\n";
+  os << "Content: (size: " << data.getContent().value_size() << ")\n";
+  os << "Signature: (type: " << data.getSignature().getType() <<
+    ", value_length: "<< data.getSignature().getValue().value_size() << ")";
+  os << std::endl;
+
+  return os;
+}
 
 } // namespace ndn
 
diff --git a/src/encoding/encoding-buffer.hpp b/src/encoding/encoding-buffer.hpp
index 7385c65..c9abd31 100644
--- a/src/encoding/encoding-buffer.hpp
+++ b/src/encoding/encoding-buffer.hpp
@@ -523,7 +523,7 @@
 
 template<bool P, class U>
 inline size_t
-prependNestedBlock(EncodingImpl<P>& blk, uint32_t type, U& nestedBlock)
+prependNestedBlock(EncodingImpl<P>& blk, uint32_t type, const U& nestedBlock)
 {
   size_t var_len = nestedBlock.wireEncode(blk);
   size_t total_len = var_len;
@@ -533,6 +533,13 @@
   return total_len;
 }
 
+template<bool P>
+inline size_t
+prependBlock(EncodingImpl<P>& blk, const Block& block)
+{
+  return blk.prependByteArray(block.wire(), block.size());
+}
+
 
 } // ndn
 
diff --git a/src/key-locator.hpp b/src/key-locator.hpp
index 54c9725..65a966b 100644
--- a/src/key-locator.hpp
+++ b/src/key-locator.hpp
@@ -26,27 +26,35 @@
 
   inline
   KeyLocator()
-    : type_(KeyLocator_None)
+    : m_type(KeyLocator_None)
   {
   }
 
   inline
   KeyLocator(const Name &name);
 
-  inline const Block& 
-  wireEncode() const;
+  ///////////////////////////////////////////////////////////////////////////////
+  
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T> &block) const;
 
-  inline void 
-  wireDecode(const Block &value);
+  const Block& 
+  wireEncode() const;
+  
+  void
+  wireDecode(const Block &wire);  
+  
+  ///////////////////////////////////////////////////////////////////////////////
   
   inline bool
   empty() const
   {
-    return type_ == KeyLocator_None;
+    return m_type == KeyLocator_None;
   }
   
   uint32_t 
-  getType() const { return type_; }
+  getType() const { return m_type; }
       
   ////////////////////////////////////////////////////////
   // Helper methods for different types of key locators
@@ -60,10 +68,10 @@
   setName(const Name &name);
   
 private:
-  uint32_t type_;
-  Name name_;
+  uint32_t m_type;
+  Name m_name;
   
-  mutable Block wire_;
+  mutable Block m_wire;
 };
 
 inline
@@ -72,61 +80,85 @@
   setName(name);
 }
 
-inline const Block& 
-KeyLocator::wireEncode() const
+template<bool T>
+inline size_t
+KeyLocator::wireEncode(EncodingImpl<T>& block) const
 {
-  if (wire_.hasWire())
-    return wire_;
+  // KeyLocator ::= KEY-LOCATOR-TYPE TLV-LENGTH KeyLocatorValue
 
-  // KeyLocator
+  // KeyLocatorValue ::= Name |
+  //                     KeyLocatorDigest |     (not supported yet)
+  //                     ...
 
-  switch (type_) {
+  // KeyLocatorDigest ::= KEY-LOCATOR-DIGEST-TYPE TLV-LENGTH BYTE+
+  
+  size_t total_len = 0;
+
+  switch (m_type) {
   case KeyLocator_None:
-    wire_ = dataBlock(Tlv::KeyLocator, reinterpret_cast<const uint8_t*>(0), 0);
     break;
   case KeyLocator_Name:
-    wire_ = Block(Tlv::KeyLocator);
-    wire_.push_back(name_.wireEncode());
-    wire_.encode();
+    total_len += m_name.wireEncode(block);
     break;
   default:
     throw Error("Unsupported KeyLocator type");
   }
+
+  total_len += block.prependVarNumber (total_len);
+  total_len += block.prependVarNumber (Tlv::KeyLocator);
+  return total_len;
+}
+
+inline const Block& 
+KeyLocator::wireEncode() const
+{
+  if (m_wire.hasWire ())
+    return m_wire;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
   
-  return wire_;
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  m_wire = buffer.block();
+  return m_wire;
 }
 
 inline void 
 KeyLocator::wireDecode(const Block &value)
 {
-  wire_ = value;
-  wire_.parse();
+  if (value.type() != Tlv::KeyLocator)
+    throw Error("Unexpected TLV type during KeyLocator decoding");
+
+  m_wire = value;
+  m_wire.parse();
   
-  if (!wire_.elements().empty() && wire_.elements_begin()->type() == Tlv::Name)
+  if (!m_wire.elements().empty() && m_wire.elements_begin()->type() == Tlv::Name)
     {
-      type_ = KeyLocator_Name;
-      name_.wireDecode(*wire_.elements_begin());
+      m_type = KeyLocator_Name;
+      m_name.wireDecode(*m_wire.elements_begin());
     }
   else
     {
-      type_ = KeyLocator_Unknown;
+      m_type = KeyLocator_Unknown;
     }
 }
 
 inline const Name&
 KeyLocator::getName() const
 {
-  if (type_ != KeyLocator_Name)
+  if (m_type != KeyLocator_Name)
     throw Error("Requested Name, but KeyLocator is not of the Name type");
 
-  return name_;
+  return m_name;
 }
 
 inline void
 KeyLocator::setName(const Name &name)
 {
-  type_ = KeyLocator_Name;
-  name_ = name;
+  m_type = KeyLocator_Name;
+  m_name = name;
 }
 
 
diff --git a/src/util/string-helper.hpp b/src/util/string-helper.hpp
index de0c41f..14f499e 100644
--- a/src/util/string-helper.hpp
+++ b/src/util/string-helper.hpp
@@ -16,6 +16,29 @@
 static const char *WHITESPACE_CHARS = " \n\r\t";
 
 /**
+ * Return the hex representation of the bytes in array.
+ * @param array The array of bytes.
+ * @return Hex string.
+ */
+inline std::string 
+toHex(const uint8_t* array, size_t arraySize)
+{
+  if (!&array)
+    return "";
+  
+  std::ostringstream result;
+  result.flags(std::ios::hex | std::ios::uppercase);
+  for (size_t i = 0; i < arraySize; ++i) {
+    uint8_t x = array[i];
+    if (x < 16)
+      result << '0';
+    result << (unsigned int)x;
+  }
+
+  return result.str();
+}
+
+/**
  * Modify str in place to erase whitespace on the left.
  * @param str
  */
diff --git a/src/util/time.hpp b/src/util/time.hpp
index 5d729d9..e0b3727 100644
--- a/src/util/time.hpp
+++ b/src/util/time.hpp
@@ -12,6 +12,17 @@
 
 namespace ndn {
 
+/**
+ * A time interval represented as the number of milliseconds.
+ */
+typedef int64_t Milliseconds;
+   
+/**
+ * The calendar time represented as the number of milliseconds since 1/1/1970.
+ */
+typedef int64_t MillisecondsSince1970;
+
+
 const boost::posix_time::ptime UNIX_EPOCH_TIME =
   boost::posix_time::ptime (boost::gregorian::date (1970, boost::gregorian::Jan, 1));
 
@@ -30,6 +41,11 @@
   return getNowMilliseconds();
 }
 
+inline MillisecondsSince1970
+getNow()
+{
+  return getNowMilliseconds();
+}
 
 /**
  * Convert to the ISO string representation of the time.
diff --git a/tests/test-interest.cpp b/tests/test-interest.cpp
index df0cdf3..4ddc3b9 100644
--- a/tests/test-interest.cpp
+++ b/tests/test-interest.cpp
@@ -97,6 +97,7 @@
   i.setNonce(1);
 
   const Block &wire = i.wireEncode();
+
   BOOST_REQUIRE_EQUAL_COLLECTIONS(Interest1, Interest1+sizeof(Interest1),
                                   wire.begin(), wire.end());
 }