ims: prevent memory pool size from becoming zero

refs: #4769

Change-Id: Id1c1dadf40db6f62bd684391066e7ad45ec8dcd1
diff --git a/src/ims/in-memory-storage-fifo.hpp b/src/ims/in-memory-storage-fifo.hpp
index 6991451..bbef50a 100644
--- a/src/ims/in-memory-storage-fifo.hpp
+++ b/src/ims/in-memory-storage-fifo.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -36,10 +36,10 @@
 {
 public:
   explicit
-  InMemoryStorageFifo(size_t limit = 10);
+  InMemoryStorageFifo(size_t limit = 16);
 
   explicit
-  InMemoryStorageFifo(boost::asio::io_service& ioService, size_t limit = 10);
+  InMemoryStorageFifo(boost::asio::io_service& ioService, size_t limit = 16);
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PROTECTED:
   /** @brief Removes one Data packet from in-memory storage based on FIFO
diff --git a/src/ims/in-memory-storage-lfu.hpp b/src/ims/in-memory-storage-lfu.hpp
index 7633cfd..3b0531c 100644
--- a/src/ims/in-memory-storage-lfu.hpp
+++ b/src/ims/in-memory-storage-lfu.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -40,10 +40,10 @@
 {
 public:
   explicit
-  InMemoryStorageLfu(size_t limit = 10);
+  InMemoryStorageLfu(size_t limit = 16);
 
   explicit
-  InMemoryStorageLfu(boost::asio::io_service& ioService, size_t limit = 10);
+  InMemoryStorageLfu(boost::asio::io_service& ioService, size_t limit = 16);
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PROTECTED:
   /** @brief Removes one Data packet from in-memory storage based on LFU, i.e. evict the least
diff --git a/src/ims/in-memory-storage-lru.hpp b/src/ims/in-memory-storage-lru.hpp
index 3dd9dfe..1f15648 100644
--- a/src/ims/in-memory-storage-lru.hpp
+++ b/src/ims/in-memory-storage-lru.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -38,9 +38,10 @@
 {
 public:
   explicit
-  InMemoryStorageLru(size_t limit = 10);
+  InMemoryStorageLru(size_t limit = 16);
 
-  InMemoryStorageLru(boost::asio::io_service& ioService, size_t limit = 10);
+  explicit
+  InMemoryStorageLru(boost::asio::io_service& ioService, size_t limit = 16);
 
 NDN_CXX_PUBLIC_WITH_TESTS_ELSE_PROTECTED:
   /** @brief Removes one Data packet from in-memory storage based on LRU, i.e. evict the least
diff --git a/src/ims/in-memory-storage.cpp b/src/ims/in-memory-storage.cpp
index 3db250d..cf5ca2d 100644
--- a/src/ims/in-memory-storage.cpp
+++ b/src/ims/in-memory-storage.cpp
@@ -100,7 +100,7 @@
 InMemoryStorage::init()
 {
   // TODO consider a more suitable initial value
-  m_capacity = 10;
+  m_capacity = m_initCapacity;
 
   if (m_limit != std::numeric_limits<size_t>::max() && m_capacity > m_limit) {
     m_capacity = m_limit;
@@ -131,7 +131,7 @@
 InMemoryStorage::setCapacity(size_t capacity)
 {
   size_t oldCapacity = m_capacity;
-  m_capacity = capacity;
+  m_capacity = std::max(capacity, m_initCapacity);
 
   if (size() > m_capacity) {
     ssize_t nAllowedFailures = size() - m_capacity;
diff --git a/src/ims/in-memory-storage.hpp b/src/ims/in-memory-storage.hpp
index 740b706..a962de2 100644
--- a/src/ims/in-memory-storage.hpp
+++ b/src/ims/in-memory-storage.hpp
@@ -332,6 +332,8 @@
   Cache m_cache;
   /// user defined maximum capacity of the in-memory storage in packets
   size_t m_limit;
+  /// initial capacity, used as minimum capacity
+  const size_t m_initCapacity = 16;
   /// current capacity of the in-memory storage in packets
   size_t m_capacity;
   /// current number of packets in in-memory storage
diff --git a/tests/unit-tests/ims/in-memory-storage-fifo.t.cpp b/tests/unit-tests/ims/in-memory-storage-fifo.t.cpp
index a873de1..00cde40 100644
--- a/tests/unit-tests/ims/in-memory-storage-fifo.t.cpp
+++ b/tests/unit-tests/ims/in-memory-storage-fifo.t.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /*
- * Copyright (c) 2013-2017 Regents of the University of California.
+ * Copyright (c) 2013-2018 Regents of the University of California.
  *
  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
  *
@@ -73,6 +73,21 @@
   BOOST_CHECK(found2 == nullptr);
 }
 
+BOOST_AUTO_TEST_CASE(MemoryPoolSizeZeroBug) // Bug #4769
+{
+  InMemoryStorageFifo ims;
+
+  BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
+  for (int i = 1; i < 5; ++i) {
+    ims.insert(*makeData(to_string(i)));
+    ims.erase(Name(to_string(i)));
+  }
+
+  BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
+  ims.insert(*makeData("/5"));
+  BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestInMemoryStorageFifo
 BOOST_AUTO_TEST_SUITE_END() // Ims
 
diff --git a/tests/unit-tests/ims/in-memory-storage-persistent.t.cpp b/tests/unit-tests/ims/in-memory-storage-persistent.t.cpp
index 1ae637d..d63061f 100644
--- a/tests/unit-tests/ims/in-memory-storage-persistent.t.cpp
+++ b/tests/unit-tests/ims/in-memory-storage-persistent.t.cpp
@@ -42,20 +42,38 @@
 BOOST_AUTO_TEST_CASE(InsertAndDouble)
 {
   InMemoryStoragePersistent ims;
+  size_t initialCapacity = ims.getCapacity();
 
-  for(int i = 0; i < 11; i++) {
-    std::ostringstream convert;
-    convert << i;
-    Name name("/" + convert.str());
-    shared_ptr<Data> data = makeData(name);
+  for (size_t i = 0; i < initialCapacity + 1; i++) {
+    shared_ptr<Data> data = makeData(to_string(i));
     data->setFreshnessPeriod(5000_ms);
     signData(data);
     ims.insert(*data);
   }
 
-  BOOST_CHECK_EQUAL(ims.size(), 11);
+  BOOST_CHECK_EQUAL(ims.size(), initialCapacity + 1);
 
-  BOOST_CHECK_EQUAL(ims.getCapacity(), 20);
+  BOOST_CHECK_EQUAL(ims.getCapacity(), initialCapacity * 2);
+}
+
+BOOST_AUTO_TEST_CASE(EraseAndShrink)
+{
+  InMemoryStoragePersistent ims;
+
+  auto capacity = ims.getCapacity() * 2;
+  ims.setCapacity(capacity);
+
+  Name name("/1");
+  shared_ptr<Data> data = makeData(name);
+  data->setFreshnessPeriod(5000_ms);
+  signData(data);
+  ims.insert(*data);
+  BOOST_CHECK_EQUAL(ims.size(), 1);
+
+  ims.erase(name);
+
+  BOOST_CHECK_EQUAL(ims.size(), 0);
+  BOOST_CHECK_EQUAL(ims.getCapacity(), capacity / 2);
 }
 
 BOOST_AUTO_TEST_SUITE_END() // TestInMemoryStoragePersistent
diff --git a/tests/unit-tests/ims/in-memory-storage.t.cpp b/tests/unit-tests/ims/in-memory-storage.t.cpp
index 2304889..d955d72 100644
--- a/tests/unit-tests/ims/in-memory-storage.t.cpp
+++ b/tests/unit-tests/ims/in-memory-storage.t.cpp
@@ -266,7 +266,7 @@
   Name name("/c");
   ims.erase(name);
   BOOST_CHECK_EQUAL(ims.size(), 3);
-  BOOST_CHECK_EQUAL(ims.getCapacity(), 5);
+  BOOST_CHECK_EQUAL(ims.getCapacity(), 16);
 }
 
 BOOST_AUTO_TEST_CASE_TEMPLATE(DigestCalculation, T, InMemoryStorages)
@@ -636,14 +636,14 @@
 {
   T ims;
 
-  ims.setCapacity(3);
-  ims.insert(*makeData("/1"));
-  ims.insert(*makeData("/2"));
-  ims.insert(*makeData("/3"));
-  BOOST_CHECK_EQUAL(ims.size(), 3);
+  ims.setCapacity(18);
+  for (int i = 1; i < 19; ++i) {
+    ims.insert(*makeData(to_string(i)));
+  }
+  BOOST_CHECK_EQUAL(ims.size(), 18);
 
-  ims.setCapacity(2);
-  BOOST_CHECK_EQUAL(ims.size(), 2);
+  ims.setCapacity(16);
+  BOOST_CHECK_EQUAL(ims.size(), 16);
 }
 
 BOOST_AUTO_TEST_CASE_TEMPLATE(GetLimit, T, InMemoryStoragesLimited)
@@ -658,19 +658,17 @@
 BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndDouble, T, InMemoryStoragesLimited)
 {
   T ims(40);
+  size_t initialCapacity = ims.getCapacity();
 
-  for (int i = 0; i < 11; i++) {
-    std::ostringstream convert;
-    convert << i;
-    Name name("/" + convert.str());
-    shared_ptr<Data> data = makeData(name);
+  for (size_t i = 0; i < initialCapacity + 1; i++) {
+    shared_ptr<Data> data = makeData(to_string(i));
     data->setFreshnessPeriod(5000_ms);
     signData(data);
     ims.insert(*data);
   }
 
-  BOOST_CHECK_EQUAL(ims.size(), 11);
-  BOOST_CHECK_EQUAL(ims.getCapacity(), 20);
+  BOOST_CHECK_EQUAL(ims.size(), initialCapacity + 1);
+  BOOST_CHECK_EQUAL(ims.getCapacity(), initialCapacity * 2);
 }
 
 BOOST_AUTO_TEST_CASE_TEMPLATE(InsertAndEvict, T, InMemoryStoragesLimited)