encoding: Optimized encoding of Interest and related data structures

Change-Id: I0609b40565835568e09c3cc0330db441fd9243b6
refs: #1172
diff --git a/src/selectors.hpp b/src/selectors.hpp
new file mode 100644
index 0000000..19a996b
--- /dev/null
+++ b/src/selectors.hpp
@@ -0,0 +1,294 @@
+/* -*- 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.
+ */
+
+#ifndef NDN_SELECTORS_HPP
+#define NDN_SELECTORS_HPP
+
+#include "common.hpp"
+#include "exclude.hpp"
+#include "encoding/encoding-buffer.hpp"
+
+namespace ndn {
+  
+/**
+ * @brief Abstraction implementing Interest selectors
+ */
+class Selectors
+{
+public:    
+  Selectors() 
+  : m_minSuffixComponents(-1)
+  , m_maxSuffixComponents(-1)
+  , m_childSelector(-1)
+  , m_mustBeFresh(false)
+  {
+  }
+
+  Selectors(int minSuffixComponents, int maxSuffixComponents, 
+            const Exclude& exclude,
+            int childSelector,
+            bool mustBeFresh) 
+    : m_minSuffixComponents(minSuffixComponents)
+    , m_maxSuffixComponents(maxSuffixComponents)
+    , m_exclude(exclude)
+    , m_childSelector(childSelector)
+    , m_mustBeFresh(mustBeFresh)
+  {
+  }
+  
+  Selectors(const Block& wire) 
+  {
+    wireDecode(wire);
+  }
+
+  bool
+  empty() const;
+    
+  /**
+   * @brief Fast encoding or block size estimation
+   */
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T> &block) const;
+  
+  /**
+   * @brief Encode to a wire format
+   */
+  const Block&
+  wireEncode() const;
+
+  /**
+   * @brief Decode the input from wire format
+   */
+  void 
+  wireDecode(const Block &wire);
+
+  ///////////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////////
+  ///////////////////////////////////////////////////////////////////////////////
+  
+  int 
+  getMinSuffixComponents() const
+  {
+    return m_minSuffixComponents;
+  }
+  
+  Selectors& 
+  setMinSuffixComponents(int minSuffixComponents)
+  {
+    m_minSuffixComponents = minSuffixComponents;
+    wire_.reset();
+    return *this;
+  }
+
+  //
+  
+  int 
+  getMaxSuffixComponents() const
+  {
+    return m_maxSuffixComponents;
+  }
+
+  Selectors& 
+  setMaxSuffixComponents(int maxSuffixComponents)
+  {
+    m_maxSuffixComponents = maxSuffixComponents;
+    wire_.reset();
+    return *this;
+  }
+  
+  //
+
+  const Exclude& 
+  getExclude() const
+  {
+    return m_exclude;
+  }
+
+  Selectors& 
+  setExclude(const Exclude& exclude)
+  {
+    m_exclude = exclude;
+    wire_.reset();
+    return *this;
+  }
+
+  //
+  
+  int 
+  getChildSelector() const
+  {
+    return m_childSelector;
+  }
+
+  Selectors& 
+  setChildSelector(int childSelector)
+  {
+    m_childSelector = childSelector;
+    wire_.reset();
+    return *this;
+  }
+
+  //
+
+  int 
+  getMustBeFresh() const
+  {
+    return m_mustBeFresh;
+  }
+
+  Selectors& 
+  setMustBeFresh(bool mustBeFresh)
+  {
+    m_mustBeFresh = mustBeFresh;
+    wire_.reset();
+    return *this;
+  }
+
+private:
+  int m_minSuffixComponents;
+  int m_maxSuffixComponents;  
+  Exclude m_exclude;
+  int m_childSelector;
+  bool m_mustBeFresh;
+
+  mutable Block wire_;
+};
+
+inline bool
+Selectors::empty() const
+{
+  return
+    (m_minSuffixComponents < 0 &&
+     m_maxSuffixComponents < 0 &&
+     m_exclude.empty() &&
+     m_childSelector < 0 &&
+     !m_mustBeFresh);
+}
+
+template<bool T>
+inline size_t
+Selectors::wireEncode(EncodingImpl<T> &block) const
+{
+  size_t total_len = 0;
+
+  // Selectors ::= SELECTORS-TYPE TLV-LENGTH
+  //                 MinSuffixComponents?
+  //                 MaxSuffixComponents?
+  //                 PublisherPublicKeyLocator?
+  //                 Exclude?
+  //                 ChildSelector?
+  //                 MustBeFresh?  
+
+  // (reverse encoding)
+
+  // MustBeFresh
+  if (getMustBeFresh())
+    {
+      total_len += prependBooleanBlock(block, Tlv::MustBeFresh);
+    }
+
+  // ChildSelector
+  if (getChildSelector() >= 0)
+    {
+      total_len += prependNonNegativeIntegerBlock(block, Tlv::ChildSelector, getChildSelector());
+    }
+
+  // Exclude
+  if (!getExclude().empty())
+    {
+      total_len += getExclude().wireEncode(block);
+    }
+  
+  // PublisherPublicKeyLocator
+  /// @todo Implement PublisherPublicKeyLocator selector
+  
+  // MaxSuffixComponents
+  if (getMaxSuffixComponents() >= 0)
+    {
+      total_len += prependNonNegativeIntegerBlock(block, Tlv::MaxSuffixComponents,
+                                                  getMaxSuffixComponents());
+    }
+
+  // MinSuffixComponents
+  if (getMinSuffixComponents() >= 0)
+    {
+      total_len += prependNonNegativeIntegerBlock(block, Tlv::MinSuffixComponents,
+                                                  getMinSuffixComponents());
+    }
+  
+  total_len += block.prependVarNumber(total_len);
+  total_len += block.prependVarNumber(Tlv::Selectors);
+  return total_len;
+}
+
+inline const Block &
+Selectors::wireEncode() const
+{
+  if (wire_.hasWire())
+    return wire_;
+
+  EncodingEstimator estimator;
+  size_t estimatedSize = wireEncode(estimator);
+  
+  EncodingBuffer buffer(estimatedSize, 0);
+  wireEncode(buffer);
+
+  wire_ = buffer.block();
+  return wire_;
+}
+
+inline void
+Selectors::wireDecode(const Block &wire) 
+{
+  if (wire.type() != Tlv::Selectors)
+    throw Tlv::Error("Unexpected TLV type when decoding Selectors");
+
+  *this = Selectors();
+  
+  wire_ = wire;
+  wire_.parse();
+
+  // MinSuffixComponents
+  Block::element_const_iterator val = wire_.find(Tlv::MinSuffixComponents);
+  if (val != wire_.elements_end())
+    {
+      m_minSuffixComponents = readNonNegativeInteger(*val);
+    }
+
+  // MaxSuffixComponents
+  val = wire_.find(Tlv::MaxSuffixComponents);
+  if (val != wire_.elements_end())
+    {
+      m_maxSuffixComponents = readNonNegativeInteger(*val);
+    }
+
+  // Exclude
+  val = wire_.find(Tlv::Exclude);
+  if (val != wire_.elements_end())
+    {
+      m_exclude.wireDecode(*val);
+    }
+
+  // ChildSelector
+  val = wire_.find(Tlv::ChildSelector);
+  if (val != wire_.elements_end())
+    {
+      m_childSelector = readNonNegativeInteger(*val);
+    }
+
+  //MustBeFresh aka AnswerOriginKind
+  val = wire_.find(Tlv::MustBeFresh);
+  if (val != wire_.elements_end())
+    {
+      m_mustBeFresh = true;
+    }
+}
+  
+} // namespace ndn
+
+#endif // NDN_SELECTORS_HPP