encoding: Block::insert

This commit also updates Block::erase to accept element_const_iterator

refs #2998

Change-Id: Ie09c99d14a065444b01abff72fd97a92387b9b91
diff --git a/.waf-tools/compiler-features.py b/.waf-tools/compiler-features.py
index 2500298..098e51f 100644
--- a/.waf-tools/compiler-features.py
+++ b/.waf-tools/compiler-features.py
@@ -70,6 +70,29 @@
                       features='cxx', mandatory=False):
         self.define('HAVE_CXX_OVERRIDE_FINAL', 1)
 
+VECTOR_INSERT_ERASE_CONST_ITERATOR = '''
+#include <vector>
+int
+main()
+{
+  std::vector<int> v;
+  std::vector<int>::const_iterator it = v.cbegin();
+
+  v.insert(it, 2);
+  it = v.cend() - 1;
+  v.erase(it);
+  return 0;
+}
+'''
+
+@conf
+def check_vector_const_iterators(self):
+    if self.check_cxx(msg='Checking for std::vector::insert with const_iterators',
+                      fragment=VECTOR_INSERT_ERASE_CONST_ITERATOR,
+                      features='cxx', mandatory=False):
+        self.define('NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR', 1)
+
 def configure(conf):
     conf.check_friend_typename()
     conf.check_override()
+    conf.check_vector_const_iterators()
diff --git a/src/encoding/block.cpp b/src/encoding/block.cpp
index 4ec1d6b..693bc17 100644
--- a/src/encoding/block.cpp
+++ b/src/encoding/block.cpp
@@ -512,17 +512,33 @@
 }
 
 Block::element_iterator
-Block::erase(Block::element_iterator position)
+Block::erase(Block::element_const_iterator position)
 {
   resetWire();
+
+#ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
   return m_subBlocks.erase(position);
+#else
+  element_iterator it = m_subBlocks.begin();
+  std::advance(it, std::distance(m_subBlocks.cbegin(), position));
+  return m_subBlocks.erase(it);
+#endif
 }
 
 Block::element_iterator
-Block::erase(Block::element_iterator first, Block::element_iterator last)
+Block::erase(Block::element_const_iterator first, Block::element_const_iterator last)
 {
   resetWire();
+
+#ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
   return m_subBlocks.erase(first, last);
+#else
+  element_iterator itStart = m_subBlocks.begin();
+  element_iterator itEnd = m_subBlocks.begin();
+  std::advance(itStart, std::distance(m_subBlocks.cbegin(), first));
+  std::advance(itEnd, std::distance(m_subBlocks.cbegin(), last));
+  return m_subBlocks.erase(itStart, itEnd);
+#endif
 }
 
 void
@@ -532,6 +548,20 @@
   m_subBlocks.push_back(element);
 }
 
+Block::element_iterator
+Block::insert(Block::element_const_iterator pos, const Block& element)
+{
+  resetWire();
+
+#ifdef NDN_CXX_HAVE_VECTOR_INSERT_ERASE_CONST_ITERATOR
+  return m_subBlocks.insert(pos, element);
+#else
+  element_iterator it = m_subBlocks.begin();
+  std::advance(it, std::distance(m_subBlocks.cbegin(), pos));
+  return m_subBlocks.insert(it, element);
+#endif
+}
+
 Block::element_const_iterator
 Block::elements_begin() const
 {
diff --git a/src/encoding/block.hpp b/src/encoding/block.hpp
index 7c45543..9407ccf 100644
--- a/src/encoding/block.hpp
+++ b/src/encoding/block.hpp
@@ -253,14 +253,23 @@
   remove(uint32_t type);
 
   element_iterator
-  erase(element_iterator position);
+  erase(element_const_iterator position);
 
   element_iterator
-  erase(element_iterator first, element_iterator last);
+  erase(element_const_iterator first, element_const_iterator last);
 
   void
   push_back(const Block& element);
 
+  /**
+   * @brief insert Insert a new element in a specific position
+   * @param pos Position to insert the new element
+   * @param element Element to be inserted
+   * @return An iterator that points to the first of the newly inserted elements.
+   */
+  element_iterator
+  insert(element_const_iterator pos, const Block& element);
+
   /** @brief Get all subelements
    */
   const element_container&
diff --git a/tests/unit-tests/encoding/block.t.cpp b/tests/unit-tests/encoding/block.t.cpp
index 62de9aa..38fc2f2 100644
--- a/tests/unit-tests/encoding/block.t.cpp
+++ b/tests/unit-tests/encoding/block.t.cpp
@@ -21,6 +21,7 @@
 
 #include "encoding/encoding-buffer.hpp"
 #include "encoding/buffer-stream.hpp"
+#include "encoding/block-helpers.hpp"
 
 #include "boost-test.hpp"
 
@@ -359,6 +360,123 @@
   BOOST_CHECK_EQUAL(e != f, true);
 }
 
+BOOST_AUTO_TEST_CASE(InsertBeginning)
+{
+  Block masterBlock(tlv::Name);
+  Block firstBlock = makeStringBlock(tlv::NameComponent, "firstName");
+  Block secondBlock = makeStringBlock(tlv::NameComponent, "secondName");
+  Block thirdBlock = makeStringBlock(tlv::NameComponent, "thirdName");
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+  masterBlock.push_back(secondBlock);
+  masterBlock.push_back(thirdBlock);
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+  Block::element_const_iterator it = masterBlock.find(tlv::NameComponent);
+  BOOST_CHECK_EQUAL(*it == secondBlock, true);
+
+  it = masterBlock.insert(it, firstBlock);
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+  BOOST_CHECK_EQUAL(*(it + 1) == secondBlock, true);
+  BOOST_CHECK_EQUAL(*(masterBlock.elements_begin()) == firstBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(InsertEnd)
+{
+  Block masterBlock(tlv::Name);
+  Block firstBlock = makeStringBlock(tlv::NameComponent, "firstName");
+  Block secondBlock = makeStringBlock(tlv::NameComponent, "secondName");
+  Block thirdBlock = makeStringBlock(tlv::NameComponent, "thirdName");
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+  masterBlock.push_back(firstBlock);
+  masterBlock.push_back(secondBlock);
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+  Block::element_const_iterator it = masterBlock.elements_end();
+  BOOST_CHECK_EQUAL(*(it - 1) == secondBlock, true);
+
+  it = masterBlock.insert(it, thirdBlock);
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+  BOOST_CHECK_EQUAL(*(it - 1) == secondBlock, true);
+  BOOST_CHECK_EQUAL(*(masterBlock.elements_end() - 1) == thirdBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(InsertMiddle)
+{
+  Block masterBlock(tlv::Name);
+  Block firstBlock = makeStringBlock(tlv::NameComponent, "firstName");
+  Block secondBlock = makeStringBlock(tlv::NameComponent, "secondName");
+  Block thirdBlock = makeStringBlock(tlv::NameComponent, "thirdName");
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+  masterBlock.push_back(firstBlock);
+  masterBlock.push_back(thirdBlock);
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+  Block::element_const_iterator it = masterBlock.find(tlv::NameComponent);
+  BOOST_CHECK_EQUAL(*it == firstBlock, true);
+
+  it = masterBlock.insert(it+1, secondBlock);
+
+  BOOST_CHECK_EQUAL(*it == secondBlock, true);
+  BOOST_CHECK_EQUAL(*(it + 1) == thirdBlock, true);
+  BOOST_CHECK_EQUAL(*(it - 1) == firstBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(EraseSingleElement)
+{
+  Block masterBlock(tlv::Name);
+  Block firstBlock = makeStringBlock(tlv::NameComponent, "firstName");
+  Block secondBlock = makeStringBlock(tlv::NameComponent, "secondName");
+  Block thirdBlock = makeStringBlock(tlv::NameComponent, "thirdName");
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+  masterBlock.push_back(firstBlock);
+  masterBlock.push_back(secondBlock);
+  masterBlock.push_back(thirdBlock);
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+  Block::element_const_iterator it = masterBlock.find(tlv::NameComponent);
+  it++;
+  BOOST_CHECK_EQUAL(*it == secondBlock, true);
+
+  it = masterBlock.erase(it);
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 2);
+  BOOST_CHECK_EQUAL(*(it) == thirdBlock, true);
+  BOOST_CHECK_EQUAL(*(it - 1) == firstBlock, true);
+}
+
+BOOST_AUTO_TEST_CASE(EraseRange)
+{
+  Block masterBlock(tlv::Name);
+  Block firstBlock = makeStringBlock(tlv::NameComponent, "firstName");
+  Block secondBlock = makeStringBlock(tlv::NameComponent, "secondName");
+  Block thirdBlock = makeStringBlock(tlv::NameComponent, "thirdName");
+  Block fourthBlock = makeStringBlock(tlv::NameComponent, "fourthName");
+  Block fifthBlock = makeStringBlock(tlv::NameComponent, "fifthName");
+  Block sixthBlock = makeStringBlock(tlv::NameComponent, "sixthName");
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 0);
+  masterBlock.push_back(firstBlock);
+  masterBlock.push_back(secondBlock);
+  masterBlock.push_back(thirdBlock);
+  masterBlock.push_back(fourthBlock);
+  masterBlock.push_back(fifthBlock);
+  masterBlock.push_back(sixthBlock);
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 6);
+  Block::element_const_iterator itStart = masterBlock.find(tlv::NameComponent);
+  itStart++;
+  Block::element_const_iterator itEnd = itStart + 3;
+  BOOST_CHECK_EQUAL(*itStart == secondBlock, true);
+  BOOST_CHECK_EQUAL(*itEnd == fifthBlock, true);
+
+  Block::element_const_iterator newIt = masterBlock.erase(itStart, itEnd);
+
+  BOOST_CHECK_EQUAL(masterBlock.elements_size(), 3);
+  BOOST_CHECK_EQUAL(*(newIt) == fifthBlock, true);
+  BOOST_CHECK_EQUAL(*(newIt - 1) == firstBlock, true);
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests