mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-20 07:43:33 +08:00
Non-moving multiset, tests are failing in C++03 mode
This commit is contained in:
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
Reference in New Issue
Block a user