encoding: Refactoring EncodingBuffer

Breaks: nfd:commit:c0273e3505ac2ccf843401be77a513d8eb663127
Breaks: ChronoSync:commit:e042f83a1df184a8e7a90ef00034d11026891cd1

Change-Id: I8275c6276c5ecfa280f87f584189907521febf5f
Refs: #2494, #2490
diff --git a/src/encoding/block-helpers.hpp b/src/encoding/block-helpers.hpp
index 0a58aa0..e3e86f4 100644
--- a/src/encoding/block-helpers.hpp
+++ b/src/encoding/block-helpers.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2014 Regents of the University of California.
+ * Copyright (c) 2013-2015 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -17,8 +17,6 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
  */
 
 #ifndef NDN_ENCODING_BLOCK_HELPERS_HPP
@@ -27,8 +25,66 @@
 #include "block.hpp"
 #include "encoding-buffer.hpp"
 
+#include <iterator>
+
 namespace ndn {
 
+/**
+ * @deprecated Use Encoder::prependBlock and Estimator::prependBlock instead
+ */
+template<bool P>
+inline size_t
+prependBlock(EncodingImpl<P>& encoder, const Block& block)
+{
+  return encoder.prependBlock(block);
+}
+
+/**
+ * @deprecated Use Encoder::prependByteArrayBlock and Estimator::prependByteArrayBlock instead
+ */
+template<bool P>
+inline size_t
+prependByteArrayBlock(EncodingImpl<P>& encoder,
+                      uint32_t type, const uint8_t* array, size_t arraySize)
+{
+  return encoder.prependByteArrayBlock(type, array, arraySize);
+}
+
+template<bool P>
+inline size_t
+prependNonNegativeIntegerBlock(EncodingImpl<P>& encoder, uint32_t type, uint64_t number)
+{
+  size_t valueLength = encoder.prependNonNegativeInteger(number);
+  size_t totalLength = valueLength;
+  totalLength += encoder.prependVarNumber(valueLength);
+  totalLength += encoder.prependVarNumber(type);
+
+  return totalLength;
+}
+
+template<bool P>
+inline size_t
+prependBooleanBlock(EncodingImpl<P>& encoder, uint32_t type)
+{
+  size_t totalLength = encoder.prependVarNumber(0);
+  totalLength += encoder.prependVarNumber(type);
+
+  return totalLength;
+}
+
+template<bool P, class U>
+inline size_t
+prependNestedBlock(EncodingImpl<P>& encoder, uint32_t type, const U& nestedBlock)
+{
+  size_t valueLength = nestedBlock.wireEncode(encoder);
+  size_t totalLength = valueLength;
+  totalLength += encoder.prependVarNumber(valueLength);
+  totalLength += encoder.prependVarNumber(type);
+
+  return totalLength;
+}
+
+
 inline Block
 nonNegativeIntegerBlock(uint32_t type, uint64_t value)
 {
@@ -64,10 +120,10 @@
 dataBlock(uint32_t type, const uint8_t* data, size_t dataSize)
 {
   EncodingEstimator estimator;
-  size_t totalLength = prependByteArrayBlock(estimator, type, data, dataSize);
+  size_t totalLength = estimator.prependByteArrayBlock(type, data, dataSize);
 
   EncodingBuffer encoder(totalLength, 0);
-  prependByteArrayBlock(encoder, type, data, dataSize);
+  encoder.prependByteArrayBlock(type, data, dataSize);
 
   return encoder.block();
 }
@@ -78,21 +134,77 @@
   return dataBlock(type, reinterpret_cast<const uint8_t*>(data), dataSize);
 }
 
-// template<class InputIterator>
-// inline Block
-// dataBlock(uint32_t type, InputIterator first, InputIterator last)
-// {
-//   size_t dataSize = 0;
-//   for (InputIterator i = first; i != last; i++)
-//     ++dataSize;
+/**
+ * @brief Helper class template to create a data block when RandomAccessIterator is used
+ */
+template<class Iterator>
+class DataBlockFast
+{
+public:
+  BOOST_CONCEPT_ASSERT((boost::RandomAccessIterator<Iterator>));
 
-//   OBufferStream os;
-//   tlv::writeVarNumber(os, type);
-//   tlv::writeVarNumber(os, dataSize);
-//   std::copy(first, last, std::ostream_iterator<uint8_t>(os));
+  static Block
+  makeBlock(uint32_t type, Iterator first, Iterator last)
+  {
+    EncodingEstimator estimator;
+    size_t valueLength = last - first;
+    size_t totalLength = valueLength;
+    totalLength += estimator.prependVarNumber(valueLength);
+    totalLength += estimator.prependVarNumber(type);
 
-//   return Block(os.buf());
-// }
+    EncodingBuffer encoder(totalLength, 0);
+    encoder.prependRange(first, last);
+    encoder.prependVarNumber(valueLength);
+    encoder.prependVarNumber(type);
+
+    return encoder.block();
+  }
+};
+
+/**
+ * @brief Helper class template to create a data block when generic InputIterator is used
+ */
+template<class Iterator>
+class DataBlockSlow
+{
+public:
+  BOOST_CONCEPT_ASSERT((boost::InputIterator<Iterator>));
+
+  static Block
+  makeBlock(uint32_t type, Iterator first, Iterator last)
+  {
+    // reserve 4 bytes in front (common for 1(type)-3(length) encoding
+    // Actual size will be adjusted as necessary by the encoder
+    EncodingBuffer encoder(4, 4);
+    size_t valueLength = encoder.appendRange(first, last);
+    encoder.prependVarNumber(valueLength);
+    encoder.prependVarNumber(type);
+
+    return encoder.block();
+  }
+};
+
+/**
+ * @brief Free function to create a block given @p type and range [@p first, @p last) of bytes
+ * @tparam Iterator iterator type satisfying at least InputIterator concept.  Implementation
+ *                  is more optimal when the iterator type satisfies RandomAccessIterator concept
+ *                  It is required that sizeof(std::iterator_traits<Iterator>::value_type) == 1.
+ */
+template<class Iterator>
+inline Block
+dataBlock(uint32_t type, Iterator first, Iterator last)
+{
+  static_assert(sizeof(typename std::iterator_traits<Iterator>::value_type) == 1,
+                "Iterator should point only to char or unsigned char");
+
+  typedef typename boost::mpl::if_<
+    std::is_base_of<std::random_access_iterator_tag,
+                    typename std::iterator_traits<Iterator>::iterator_category>,
+    DataBlockFast<Iterator>,
+    DataBlockSlow<Iterator>>::type DataBlock;
+
+  return DataBlock::makeBlock(type, first, last);
+}
 
 } // namespace ndn
 
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index e8d2cf9..ce13427 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -51,7 +51,7 @@
 }
 
 Block::Block(const EncodingBuffer& buffer)
-  : m_buffer(buffer.m_buffer)
+  : m_buffer(const_cast<EncodingBuffer&>(buffer).getBuffer())
   , m_begin(buffer.begin())
   , m_end(buffer.end())
   , m_size(m_end - m_begin)
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index 3daf497..4bbfe52 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -28,6 +28,7 @@
 
 #include "buffer.hpp"
 #include "tlv.hpp"
+#include "encoding-buffer-fwd.hpp"
 
 namespace boost {
 namespace asio {
@@ -37,9 +38,6 @@
 
 namespace ndn {
 
-template<bool> class EncodingImpl;
-typedef EncodingImpl<true> EncodingBuffer;
-
 /** @brief Class representing a wire element of NDN-TLV packet format
  */
 class Block
@@ -247,6 +245,12 @@
   Block
   blockFromValue() const;
 
+  /**
+   * @brief Get underlying buffer
+   */
+  shared_ptr<const Buffer>
+  getBuffer() const;
+
 public: // EqualityComparable concept
   bool
   operator==(const Block& other) const;
@@ -258,7 +262,7 @@
   operator boost::asio::const_buffer() const;
 
 protected:
-  ConstBufferPtr m_buffer;
+  shared_ptr<const Buffer> m_buffer;
 
   uint32_t m_type;
 
@@ -270,13 +274,18 @@
   Buffer::const_iterator m_value_end;
 
   mutable element_container m_subBlocks;
-  friend class EncodingImpl<true>;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
+inline shared_ptr<const Buffer>
+Block::getBuffer() const
+{
+  return m_buffer;
+}
+
 inline bool
 Block::empty() const
 {
diff --git a/src/encoding/encoder.cpp b/src/encoding/encoder.cpp
new file mode 100644
index 0000000..4b32c10
--- /dev/null
+++ b/src/encoding/encoder.cpp
@@ -0,0 +1,283 @@
+/* -*- 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 "encoder.hpp"
+
+namespace ndn {
+namespace encoding {
+
+Encoder::Encoder(size_t totalReserve/* = 8800*/, size_t reserveFromBack/* = 400*/)
+  : m_buffer(new Buffer(totalReserve))
+{
+  m_begin = m_end = m_buffer->end() - (reserveFromBack < totalReserve ? reserveFromBack : 0);
+}
+
+
+Encoder::Encoder(const Block& block)
+  : m_buffer(const_pointer_cast<Buffer>(block.getBuffer()))
+  , m_begin(m_buffer->begin() + (block.begin() - m_buffer->begin()))
+  , m_end(m_buffer->begin()   + (block.end()   - m_buffer->begin()))
+{
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+void
+Encoder::reserveBack(size_t size)
+{
+  if ((m_end + size) > m_buffer->end())
+    reserve(m_buffer->size() * 2 + size, false);
+}
+
+void
+Encoder::reserveFront(size_t size)
+{
+  if ((m_buffer->begin() + size) > m_begin)
+    reserve(m_buffer->size() * 2 + size, true);
+}
+
+
+Block
+Encoder::block(bool verifyLength/* = true*/) const
+{
+  return Block(m_buffer,
+               m_begin, m_end,
+               verifyLength);
+}
+
+void
+Encoder::reserve(size_t size, bool addInFront)
+{
+  if (size < m_buffer->size()) {
+    size = m_buffer->size();
+  }
+
+  if (addInFront) {
+    size_t diffEnd = m_buffer->end() - m_end;
+    size_t diffBegin = m_buffer->end() - m_begin;
+
+    Buffer* buf = new Buffer(size);
+    std::copy_backward(m_buffer->begin(), m_buffer->end(), buf->end());
+
+    m_buffer.reset(buf);
+
+    m_end = m_buffer->end() - diffEnd;
+    m_begin = m_buffer->end() - diffBegin;
+  }
+  else {
+    size_t diffEnd = m_end - m_buffer->begin();
+    size_t diffBegin = m_begin - m_buffer->begin();
+
+    Buffer* buf = new Buffer(size);
+    std::copy(m_buffer->begin(), m_buffer->end(), buf->begin());
+
+    m_buffer.reset(buf);
+
+    m_end = m_buffer->begin() + diffEnd;
+    m_begin = m_buffer->begin() + diffBegin;
+  }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+size_t
+Encoder::prependByte(uint8_t value)
+{
+  reserveFront(1);
+
+  m_begin--;
+  *m_begin = value;
+  return 1;
+}
+
+size_t
+Encoder::appendByte(uint8_t value)
+{
+  reserveBack(1);
+
+  *m_end = value;
+  m_end++;
+  return 1;
+}
+
+
+size_t
+Encoder::prependByteArray(const uint8_t* array, size_t length)
+{
+  reserveFront(length);
+
+  m_begin -= length;
+  std::copy(array, array + length, m_begin);
+  return length;
+}
+
+size_t
+Encoder::appendByteArray(const uint8_t* array, size_t length)
+{
+  reserveBack(length);
+
+  std::copy(array, array + length, m_end);
+  m_end += length;
+  return length;
+}
+
+
+size_t
+Encoder::prependVarNumber(uint64_t varNumber)
+{
+  if (varNumber < 253) {
+    prependByte(static_cast<uint8_t>(varNumber));
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
+    prependByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
+    prependByte(253);
+    return 3;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
+    prependByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
+    prependByte(254);
+    return 5;
+  }
+  else {
+    uint64_t value = htobe64(varNumber);
+    prependByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
+    prependByte(255);
+    return 9;
+  }
+}
+
+size_t
+Encoder::appendVarNumber(uint64_t varNumber)
+{
+  if (varNumber < 253) {
+    appendByte(static_cast<uint8_t>(varNumber));
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    appendByte(253);
+    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
+    appendByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
+    return 3;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    appendByte(254);
+    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
+    appendByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
+    return 5;
+  }
+  else {
+    appendByte(255);
+    uint64_t value = htobe64(varNumber);
+    appendByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
+    return 9;
+  }
+}
+
+
+size_t
+Encoder::prependNonNegativeInteger(uint64_t varNumber)
+{
+  if (varNumber <= std::numeric_limits<uint8_t>::max()) {
+    return prependByte(static_cast<uint8_t>(varNumber));
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
+    return prependByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
+    return prependByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
+  }
+  else {
+    uint64_t value = htobe64(varNumber);
+    return prependByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
+  }
+}
+
+size_t
+Encoder::appendNonNegativeInteger(uint64_t varNumber)
+{
+  if (varNumber <= std::numeric_limits<uint8_t>::max()) {
+    return appendByte(static_cast<uint8_t>(varNumber));
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
+    return appendByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
+    return appendByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
+  }
+  else {
+    uint64_t value = htobe64(varNumber);
+    return appendByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
+  }
+}
+
+size_t
+Encoder::prependByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize)
+{
+  size_t totalLength = prependByteArray(array, arraySize);
+  totalLength += prependVarNumber(arraySize);
+  totalLength += prependVarNumber(type);
+
+  return totalLength;
+}
+
+size_t
+Encoder::appendByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize)
+{
+  size_t totalLength = appendVarNumber(type);
+  totalLength += appendVarNumber(arraySize);
+  totalLength += appendByteArray(array, arraySize);
+
+  return totalLength;
+}
+
+size_t
+Encoder::prependBlock(const Block& block)
+{
+  if (block.hasWire()) {
+    return prependByteArray(block.wire(), block.size());
+  }
+  else {
+    return prependByteArrayBlock(block.type(), block.value(), block.value_size());
+  }
+}
+
+size_t
+Encoder::appendBlock(const Block& block)
+{
+  if (block.hasWire()) {
+    return appendByteArray(block.wire(), block.size());
+  }
+  else {
+    return appendByteArrayBlock(block.type(), block.value(), block.value_size());
+  }
+}
+
+} // namespace encoding
+} // namespace ndn
diff --git a/src/encoding/encoder.hpp b/src/encoding/encoder.hpp
new file mode 100644
index 0000000..a258d1e
--- /dev/null
+++ b/src/encoding/encoder.hpp
@@ -0,0 +1,343 @@
+/* -*- 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_ENCODING_ENCODER_HPP
+#define NDN_ENCODING_ENCODER_HPP
+
+#include "../common.hpp"
+#include "block.hpp"
+
+namespace ndn {
+namespace encoding {
+
+/**
+ * @brief Helper class to perform TLV encoding
+ * Interface of this class (mostly) matches interface of Estimator class
+ * @sa Estimator
+ */
+class Encoder
+{
+public: // common interface between Encoder and Estimator
+  /**
+   * @brief Create instance of the encoder with the specified reserved sizes
+   * @param totalReserve  initial buffer size to reserve
+   * @param totalFromBack number of bytes to reserve for append* operations
+   */
+  explicit
+  Encoder(size_t totalReserve = 8800, size_t reserveFromBack = 400);
+
+  Encoder(const Encoder&) = delete;
+
+  Encoder&
+  operator=(const Encoder&) = delete;
+
+  /**
+   * @brief Prepend a byte
+   */
+  size_t
+  prependByte(uint8_t value);
+
+  /**
+   * @brief Append a byte
+   */
+  size_t
+  appendByte(uint8_t value);
+
+  /**
+   * @brief Prepend a byte array @p array of length @p length
+   */
+  size_t
+  prependByteArray(const uint8_t* array, size_t length);
+
+  /**
+   * @brief Append a byte array @p array of length @p length
+   */
+  size_t
+  appendByteArray(const uint8_t* array, size_t length);
+
+  /**
+   * @brief Prepend range of bytes from the range [@p first, @p last)
+   */
+  template<class Iterator>
+  size_t
+  prependRange(Iterator first, Iterator last);
+
+  /**
+   * @brief Append range of bytes from the range [@p first, @p last)
+   */
+  template<class Iterator>
+  size_t
+  appendRange(Iterator first, Iterator last);
+
+  /**
+   * @brief Prepend VarNumber @p varNumber of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  prependVarNumber(uint64_t varNumber);
+
+  /**
+   * @brief Prepend VarNumber @p varNumber of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  appendVarNumber(uint64_t varNumber);
+
+  /**
+   * @brief Prepend non-negative integer @p integer of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  prependNonNegativeInteger(uint64_t integer);
+
+  /**
+   * @brief Append non-negative integer @p integer of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  appendNonNegativeInteger(uint64_t integer);
+
+  /**
+   * @brief Prepend TLV block of type @p type and value from buffer @p array of size @p arraySize
+   */
+  size_t
+  prependByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize);
+
+  /**
+   * @brief Append TLV block of type @p type and value from buffer @p array of size @p arraySize
+   */
+  size_t
+  appendByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize);
+
+  /**
+   * @brief Prepend TLV block @p block
+   */
+  size_t
+  prependBlock(const Block& block);
+
+  /**
+   * @brief Append TLV block @p block
+   */
+  size_t
+  appendBlock(const Block& block);
+
+public: // unique interface to the Encoder
+  typedef Buffer::value_type value_type;
+  typedef Buffer::iterator iterator;
+  typedef Buffer::const_iterator const_iterator;
+
+  /**
+   * @brief Create EncodingBlock from existing block
+   *
+   * This is a dangerous constructor and should be used with caution.
+   * It will modify contents of the buffer that is used by block and may
+   * impact data in other blocks.
+   *
+   * The primary purpose for this method is to be used to extend Block
+   * after sign operation.
+   */
+  explicit
+  Encoder(const Block& block);
+
+  /**
+   * @brief Reserve @p size bytes for the underlying buffer
+   * @param addInFront if true, then @p size bytes will be available in front (i.e., subsequent call
+   *        to prepend* will not need to allocate memory).  If false, then reservation will be done
+   *        at the end of the buffer (i.d., for subsequent append* calls)
+   * @note Reserve size is exact, unlike reserveFront and reserveBack methods
+   * @sa reserveFront, reserveBack
+   */
+  void
+  reserve(size_t size, bool addInFront);
+
+  /**
+   * @brief Reserve at least @p size bytes at the back of the underlying buffer
+   * @note the actual reserve size can be (and in most cases is) larger than specified reservation
+   *       length
+   */
+  void
+  reserveBack(size_t size);
+
+  /**
+   * @brief Reserve at least @p isze bytes at the beginning of the underlying buffer
+   * @note the actual reserve size can be (and in most cases is) larger than specified reservation
+   *       length
+   */
+  void
+  reserveFront(size_t size);
+
+  /**
+   * @brief Get size of the underlying buffer
+   */
+  size_t
+  capacity() const;
+
+  /**
+   * @brief Get underlying buffer
+   */
+  shared_ptr<Buffer>
+  getBuffer();
+
+public: // accessors
+
+  /**
+   * @brief Get an iterator pointing to the first byte of the encoded buffer
+   */
+  iterator
+  begin();
+
+  /**
+   * @brief Get an iterator pointing to the past-the-end byte of the encoded buffer
+   */
+  iterator
+  end();
+
+  /**
+   * @brief Get an iterator pointing to the first byte of the encoded buffer
+   */
+  const_iterator
+  begin() const;
+
+  const_iterator
+  end() const;
+
+  /**
+   * @brief Get a pointer to the first byte of the encoded buffer
+   */
+  uint8_t*
+  buf();
+
+  /**
+   * @brief Get a pointer to the first byte of the encoded buffer
+   */
+  const uint8_t*
+  buf() const;
+
+  /**
+   * @brief Get size of the encoded buffer
+   */
+  size_t
+  size() const;
+
+  /**
+   * @brief Create Block from the underlying buffer
+   *
+   * @param verifyLength If this parameter set to true, Block's constructor
+   *                     will be requested to verify consistency of the encoded
+   *                     length in the Block, otherwise ignored
+   */
+  Block
+  block(bool verifyLength = true) const;
+
+private:
+  shared_ptr<Buffer> m_buffer;
+
+  // invariant: m_begin always points to the position of last-written byte (if prepending data)
+  iterator m_begin;
+  // invariant: m_end always points to the position of next unwritten byte (if appending data)
+  iterator m_end;
+};
+
+
+inline size_t
+Encoder::size() const
+{
+  return m_end - m_begin;
+}
+
+inline shared_ptr<Buffer>
+Encoder::getBuffer()
+{
+  return m_buffer;
+}
+
+inline size_t
+Encoder::capacity() const
+{
+  return m_buffer->size();
+}
+
+inline Buffer::iterator
+Encoder::begin()
+{
+  return m_begin;
+}
+
+inline Buffer::iterator
+Encoder::end()
+{
+  return m_end;
+}
+
+inline Buffer::const_iterator
+Encoder::begin() const
+{
+  return m_begin;
+}
+
+inline Buffer::const_iterator
+Encoder::end() const
+{
+  return m_end;
+}
+
+inline uint8_t*
+Encoder::buf()
+{
+  return &(*m_begin);
+}
+
+inline const uint8_t*
+Encoder::buf() const
+{
+  return &(*m_begin);
+}
+
+template<class Iterator>
+inline size_t
+Encoder::prependRange(Iterator first, Iterator last)
+{
+  size_t length = std::distance(first, last);
+  reserveFront(length);
+
+  m_begin -= length;
+  std::copy(first, last, m_begin);
+  return length;
+}
+
+
+template<class Iterator>
+inline size_t
+Encoder::appendRange(Iterator first, Iterator last)
+{
+  size_t length = std::distance(first, last);
+  reserveBack(length);
+
+  std::copy(first, last, m_end);
+  m_end += length;
+  return length;
+}
+
+
+} // namespace encoding
+} // namespace ndn
+
+#endif // NDN_ENCODING_ENCODER_HPP
diff --git a/src/encoding/encoding-buffer-fwd.hpp b/src/encoding/encoding-buffer-fwd.hpp
new file mode 100644
index 0000000..359864f
--- /dev/null
+++ b/src/encoding/encoding-buffer-fwd.hpp
@@ -0,0 +1,58 @@
+/* -*- 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_ENCODING_ENCODING_BUFFER_FWD_HPP
+#define NDN_ENCODING_ENCODING_BUFFER_FWD_HPP
+
+namespace ndn {
+namespace encoding {
+
+typedef bool Tag;
+
+/**
+ * @brief Tag for EncodingImpl to indicate that Encoder is requested
+ * Implementation of the tag may change to class. Use of true directly
+ * as a template parameter is discouraged.
+ */
+static const Tag EncoderTag = true;
+
+/**
+ * @brief Tag for EncodingImpl to indicate that Estimator is requested
+ * Implementation of the tag may change to class. Use of false directly
+ * as a template parameter is discouraged.
+ */
+static const Tag EstimatorTag = false;
+
+template<Tag TAG>
+class EncodingImpl;
+
+typedef EncodingImpl<EncoderTag> EncodingBuffer;
+typedef EncodingImpl<EstimatorTag> EncodingEstimator;
+
+} // namespace encoding
+
+using encoding::EncodingImpl;
+using encoding::EncodingBuffer;
+using encoding::EncodingEstimator;
+
+} // namespace ndn
+
+#endif // NDN_ENCODING_ENCODING_BUFFER_FWD_HPP
diff --git a/src/encoding/encoding-buffer.hpp b/src/encoding/encoding-buffer.hpp
index 4c5185e..67cc4bb 100644
--- a/src/encoding/encoding-buffer.hpp
+++ b/src/encoding/encoding-buffer.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2013-2014 Regents of the University of California.
+ * Copyright (c) 2013-2015 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -17,636 +17,54 @@
  * <http://www.gnu.org/licenses/>.
  *
  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
- *
- * @author Wentao Shang <http://irl.cs.ucla.edu/~wentao/>
- * @author Alexander Afanasyev <http://lasr.cs.ucla.edu/afanasyev/index.html>
  */
 
 #ifndef NDN_ENCODING_ENCODING_BUFFER_HPP
 #define NDN_ENCODING_ENCODING_BUFFER_HPP
 
 #include "../common.hpp"
-
-#include "buffer.hpp"
-#include "block.hpp"
+#include "encoding-buffer-fwd.hpp"
+#include "encoder.hpp"
+#include "estimator.hpp"
 
 namespace ndn {
-
 namespace encoding {
-static const bool Buffer = true;
-static const bool Estimator = false;
-} // encoding
-
-template<bool isRealEncoderNotEstimator>
-class EncodingImpl;
-
-typedef EncodingImpl<encoding::Buffer> EncodingBuffer;
-typedef EncodingImpl<encoding::Estimator> EncodingEstimator;
 
 /**
- * @brief Class representing wire element of the NDN packet
+ * @brief EncodingImpl specialization for real TLV encoding
  */
 template<>
-class EncodingImpl<encoding::Buffer>
+class EncodingImpl<EncoderTag> : public encoding::Encoder
 {
 public:
-  /**
-   * @brief Constructor to create a EncodingImpl with specified reserved sizes
-   *
-   * The caller should make sure that that reserveFromBack does not exceed totalReserve,
-   * otherwise behavior is undefined.
-   */
-  EncodingImpl(size_t totalReserve = 8800,
-               size_t reserveFromBack = 400)
-    : m_buffer(new Buffer(totalReserve))
+  explicit
+  EncodingImpl(size_t totalReserve = 8800, size_t reserveFromBack = 400)
+    : Encoder(totalReserve, reserveFromBack)
   {
-    m_begin = m_end = m_buffer->end() - (reserveFromBack < totalReserve ? reserveFromBack : 0);
   }
 
-  /**
-   * @brief Create EncodingBlock from existing block
-   *
-   * This is a dangerous constructor and should be used with caution.
-   * It will modify contents of the buffer that is used by block and may
-   * impact data in other blocks.
-   *
-   * The primary purpose for this method is to be used to extend Block
-   * after sign operation.
-   */
   explicit
   EncodingImpl(const Block& block)
-    : m_buffer(const_pointer_cast<Buffer>(block.m_buffer))
-    , m_begin(m_buffer->begin() + (block.begin() - m_buffer->begin()))
-    , m_end(m_buffer->begin()   + (block.end()   - m_buffer->begin()))
+    : Encoder(block)
   {
   }
-
-  inline size_t
-  size() const;
-
-  inline size_t
-  capacity() const;
-
-  inline uint8_t*
-  buf();
-
-  inline const uint8_t*
-  buf() const;
-
-  /**
-   * @brief Create Block from the underlying EncodingBuffer
-   *
-   * @param verifyLength If this parameter set to true, Block's constructor
-   *                     will be requested to verify consistency of the encoded
-   *                     length in the Block, otherwise ignored
-   */
-  inline Block
-  block(bool verifyLength = true) const;
-
-  inline void
-  resize(size_t size, bool addInFront);
-
-  inline Buffer::iterator
-  begin();
-
-  inline Buffer::iterator
-  end();
-
-  inline Buffer::const_iterator
-  begin() const;
-
-  inline Buffer::const_iterator
-  end() const;
-
-  inline size_t
-  prependByte(uint8_t value);
-
-  inline size_t
-  prependByteArray(const uint8_t* array, size_t length);
-
-  inline size_t
-  prependNonNegativeInteger(uint64_t varNumber);
-
-  inline size_t
-  prependVarNumber(uint64_t varNumber);
-
-  inline size_t
-  prependBlock(const Block& block);
-
-  inline size_t
-  appendByte(uint8_t value);
-
-  inline size_t
-  appendByteArray(const uint8_t* array, size_t length);
-
-  inline size_t
-  appendNonNegativeInteger(uint64_t varNumber);
-
-  inline size_t
-  appendVarNumber(uint64_t varNumber);
-
-  inline size_t
-  appendBlock(const Block& block);
-
-  // inline void
-  // removeByteFromFront();
-
-  // inline void
-  // removeByteFromEnd();
-
-  // inline void
-  // removeVarNumberFromFront(uint64_t varNumber);
-
-  // inline void
-  // removeVarNumberFromBack(uint64_t varNumber);
-
-private:
-  BufferPtr m_buffer;
-
-  // invariant: m_begin always points to the position of last-written byte (if prepending data)
-  Buffer::iterator m_begin;
-  // invariant: m_end always points to the position of next unwritten byte (if appending data)
-  Buffer::iterator m_end;
-
-  friend class Block;
 };
 
-
 /**
- * @brief Class representing wire element of the NDN packet
+ * @brief EncodingImpl specialization TLV size estimation
  */
 template<>
-class EncodingImpl<encoding::Estimator>
+class EncodingImpl<EstimatorTag> : public encoding::Estimator
 {
 public:
-  EncodingImpl(size_t totalReserve = 8800,
-               size_t reserveFromBack = 400)
+  explicit
+  EncodingImpl(size_t totalReserve = 0, size_t totalFromBack = 0)
+    : Estimator(totalReserve, totalFromBack)
   {
   }
-
-  inline size_t
-  prependByte(uint8_t value);
-
-  inline size_t
-  prependByteArray(const uint8_t* array, size_t length);
-
-  inline size_t
-  prependNonNegativeInteger(uint64_t varNumber);
-
-  inline size_t
-  prependVarNumber(uint64_t varNumber);
-
-  inline size_t
-  prependBlock(const Block& block);
-
-  inline size_t
-  appendByte(uint8_t value);
-
-  inline size_t
-  appendByteArray(const uint8_t* array, size_t length);
-
-  inline size_t
-  appendNonNegativeInteger(uint64_t varNumber);
-
-  inline size_t
-  appendVarNumber(uint64_t varNumber);
-
-  inline size_t
-  appendBlock(const Block& block);
 };
 
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-/// helper methods
-
-template<bool P>
-inline size_t
-prependNonNegativeIntegerBlock(EncodingImpl<P>& encoder, uint32_t type, uint64_t number)
-{
-  size_t valueLength = encoder.prependNonNegativeInteger(number);
-  size_t totalLength = valueLength;
-  totalLength += encoder.prependVarNumber(valueLength);
-  totalLength += encoder.prependVarNumber(type);
-
-  return totalLength;
-}
-
-template<bool P>
-inline size_t
-prependByteArrayBlock(EncodingImpl<P>& encoder, uint32_t type,
-                      const uint8_t* array, size_t arraySize)
-{
-  size_t valueLength = encoder.prependByteArray(array, arraySize);
-  size_t totalLength = valueLength;
-  totalLength += encoder.prependVarNumber(valueLength);
-  totalLength += encoder.prependVarNumber(type);
-
-  return totalLength;
-}
-
-template<bool P>
-inline size_t
-prependBooleanBlock(EncodingImpl<P>& encoder, uint32_t type)
-{
-  size_t totalLength = encoder.prependVarNumber(0);
-  totalLength += encoder.prependVarNumber(type);
-
-  return totalLength;
-}
-
-
-template<bool P, class U>
-inline size_t
-prependNestedBlock(EncodingImpl<P>& encoder, uint32_t type, const U& nestedBlock)
-{
-  size_t valueLength = nestedBlock.wireEncode(encoder);
-  size_t totalLength = valueLength;
-  totalLength += encoder.prependVarNumber(valueLength);
-  totalLength += encoder.prependVarNumber(type);
-
-  return totalLength;
-}
-
-template<bool P>
-inline size_t
-prependBlock(EncodingImpl<P>& encoder, const Block& block)
-{
-  return encoder.prependByteArray(block.wire(), block.size());
-}
-
-template<bool P>
-inline size_t
-appendByteArrayBlock(EncodingImpl<P>& encoder, uint32_t type,
-                      const uint8_t* array, size_t arraySize)
-{
-  size_t totalLength = encoder.appendVarNumber(type);
-  totalLength += encoder.appendVarNumber(arraySize);
-  totalLength += encoder.appendByteArray(array, arraySize);
-
-  return totalLength;
-}
-
-template<bool P>
-inline size_t
-appendBlock(EncodingImpl<P>& encoder, const Block& block)
-{
-  return encoder.appendByteArray(block.wire(), block.size());
-}
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-
-inline size_t
-EncodingImpl<encoding::Buffer>::size() const
-{
-  return m_end - m_begin;
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::capacity() const
-{
-  return m_buffer->size();
-}
-
-inline uint8_t*
-EncodingImpl<encoding::Buffer>::buf()
-{
-  return &(*m_begin);
-}
-
-inline const uint8_t*
-EncodingImpl<encoding::Buffer>::buf() const
-{
-  return &(*m_begin);
-}
-
-inline Block
-EncodingImpl<encoding::Buffer>::block(bool verifyLength/* = true*/) const
-{
-  return Block(m_buffer,
-               m_begin, m_end,
-               verifyLength);
-}
-
-inline void
-EncodingImpl<encoding::Buffer>::resize(size_t size, bool addInFront)
-{
-  if (addInFront)
-    {
-      size_t diff_end = m_buffer->end() - m_end;
-      size_t diff_begin = m_buffer->end() - m_begin;
-
-      Buffer* buf = new Buffer(size);
-      std::copy_backward(m_buffer->begin(), m_buffer->end(), buf->end());
-
-      m_buffer.reset(buf);
-
-      m_end = m_buffer->end() - diff_end;
-      m_begin = m_buffer->end() - diff_begin;
-    }
-  else
-    {
-      size_t diff_end = m_end - m_buffer->begin();
-      size_t diff_begin = m_begin - m_buffer->begin();
-
-      Buffer* buf = new Buffer(size);
-      std::copy(m_buffer->begin(), m_buffer->end(), buf->begin());
-
-      m_buffer.reset(buf);
-
-      m_end = m_buffer->begin() + diff_end;
-      m_begin = m_buffer->begin() + diff_begin;
-    }
-}
-
-inline Buffer::iterator
-EncodingImpl<encoding::Buffer>::begin()
-{
-  return m_begin;
-}
-
-inline Buffer::iterator
-EncodingImpl<encoding::Buffer>::end()
-{
-  return m_end;
-}
-
-inline Buffer::const_iterator
-EncodingImpl<encoding::Buffer>::begin() const
-{
-  return m_begin;
-}
-
-inline Buffer::const_iterator
-EncodingImpl<encoding::Buffer>::end() const
-{
-  return m_end;
-}
-
-
-//////////////////////////////////////////////////////////
-// Prepend to the back of the buffer. Resize if needed. //
-//////////////////////////////////////////////////////////
-
-inline size_t
-EncodingImpl<encoding::Buffer>::prependByte(uint8_t value)
-{
-  if (m_begin == m_buffer->begin())
-    resize(m_buffer->size() * 2, true);
-
-  m_begin--;
-  *m_begin = value;
-  return 1;
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::prependByte(uint8_t value)
-{
-  return 1;
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::prependByteArray(const uint8_t* array, size_t length)
-{
-  if ((m_buffer->begin() + length) > m_begin)
-    resize(m_buffer->size() * 2 + length, true);
-
-  m_begin -= length;
-  std::copy(array, array + length, m_begin);
-  return length;
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::prependByteArray(const uint8_t* array, size_t length)
-{
-  return length;
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::prependNonNegativeInteger(uint64_t varNumber)
-{
-  if (varNumber <= std::numeric_limits<uint8_t>::max()) {
-    return prependByte(static_cast<uint8_t>(varNumber));
-  }
-  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
-    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
-    return prependByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
-  }
-  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
-    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
-    return prependByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
-  }
-  else {
-    uint64_t value = htobe64(varNumber);
-    return prependByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::prependNonNegativeInteger(uint64_t varNumber)
-{
-  if (varNumber <= std::numeric_limits<uint8_t>::max()) {
-    return 1;
-  }
-  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
-    return 2;
-  }
-  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
-    return 4;
-  }
-  else {
-    return 8;
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::prependVarNumber(uint64_t varNumber)
-{
-  if (varNumber < 253) {
-    prependByte(static_cast<uint8_t>(varNumber));
-    return 1;
-  }
-  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
-    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
-    prependByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
-    prependByte(253);
-    return 3;
-  }
-  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
-    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
-    prependByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
-    prependByte(254);
-    return 5;
-  }
-  else {
-    uint64_t value = htobe64(varNumber);
-    prependByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
-    prependByte(255);
-    return 9;
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::prependVarNumber(uint64_t varNumber)
-{
-  if (varNumber < 253) {
-    return 1;
-  }
-  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
-    return 3;
-  }
-  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
-    return 5;
-  }
-  else {
-    return 9;
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::prependBlock(const Block& block)
-{
-  if (block.hasWire()) {
-    return prependByteArray(block.wire(), block.size());
-  }
-  else {
-    return prependByteArrayBlock(*this, block.type(), block.value(), block.value_size());
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::prependBlock(const Block& block)
-{
-  if (block.hasWire()) {
-    return block.size();
-  }
-  else {
-    return prependByteArrayBlock(*this, block.type(), block.value(), block.value_size());
-  }
-}
-
-/////////////////////////////////////////////////////////
-// Append to the back of the buffer. Resize if needed. //
-/////////////////////////////////////////////////////////
-
-inline size_t
-EncodingImpl<encoding::Buffer>::appendByte(uint8_t value)
-{
-  if (m_end == m_buffer->end())
-    resize(m_buffer->size() * 2, false);
-
-  *m_end = value;
-  m_end++;
-  return 1;
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::appendByte(uint8_t value)
-{
-  return 1;
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::appendByteArray(const uint8_t* array, size_t length)
-{
-  if ((m_end + length) > m_buffer->end())
-    resize(m_buffer->size() * 2 + length, false);
-
-  std::copy(array, array + length, m_end);
-  m_end += length;
-  return length;
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::appendBlock(const Block& block)
-{
-  if (block.hasWire()) {
-    return appendByteArray(block.wire(), block.size());
-  }
-  else {
-    return appendByteArrayBlock(*this, block.type(), block.value(), block.value_size());
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::appendBlock(const Block& block)
-{
-  if (block.hasWire()) {
-    return block.size();
-  }
-  else {
-    return appendByteArrayBlock(*this, block.type(), block.value(), block.value_size());
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::appendByteArray(const uint8_t* array, size_t length)
-{
-  return prependByteArray(array, length);
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::appendNonNegativeInteger(uint64_t varNumber)
-{
-  if (varNumber <= std::numeric_limits<uint8_t>::max()) {
-    return appendByte(static_cast<uint8_t>(varNumber));
-  }
-  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
-    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
-    return appendByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
-  }
-  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
-    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
-    return appendByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
-  }
-  else {
-    uint64_t value = htobe64(varNumber);
-    return appendByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::appendNonNegativeInteger(uint64_t varNumber)
-{
-  return prependNonNegativeInteger(varNumber);
-}
-
-inline size_t
-EncodingImpl<encoding::Buffer>::appendVarNumber(uint64_t varNumber)
-{
-  if (varNumber < 253) {
-    appendByte(static_cast<uint8_t>(varNumber));
-    return 1;
-  }
-  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
-    appendByte(253);
-    uint16_t value = htobe16(static_cast<uint16_t>(varNumber));
-    appendByteArray(reinterpret_cast<const uint8_t*>(&value), 2);
-    return 3;
-  }
-  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
-    appendByte(254);
-    uint32_t value = htobe32(static_cast<uint32_t>(varNumber));
-    appendByteArray(reinterpret_cast<const uint8_t*>(&value), 4);
-    return 5;
-  }
-  else {
-    appendByte(255);
-    uint64_t value = htobe64(varNumber);
-    appendByteArray(reinterpret_cast<const uint8_t*>(&value), 8);
-    return 9;
-  }
-}
-
-inline size_t
-EncodingImpl<encoding::Estimator>::appendVarNumber(uint64_t varNumber)
-{
-  return prependVarNumber(varNumber);
-}
-
-} // ndn
+} // namespace encoding
+} // namespace ndn
 
 #endif // NDN_ENCODING_ENCODING_BUFFER_HPP
diff --git a/src/encoding/estimator.cpp b/src/encoding/estimator.cpp
new file mode 100644
index 0000000..9ec2336
--- /dev/null
+++ b/src/encoding/estimator.cpp
@@ -0,0 +1,140 @@
+/* -*- 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 "estimator.hpp"
+
+namespace ndn {
+namespace encoding {
+
+Estimator::Estimator(size_t totalReserve, size_t reserveFromBack)
+{
+}
+
+size_t
+Estimator::prependByte(uint8_t value)
+{
+  return 1;
+}
+
+size_t
+Estimator::appendByte(uint8_t value)
+{
+  return 1;
+}
+
+
+size_t
+Estimator::prependByteArray(const uint8_t* array, size_t length)
+{
+  return length;
+}
+
+size_t
+Estimator::appendByteArray(const uint8_t* array, size_t length)
+{
+  return prependByteArray(array, length);
+}
+
+size_t
+Estimator::prependVarNumber(uint64_t varNumber)
+{
+  if (varNumber < 253) {
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    return 3;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    return 5;
+  }
+  else {
+    return 9;
+  }
+}
+
+size_t
+Estimator::appendVarNumber(uint64_t varNumber)
+{
+  return prependVarNumber(varNumber);
+}
+
+
+size_t
+Estimator::prependNonNegativeInteger(uint64_t varNumber)
+{
+  if (varNumber <= std::numeric_limits<uint8_t>::max()) {
+    return 1;
+  }
+  else if (varNumber <= std::numeric_limits<uint16_t>::max()) {
+    return 2;
+  }
+  else if (varNumber <= std::numeric_limits<uint32_t>::max()) {
+    return 4;
+  }
+  else {
+    return 8;
+  }
+}
+
+size_t
+Estimator::appendNonNegativeInteger(uint64_t varNumber)
+{
+  return prependNonNegativeInteger(varNumber);
+}
+
+
+size_t
+Estimator::prependByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize)
+{
+  size_t totalLength = arraySize;
+  totalLength += prependVarNumber(arraySize);
+  totalLength += prependVarNumber(type);
+
+  return totalLength;
+}
+
+size_t
+Estimator::appendByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize)
+{
+  return prependByteArrayBlock(type, array, arraySize);
+}
+
+
+size_t
+Estimator::prependBlock(const Block& block)
+{
+  if (block.hasWire()) {
+    return block.size();
+  }
+  else {
+    return prependByteArrayBlock(block.type(), block.value(), block.value_size());
+  }
+}
+
+size_t
+Estimator::appendBlock(const Block& block)
+{
+  return prependBlock(block);
+}
+
+
+} // namespace encoding
+} // namespace ndn
diff --git a/src/encoding/estimator.hpp b/src/encoding/estimator.hpp
new file mode 100644
index 0000000..19d6423
--- /dev/null
+++ b/src/encoding/estimator.hpp
@@ -0,0 +1,162 @@
+/* -*- 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_ENCODING_ESTIMATOR_HPP
+#define NDN_ENCODING_ESTIMATOR_HPP
+
+#include "../common.hpp"
+#include "block.hpp"
+
+namespace ndn {
+namespace encoding {
+
+/**
+ * @brief Helper class to estimate size of TLV encoding
+ * Interface of this class (mostly) matches interface of Encoder class
+ * @sa Encoder
+ */
+class Estimator
+{
+public: // common interface between Encoder and Estimator
+  /**
+   * @brief Create instance of the estimator
+   * @param totalReserve  not used (for compatibility with the Encoder)
+   * @param totalFromBack not used (for compatibility with the Encoder)
+   */
+  explicit
+  Estimator(size_t totalReserve = 0, size_t reserveFromBack = 0);
+
+  Estimator(const Estimator&) = delete;
+
+  Estimator&
+  operator=(const Estimator&) = delete;
+
+  /**
+   * @brief Prepend a byte
+   */
+  size_t
+  prependByte(uint8_t value);
+
+  /**
+   * @brief Append a byte
+   */
+  size_t
+  appendByte(uint8_t value);
+
+  /**
+   * @brief Prepend a byte array @p array of length @p length
+   */
+  size_t
+  prependByteArray(const uint8_t* array, size_t length);
+
+  /**
+   * @brief Append a byte array @p array of length @p length
+   */
+  size_t
+  appendByteArray(const uint8_t* array, size_t length);
+
+  /**
+   * @brief Prepend range of bytes from the range [@p first, @p last)
+   */
+  template<class Iterator>
+  size_t
+  prependRange(Iterator first, Iterator last);
+
+  /**
+   * @brief Append range of bytes from the range [@p first, @p last)
+   */
+  template<class Iterator>
+  size_t
+  appendRange(Iterator first, Iterator last);
+
+  /**
+   * @brief Prepend VarNumber @p varNumber of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  prependVarNumber(uint64_t varNumber);
+
+  /**
+   * @brief Prepend VarNumber @p varNumber of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  appendVarNumber(uint64_t varNumber);
+
+  /**
+   * @brief Prepend non-negative integer @p integer of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  prependNonNegativeInteger(uint64_t integer);
+
+  /**
+   * @brief Append non-negative integer @p integer of NDN TLV encoding
+   * @sa http://named-data.net/doc/ndn-tlv/
+   */
+  size_t
+  appendNonNegativeInteger(uint64_t integer);
+
+  /**
+   * @brief Prepend TLV block of type @p type and value from buffer @p array of size @p arraySize
+   */
+  size_t
+  prependByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize);
+
+  /**
+   * @brief Append TLV block of type @p type and value from buffer @p array of size @p arraySize
+   */
+  size_t
+  appendByteArrayBlock(uint32_t type, const uint8_t* array, size_t arraySize);
+
+  /**
+   * @brief Prepend TLV block @p block
+   */
+  size_t
+  prependBlock(const Block& block);
+
+  /**
+   * @brief Append TLV block @p block
+   */
+  size_t
+  appendBlock(const Block& block);
+};
+
+
+template<class Iterator>
+inline size_t
+Estimator::prependRange(Iterator first, Iterator last)
+{
+  return std::distance(first, last);
+}
+
+
+template<class Iterator>
+inline size_t
+Estimator::appendRange(Iterator first, Iterator last)
+{
+  return prependRange(first, last);
+}
+
+} // namespace encoding
+} // namespace ndn
+
+#endif // NDN_ENCODING_ESTIMATOR_HPP