Non-moving multiset, tests are failing in C++03 mode

This commit is contained in:
Pavel Kirienko
2015-05-15 21:32:08 +03:00
parent 0d85d672c7
commit 048e0a33ee
3 changed files with 238 additions and 285 deletions
+1
View File
@@ -11,6 +11,7 @@
#include <uavcan/build_config.hpp> #include <uavcan/build_config.hpp>
#include <uavcan/dynamic_memory.hpp> #include <uavcan/dynamic_memory.hpp>
#include <uavcan/util/templates.hpp> #include <uavcan/util/templates.hpp>
#include <uavcan/util/placement_new.hpp>
namespace uavcan namespace uavcan
{ {
+197 -243
View File
@@ -11,45 +11,62 @@
#include <uavcan/build_config.hpp> #include <uavcan/build_config.hpp>
#include <uavcan/dynamic_memory.hpp> #include <uavcan/dynamic_memory.hpp>
#include <uavcan/util/templates.hpp> #include <uavcan/util/templates.hpp>
#include <uavcan/util/placement_new.hpp>
#if !defined(UAVCAN_CPP_VERSION) || !defined(UAVCAN_CPP11)
# error UAVCAN_CPP_VERSION
#endif
namespace uavcan namespace uavcan
{ {
/** /**
* Slow but memory efficient unordered set. * Slow but memory efficient unordered multiset. Unlike Map<>, this container does not move objects, so
* they don't have to be copyable.
* *
* Items can be allocated in a static buffer or in the node's memory pool if the static buffer is exhausted. * Items can be allocated in a static buffer or in the node's memory pool if the static buffer is exhausted.
* When an item is deleted from the static buffer, one pair from the memory pool will be moved in the free
* slot of the static buffer, so the use of the memory pool is minimized.
*
* Please be aware that this container does not perform any speed optimizations to minimize memory footprint,
* so the complexity of most operations is O(N).
*
* Type requirements:
* T must be copyable, assignable and default constructible.
* T must implement a comparison operator.
* T's default constructor must initialize the object into invalid state.
* Size of T must not exceed MemPoolBlockSize.
*/ */
template <typename T> template <typename T>
class UAVCAN_EXPORT MultisetBase : Noncopyable class UAVCAN_EXPORT MultisetBase : Noncopyable
{ {
template <typename, unsigned> friend class Multiset;
protected: protected:
/* struct Item : ::uavcan::Noncopyable
* Purpose of this type is to enforce default initialization of T
*/
struct Item
{ {
T value; T* ptr;
Item() : value() { }
Item(const T& v) : value(v) { } #if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
bool operator==(const Item& rhs) const { return rhs.value == value; } alignas(T) unsigned char pool[sizeof(T)]; ///< Memory efficient version
bool operator!=(const Item& rhs) const { return !operator==(rhs); } #else
operator T() const { return value; } union
{
unsigned char pool[sizeof(T)];
long double _aligner1_;
long long _aligner2_;
};
#endif
Item()
: ptr(NULL)
{
fill_n(pool, sizeof(pool), static_cast<unsigned char>(0));
}
~Item() { destroy(); }
bool isConstructed() const { return ptr != NULL; }
void destroy()
{
if (ptr != NULL)
{
ptr->~T();
ptr = NULL;
fill_n(pool, sizeof(pool), static_cast<unsigned char>(0));
}
}
}; };
struct Chunk : LinkedListNode<Chunk> private:
struct Chunk : LinkedListNode<Chunk>, ::uavcan::Noncopyable
{ {
enum { NumItems = (MemPoolBlockSize - sizeof(LinkedListNode<Chunk>)) / sizeof(Item) }; enum { NumItems = (MemPoolBlockSize - sizeof(LinkedListNode<Chunk>)) / sizeof(Item) };
Item items[NumItems]; Item items[NumItems];
@@ -58,7 +75,7 @@ protected:
{ {
StaticAssert<(static_cast<unsigned>(NumItems) > 0)>::check(); StaticAssert<(static_cast<unsigned>(NumItems) > 0)>::check();
IsDynamicallyAllocatable<Chunk>::check(); IsDynamicallyAllocatable<Chunk>::check();
UAVCAN_ASSERT(items[0].value == T()); UAVCAN_ASSERT(!items[0].isConstructed());
} }
static Chunk* instantiate(IPoolAllocator& allocator) static Chunk* instantiate(IPoolAllocator& allocator)
@@ -81,11 +98,11 @@ protected:
} }
} }
Item* find(const Item& item) Item* findFreeSlot()
{ {
for (unsigned i = 0; i < static_cast<unsigned>(NumItems); i++) for (unsigned i = 0; i < static_cast<unsigned>(NumItems); i++)
{ {
if (items[i] == item) if (!items[i].isConstructed())
{ {
return items + i; return items + i;
} }
@@ -94,7 +111,6 @@ protected:
} }
}; };
private:
LinkedListRoot<Chunk> list_; LinkedListRoot<Chunk> list_;
IPoolAllocator& allocator_; IPoolAllocator& allocator_;
#if !UAVCAN_TINY #if !UAVCAN_TINY
@@ -102,11 +118,8 @@ private:
const unsigned num_static_entries_; const unsigned num_static_entries_;
#endif #endif
Item* find(const Item& item); Item* findOrCreateFreeSlot();
#if !UAVCAN_TINY
void optimizeStorage();
#endif
void compact(); void compact();
struct YesPredicate struct YesPredicate
@@ -114,6 +127,20 @@ private:
bool operator()(const T&) const { return true; } bool operator()(const T&) const { return true; }
}; };
struct IndexPredicate : ::uavcan::Noncopyable
{
unsigned index;
IndexPredicate(unsigned target_index) : index(target_index) { }
bool operator()(const T&) { return index--==0; }
};
struct ComparingPredicate
{
const T& reference;
ComparingPredicate(const T& ref) : reference(ref) { }
bool operator()(const T& sample) { return reference == sample; }
};
protected: protected:
#if UAVCAN_TINY #if UAVCAN_TINY
MultisetBase(IPoolAllocator& allocator) MultisetBase(IPoolAllocator& allocator)
@@ -126,9 +153,7 @@ protected:
: allocator_(allocator) : allocator_(allocator)
, static_(static_buf) , static_(static_buf)
, num_static_entries_(num_static_entries) , num_static_entries_(num_static_entries)
{ { }
UAVCAN_ASSERT(Item() == Item());
}
#endif #endif
/// Derived class destructor must call removeAll(); /// Derived class destructor must call removeAll();
@@ -138,17 +163,70 @@ protected:
} }
public: public:
/**
* This is needed for testing.
*/
enum { NumItemsPerDynamicChunk = Chunk::NumItems };
/** /**
* Adds one item and returns a pointer to it. * Adds one item and returns a pointer to it.
* If add fails due to lack of memory, NULL will be returned. * If add fails due to lack of memory, NULL will be returned.
*/ */
T* add(const T& item); T* add()
{
Item* const item = findOrCreateFreeSlot();
if (item == NULL)
{
return NULL;
}
UAVCAN_ASSERT(item->ptr == NULL);
item->ptr = new (item->pool) T();
return item->ptr;
}
template <typename P1>
T* add(P1 p1)
{
Item* const item = findOrCreateFreeSlot();
if (item == NULL)
{
return NULL;
}
UAVCAN_ASSERT(item->ptr == NULL);
item->ptr = new (item->pool) T(p1);
return item->ptr;
}
template <typename P1, typename P2>
T* add(P1 p1, P2 p2)
{
Item* const item = findOrCreateFreeSlot();
if (item == NULL)
{
return NULL;
}
UAVCAN_ASSERT(item->ptr == NULL);
item->ptr = new (item->pool) T(p1, p2);
return item->ptr;
}
template <typename P1, typename P2, typename P3>
T* add(P1 p1, P2 p2, P3 p3)
{
Item* const item = findOrCreateFreeSlot();
if (item == NULL)
{
return NULL;
}
UAVCAN_ASSERT(item->ptr == NULL);
item->ptr = new (item->pool) T(p1, p2, p3);
return item->ptr;
}
/** /**
* Does nothing if there's no such item. * @ref removeMatching()
* Only the first matching item will be removed.
*/ */
void remove(const T& item); enum RemoveStrategy { RemoveOne, RemoveAll };
/** /**
* Removes entries where the predicate returns true. * Removes entries where the predicate returns true.
@@ -156,7 +234,15 @@ public:
* bool (T& item) * bool (T& item)
*/ */
template <typename Predicate> template <typename Predicate>
void removeWhere(Predicate predicate); void removeMatching(Predicate predicate, RemoveStrategy strategy);
template <typename Predicate>
void removeAllMatching(Predicate predicate) { removeMatching<Predicate>(predicate, RemoveAll); }
template <typename Predicate>
void removeFirstMatching(Predicate predicate) { removeMatching<Predicate>(predicate, RemoveOne); }
void removeFirst(const T& ref) { removeFirstMatching(ComparingPredicate(ref)); }
/** /**
* Returns first entry where the predicate returns true. * Returns first entry where the predicate returns true.
@@ -164,28 +250,45 @@ public:
* bool (const T& item) * bool (const T& item)
*/ */
template <typename Predicate> template <typename Predicate>
const T* findFirst(Predicate predicate) const; T* find(Predicate predicate);
template <typename Predicate>
const T* find(Predicate predicate) const
{
return const_cast<MultisetBase<T>*>(this)->find<Predicate>(predicate);
}
/** /**
* Removes all items; all pool memory will be released. * Removes all items; all pool memory will be released.
*/ */
void removeAll(); void removeAll() { removeAllMatching(YesPredicate()); }
/** /**
* Returns an item located at the specified position from the beginning. * Returns an item located at the specified position from the beginning.
* Note that any insertion or deletion may greatly disturb internal ordering, so use with care. * Note that any insertion or deletion may greatly disturb internal ordering, so use with care.
* If index is greater than or equal the number of items, null pointer will be returned. * If index is greater than or equal the number of items, null pointer will be returned.
*/ */
T* getByIndex(unsigned index); T* getByIndex(unsigned index)
const T* getByIndex(unsigned index) const; {
IndexPredicate predicate(index);
return find<IndexPredicate&>(predicate);
}
bool isEmpty() const; const T* getByIndex(unsigned index) const
{
return const_cast<MultisetBase<T>*>(this)->getByIndex(index);
}
/**
* This is O(1)
*/
bool isEmpty() const { return find(YesPredicate()) == NULL; }
/** /**
* Counts number of items stored. * Counts number of items stored.
* Best case complexity is O(N). * Best case complexity is O(N).
*/ */
unsigned getSize() const; unsigned getSize() const { return getNumStaticItems() + getNumDynamicItems(); }
/** /**
* For testing, do not use directly. * For testing, do not use directly.
@@ -207,9 +310,7 @@ public:
// This instantiation will not be valid in UAVCAN_TINY mode // This instantiation will not be valid in UAVCAN_TINY mode
explicit Multiset(IPoolAllocator& allocator) explicit Multiset(IPoolAllocator& allocator)
: MultisetBase<T>(static_, NumStaticEntries, allocator) : MultisetBase<T>(static_, NumStaticEntries, allocator)
{ { }
UAVCAN_ASSERT(static_[0].value == T());
}
~Multiset() { this->removeAll(); } ~Multiset() { this->removeAll(); }
@@ -238,87 +339,42 @@ public:
* MultisetBase<> * MultisetBase<>
*/ */
template <typename T> template <typename T>
typename MultisetBase<T>::Item* MultisetBase<T>::find(const Item& item) typename MultisetBase<T>::Item* MultisetBase<T>::findOrCreateFreeSlot()
{ {
#if !UAVCAN_TINY #if !UAVCAN_TINY
// Search in static pool
for (unsigned i = 0; i < num_static_entries_; i++) for (unsigned i = 0; i < num_static_entries_; i++)
{ {
if (static_[i] == item) if (!static_[i].isConstructed())
{ {
return static_ + i; return &static_[i];
} }
} }
#endif #endif
Chunk* p = list_.get(); // Search in dynamic pool
while (p)
{ {
Item* const dyn = p->find(item);
if (dyn != NULL)
{
return dyn;
}
p = p->getNextListNode();
}
return NULL;
}
#if !UAVCAN_TINY
template <typename T>
void MultisetBase<T>::optimizeStorage()
{
while (true)
{
// Looking for first EMPTY static entry
Item* stat = NULL;
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (static_[i] == Item())
{
stat = static_ + i;
break;
}
}
if (stat == NULL)
{
break;
}
// Looking for the first NON-EMPTY dynamic entry, erasing immediately
Chunk* p = list_.get(); Chunk* p = list_.get();
Item dyn;
UAVCAN_ASSERT(dyn == Item());
while (p) while (p)
{ {
bool stop = false; Item* const dyn = p->findFreeSlot();
for (int i = 0; i < Chunk::NumItems; i++) if (dyn != NULL)
{ {
if (p->items[i] != Item()) // Non empty return dyn;
{
dyn = p->items[i]; // Copy by value
p->items[i] = Item(); // Erase immediately
stop = true;
break;
}
}
if (stop)
{
break;
} }
p = p->getNextListNode(); p = p->getNextListNode();
} }
if (dyn == Item())
{
break;
}
// Migrating
*stat = dyn;
} }
}
#endif // !UAVCAN_TINY // Create new dynamic chunk
Chunk* const chunk = Chunk::instantiate(allocator_);
if (chunk == NULL)
{
return NULL;
}
list_.insert(chunk);
return &chunk->items[0];
}
template <typename T> template <typename T>
void MultisetBase<T>::compact() void MultisetBase<T>::compact()
@@ -330,7 +386,7 @@ void MultisetBase<T>::compact()
bool remove_this = true; bool remove_this = true;
for (int i = 0; i < Chunk::NumItems; i++) for (int i = 0; i < Chunk::NumItems; i++)
{ {
if (p->items[i] != Item()) if (p->items[i].isConstructed())
{ {
remove_this = false; remove_this = false;
break; break;
@@ -345,103 +401,73 @@ void MultisetBase<T>::compact()
} }
} }
template <typename T>
T* MultisetBase<T>::add(const T& value)
{
UAVCAN_ASSERT(!(value == T()));
remove(value);
Item* const item = find(Item());
if (item)
{
*item = Item(value);
return &item->value;
}
Chunk* const itemg = Chunk::instantiate(allocator_);
if (itemg == NULL)
{
return NULL;
}
list_.insert(itemg);
itemg->items[0] = Item(value);
return &itemg->items[0].value;
}
template <typename T>
void MultisetBase<T>::remove(const T& value)
{
UAVCAN_ASSERT(!(value == T()));
Item* const item = find(Item(value));
if (item != NULL)
{
*item = Item();
#if !UAVCAN_TINY
optimizeStorage();
#endif
compact();
}
}
template <typename T> template <typename T>
template <typename Predicate> template <typename Predicate>
void MultisetBase<T>::removeWhere(Predicate predicate) void MultisetBase<T>::removeMatching(Predicate predicate, const RemoveStrategy strategy)
{ {
unsigned num_removed = 0; unsigned num_removed = 0;
#if !UAVCAN_TINY #if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++) for (unsigned i = 0; i < num_static_entries_; i++)
{ {
if (static_[i] != Item()) if (static_[i].isConstructed())
{ {
if (predicate(static_[i].value)) if (predicate(*static_[i].ptr))
{ {
num_removed++; num_removed++;
static_[i] = Item(); static_[i].destroy();
} }
} }
if ((num_removed > 0) && (strategy == RemoveOne))
{
break;
}
} }
#endif #endif
Chunk* p = list_.get(); Chunk* p = list_.get();
while (p) while (p)
{ {
if ((num_removed > 0) && (strategy == RemoveOne))
{
break;
}
for (int i = 0; i < Chunk::NumItems; i++) for (int i = 0; i < Chunk::NumItems; i++)
{ {
const Item* const item = p->items + i; Item& item = p->items[i];
if ((*item) != Item()) if (item.isConstructed())
{ {
if (predicate(item->value)) if (predicate(*item.ptr))
{ {
num_removed++; num_removed++;
p->items[i] = Item(); item.destroy();
} }
} }
} }
p = p->getNextListNode(); p = p->getNextListNode();
} }
if (num_removed > 0) if (num_removed > 0)
{ {
#if !UAVCAN_TINY
optimizeStorage();
#endif
compact(); compact();
} }
} }
template <typename T> template <typename T>
template <typename Predicate> template <typename Predicate>
const T* MultisetBase<T>::findFirst(Predicate predicate) const T* MultisetBase<T>::find(Predicate predicate)
{ {
#if !UAVCAN_TINY #if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++) for (unsigned i = 0; i < num_static_entries_; i++)
{ {
if (static_[i] != Item()) if (static_[i].isConstructed())
{ {
if (predicate(static_[i].value)) if (predicate(*static_[i].ptr))
{ {
return &static_[i].value; return static_[i].ptr;
} }
} }
} }
@@ -452,12 +478,11 @@ const T* MultisetBase<T>::findFirst(Predicate predicate) const
{ {
for (int i = 0; i < Chunk::NumItems; i++) for (int i = 0; i < Chunk::NumItems; i++)
{ {
const Item* const item = p->items + i; if (p->items[i].isConstructed())
if ((*item) != Item())
{ {
if (predicate(item->value)) if (predicate(*p->items[i].ptr))
{ {
return &p->items[i].value; return p->items[i].ptr;
} }
} }
} }
@@ -466,70 +491,6 @@ const T* MultisetBase<T>::findFirst(Predicate predicate) const
return NULL; return NULL;
} }
template <typename T>
void MultisetBase<T>::removeAll()
{
removeWhere(YesPredicate());
}
template <typename T>
T* MultisetBase<T>::getByIndex(unsigned index)
{
#if !UAVCAN_TINY
// Checking the static storage
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (static_[i] != Item())
{
if (index == 0)
{
return &static_[i].value;
}
index--;
}
}
#endif
// Slowly crawling through the dynamic storage
Chunk* p = list_.get();
while (p)
{
for (int i = 0; i < Chunk::NumItems; i++)
{
Item* const item = p->items + i;
if ((*item) != Item())
{
if (index == 0)
{
return &item->value;
}
index--;
}
}
p = p->getNextListNode();
}
return NULL;
}
template <typename T>
const T* MultisetBase<T>::getByIndex(unsigned index) const
{
return const_cast<MultisetBase<T>*>(this)->getByIndex(index);
}
template <typename T>
bool MultisetBase<T>::isEmpty() const
{
return getSize() == 0;
}
template <typename T>
unsigned MultisetBase<T>::getSize() const
{
return getNumStaticItems() + getNumDynamicItems();
}
template <typename T> template <typename T>
unsigned MultisetBase<T>::getNumStaticItems() const unsigned MultisetBase<T>::getNumStaticItems() const
{ {
@@ -537,10 +498,7 @@ unsigned MultisetBase<T>::getNumStaticItems() const
#if !UAVCAN_TINY #if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++) for (unsigned i = 0; i < num_static_entries_; i++)
{ {
if (static_[i] != Item()) num += static_[i].isConstructed() ? 1U : 0U;
{
num++;
}
} }
#endif #endif
return num; return num;
@@ -555,11 +513,7 @@ unsigned MultisetBase<T>::getNumDynamicItems() const
{ {
for (int i = 0; i < Chunk::NumItems; i++) for (int i = 0; i < Chunk::NumItems; i++)
{ {
const Item* const item = p->items + i; num += p->items[i].isConstructed() ? 1U : 0U;
if ((*item) != Item())
{
num++;
}
} }
p = p->getNextListNode(); p = p->getNextListNode();
} }
+40 -42
View File
@@ -44,7 +44,7 @@ TEST(Multiset, Basic)
std::auto_ptr<MultisetType> mset(new MultisetType(poolmgr)); std::auto_ptr<MultisetType> mset(new MultisetType(poolmgr));
// Empty // Empty
mset->remove("foo"); mset->removeFirst("foo");
ASSERT_EQ(0, pool.getNumUsedBlocks()); ASSERT_EQ(0, pool.getNumUsedBlocks());
ASSERT_FALSE(mset->getByIndex(0)); ASSERT_FALSE(mset->getByIndex(0));
ASSERT_FALSE(mset->getByIndex(1)); ASSERT_FALSE(mset->getByIndex(1));
@@ -61,54 +61,57 @@ TEST(Multiset, Basic)
ASSERT_TRUE(*mset->getByIndex(0) == "1"); ASSERT_TRUE(*mset->getByIndex(0) == "1");
ASSERT_TRUE(*mset->getByIndex(1) == "2"); ASSERT_TRUE(*mset->getByIndex(1) == "2");
// Dynamic addion // Dynamic addition
ASSERT_EQ("3", *mset->add("3")); ASSERT_EQ("3", *mset->add("3"));
ASSERT_EQ("3", *mset->getByIndex(2));
ASSERT_EQ(1, pool.getNumUsedBlocks()); ASSERT_EQ(1, pool.getNumUsedBlocks());
ASSERT_EQ("4", *mset->add("4")); ASSERT_EQ("4", *mset->add("4"));
ASSERT_EQ(1, pool.getNumUsedBlocks()); // Assuming that at least 2 items fit one block ASSERT_LE(1, pool.getNumUsedBlocks()); // One or more
ASSERT_EQ(2, mset->getNumStaticItems()); ASSERT_EQ(2, mset->getNumStaticItems());
ASSERT_EQ(2, mset->getNumDynamicItems()); ASSERT_EQ(2, mset->getNumDynamicItems());
// Making sure everything is here // Making sure everything is here
ASSERT_EQ("1", *mset->getByIndex(0)); ASSERT_EQ("1", *mset->getByIndex(0));
ASSERT_EQ("2", *mset->getByIndex(1)); ASSERT_EQ("2", *mset->getByIndex(1));
ASSERT_EQ("3", *mset->getByIndex(2)); // 2 and 3 are not tested because their placement depends on number of items per dynamic block
ASSERT_EQ("4", *mset->getByIndex(3));
ASSERT_FALSE(mset->getByIndex(100)); ASSERT_FALSE(mset->getByIndex(100));
ASSERT_FALSE(mset->getByIndex(4)); ASSERT_FALSE(mset->getByIndex(4));
const std::string data_at_pos2 = *mset->getByIndex(2);
const std::string data_at_pos3 = *mset->getByIndex(3);
// Finding some items // Finding some items
ASSERT_EQ("1", *mset->findFirst(FindPredicate("1"))); ASSERT_EQ("1", *mset->find(FindPredicate("1")));
ASSERT_EQ("2", *mset->findFirst(FindPredicate("2"))); ASSERT_EQ("2", *mset->find(FindPredicate("2")));
ASSERT_EQ("3", *mset->findFirst(FindPredicate("3"))); ASSERT_EQ("3", *mset->find(FindPredicate("3")));
ASSERT_EQ("4", *mset->findFirst(FindPredicate("4"))); ASSERT_EQ("4", *mset->find(FindPredicate("4")));
ASSERT_FALSE(mset->findFirst(FindPredicate("nonexistent"))); ASSERT_FALSE(mset->find(FindPredicate("nonexistent")));
// Removing one static // Removing one static; ordering will be preserved
mset->remove("1"); // One of dynamics now migrates to the static storage mset->removeFirst("1");
mset->remove("foo"); // There's no such thing anyway mset->removeFirst("foo"); // There's no such thing anyway
ASSERT_EQ(1, pool.getNumUsedBlocks()); ASSERT_LE(1, pool.getNumUsedBlocks());
ASSERT_EQ(2, mset->getNumStaticItems()); ASSERT_EQ(1, mset->getNumStaticItems());
ASSERT_EQ(1, mset->getNumDynamicItems()); ASSERT_EQ(2, mset->getNumDynamicItems()); // This container does not move items
// Ordering has not changed - first dynamic entry has moved to the first static slot // Ordering has not changed
ASSERT_EQ("3", *mset->getByIndex(0)); ASSERT_EQ("2", *mset->getByIndex(0)); // Entry "1" was here
ASSERT_EQ("2", *mset->getByIndex(1)); ASSERT_EQ(data_at_pos2, *mset->getByIndex(1));
ASSERT_EQ("4", *mset->getByIndex(2)); ASSERT_EQ(data_at_pos3, *mset->getByIndex(2));
// Removing another static // Removing another static
mset->remove("2"); mset->removeFirst("2");
ASSERT_EQ(2, mset->getNumStaticItems()); ASSERT_EQ(0, mset->getNumStaticItems());
ASSERT_EQ(0, mset->getNumDynamicItems()); ASSERT_EQ(2, mset->getNumDynamicItems());
ASSERT_EQ(0, pool.getNumUsedBlocks()); // No dynamic entries left ASSERT_LE(1, pool.getNumUsedBlocks());
// Adding some new dynamics // Adding some new items
unsigned max_value_integer = 0; unsigned max_value_integer = 0;
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {
const std::string value = toString(i); const std::string value = toString(i);
std::string* res = mset->add(value); // Will override some from the above std::string* res = mset->add(value); // Will NOT override above
if (res == NULL) if (res == NULL)
{ {
ASSERT_LT(2, i); ASSERT_LT(2, i);
@@ -121,25 +124,18 @@ TEST(Multiset, Basic)
max_value_integer = unsigned(i); max_value_integer = unsigned(i);
} }
std::cout << "Max value: " << max_value_integer << std::endl; std::cout << "Max value: " << max_value_integer << std::endl;
ASSERT_LT(4, max_value_integer);
// Making sure there is true OOM // Making sure there is true OOM
ASSERT_EQ(0, pool.getNumFreeBlocks()); ASSERT_EQ(0, pool.getNumFreeBlocks());
ASSERT_FALSE(mset->add("nonexistent")); ASSERT_FALSE(mset->add("nonexistent"));
// Removing odd values - nearly half of them // Removing odd values - nearly half of them
ASSERT_EQ(2, mset->getNumStaticItems()); mset->removeAllMatching(oddValuePredicate);
const unsigned num_dynamics_old = mset->getNumDynamicItems();
mset->removeWhere(oddValuePredicate);
ASSERT_EQ(2, mset->getNumStaticItems());
const unsigned num_dynamics_new = mset->getNumDynamicItems();
std::cout << "Num of dynamic pairs reduced from " << num_dynamics_old << " to " << num_dynamics_new << std::endl;
ASSERT_LT(num_dynamics_new, num_dynamics_old);
// Making sure there's no odd values left // Making sure there's no odd values left
for (unsigned kv_int = 0; kv_int <= max_value_integer; kv_int++) for (unsigned kv_int = 0; kv_int <= max_value_integer; kv_int++)
{ {
const std::string* val = mset->findFirst(FindPredicate(toString(kv_int))); const std::string* val = mset->find(FindPredicate(toString(kv_int)));
if (val) if (val)
{ {
ASSERT_FALSE(kv_int & 1); ASSERT_FALSE(kv_int & 1);
@@ -160,16 +156,17 @@ TEST(Multiset, NoStatic)
{ {
using uavcan::Multiset; using uavcan::Multiset;
static const int POOL_BLOCKS = 3; uavcan::PoolAllocator<1024, 128> pool; // Large enough to keep everything
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
uavcan::PoolManager<2> poolmgr; uavcan::PoolManager<2> poolmgr;
poolmgr.addPool(&pool); poolmgr.addPool(&pool);
typedef Multiset<std::string> MultisetType; typedef Multiset<std::string> MultisetType;
std::auto_ptr<MultisetType> mset(new MultisetType(poolmgr)); std::auto_ptr<MultisetType> mset(new MultisetType(poolmgr));
ASSERT_LE(2, MultisetType::NumItemsPerDynamicChunk);
// Empty // Empty
mset->remove("foo"); mset->removeFirst("foo");
ASSERT_EQ(0, pool.getNumUsedBlocks()); ASSERT_EQ(0, pool.getNumUsedBlocks());
ASSERT_FALSE(mset->getByIndex(0)); ASSERT_FALSE(mset->getByIndex(0));
@@ -192,16 +189,17 @@ TEST(Multiset, PrimitiveKey)
{ {
using uavcan::Multiset; using uavcan::Multiset;
static const int POOL_BLOCKS = 3; uavcan::PoolAllocator<1024, 128> pool; // Large enough to keep everything
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
uavcan::PoolManager<2> poolmgr; uavcan::PoolManager<2> poolmgr;
poolmgr.addPool(&pool); poolmgr.addPool(&pool);
typedef Multiset<short, 2> MultisetType; typedef Multiset<int, 2> MultisetType;
std::auto_ptr<MultisetType> mset(new MultisetType(poolmgr)); std::auto_ptr<MultisetType> mset(new MultisetType(poolmgr));
ASSERT_LE(2, MultisetType::NumItemsPerDynamicChunk);
// Empty // Empty
mset->remove(8); mset->removeFirst(8);
ASSERT_EQ(0, pool.getNumUsedBlocks()); ASSERT_EQ(0, pool.getNumUsedBlocks());
ASSERT_EQ(0, mset->getSize()); ASSERT_EQ(0, mset->getSize());
ASSERT_FALSE(mset->getByIndex(0)); ASSERT_FALSE(mset->getByIndex(0));