data: Optimize Data signing

As of this commit, KeyChain::sign pre-allocates EncodingBuffer, requests
unsigned portion of Data using Data::wireEncode(EncodingBuffer, true),
and then appends the resulting signature and prepends Data packet
header.  This way there is no extra memory allocation after Data packet
is signed.

Change-Id: I670e9a2f1d6f5e9b049f41b47f011af384f32c95
diff --git a/src/data.cpp b/src/data.cpp
new file mode 100644
index 0000000..8ce8f4c
--- /dev/null
+++ b/src/data.cpp
@@ -0,0 +1,308 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2014 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 "data.hpp"
+#include "util/crypto.hpp"
+
+namespace ndn {
+
+Data::Data()
+  : m_content(Tlv::Content) // empty content
+{
+}
+
+Data::Data(const Name& name)
+  : m_name(name)
+{
+}
+
+Data::Data(const Block& wire)
+{
+  wireDecode(wire);
+}
+
+template<bool T>
+size_t
+Data::wireEncode(EncodingImpl<T>& block, bool unsignedPortion/* = false*/) const
+{
+  size_t totalLength = 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
+      totalLength += prependBlock(block, m_signature.getValue());
+    }
+
+  // SignatureInfo
+  totalLength += prependBlock(block, m_signature.getInfo());
+
+  // Content
+  totalLength += prependBlock(block, getContent());
+
+  // MetaInfo
+  totalLength += getMetaInfo().wireEncode(block);
+
+  // Name
+  totalLength += getName().wireEncode(block);
+
+  if (!unsignedPortion)
+    {
+      totalLength += block.prependVarNumber(totalLength);
+      totalLength += block.prependVarNumber(Tlv::Data);
+    }
+  return totalLength;
+}
+
+const Block&
+Data::wireEncode(EncodingBuffer& encoder, const Block& signatureValue) const
+{
+  size_t totalLength = encoder.size();
+  totalLength += encoder.appendBlock(signatureValue);
+
+  totalLength += encoder.prependVarNumber(totalLength);
+  encoder.prependVarNumber(Tlv::Data);
+
+  const_cast<Data*>(this)->wireDecode(encoder.block());
+  return m_wire;
+}
+
+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;
+}
+
+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);
+}
+
+Data&
+Data::setName(const Name& name)
+{
+  onChanged();
+  m_name = name;
+
+  return *this;
+}
+
+Data&
+Data::setMetaInfo(const MetaInfo& metaInfo)
+{
+  onChanged();
+  m_metaInfo = metaInfo;
+
+  return *this;
+}
+
+Data&
+Data::setContentType(uint32_t type)
+{
+  onChanged();
+  m_metaInfo.setType(type);
+
+  return *this;
+}
+
+Data&
+Data::setFreshnessPeriod(const time::milliseconds& freshnessPeriod)
+{
+  onChanged();
+  m_metaInfo.setFreshnessPeriod(freshnessPeriod);
+
+  return *this;
+}
+
+Data&
+Data::setFinalBlockId(const name::Component& finalBlockId)
+{
+  onChanged();
+  m_metaInfo.setFinalBlockId(finalBlockId);
+
+  return *this;
+}
+
+const Block&
+Data::getContent() const
+{
+  if (m_content.empty())
+    m_content = dataBlock(Tlv::Content, reinterpret_cast<const uint8_t*>(0), 0);
+
+  if (!m_content.hasWire())
+      m_content.encode();
+  return m_content;
+}
+
+Data&
+Data::setContent(const uint8_t* content, size_t contentLength)
+{
+  onChanged();
+
+  m_content = dataBlock(Tlv::Content, content, contentLength);
+
+  return *this;
+}
+
+Data&
+Data::setContent(const ConstBufferPtr& contentValue)
+{
+  onChanged();
+
+  m_content = Block(Tlv::Content, contentValue); // not a real wire encoding yet
+
+  return *this;
+}
+
+Data&
+Data::setContent(const Block& content)
+{
+  onChanged();
+
+  if (content.type() == Tlv::Content)
+    m_content = content;
+  else {
+    m_content = Block(Tlv::Content, content);
+  }
+
+  return *this;
+}
+
+Data&
+Data::setSignature(const Signature& signature)
+{
+  onChanged();
+  m_signature = signature;
+
+  return *this;
+}
+
+Data&
+Data::setSignatureValue(const Block& value)
+{
+  onChanged();
+  m_signature.setValue(value);
+
+  return *this;
+}
+
+//
+
+Data&
+Data::setIncomingFaceId(uint64_t incomingFaceId)
+{
+  getLocalControlHeader().setIncomingFaceId(incomingFaceId);
+  // ! do not reset Data's wire !
+
+  return *this;
+}
+
+void
+Data::onChanged()
+{
+  // The values have changed, so the wire format is invalidated
+
+  // !!!Note!!! Signature is not invalidated and it is responsibility of
+  // the application to do proper re-signing if necessary
+
+  m_wire.reset();
+}
+
+bool
+Data::operator==(const Data& other) const
+{
+  return getName() == other.getName() &&
+    getMetaInfo() == other.getMetaInfo() &&
+    getContent() == other.getContent() &&
+    getSignature() == other.getSignature();
+}
+
+bool
+Data::operator!=(const Data& other) const
+{
+  return !(*this == other);
+}
+
+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