First stab at global refactoring of memory management - the library builds, but unit tests are failing horribly

This commit is contained in:
Pavel Kirienko
2015-10-14 08:29:50 +03:00
parent 0643879922
commit be84897ed6
33 changed files with 203 additions and 756 deletions
+28 -226
View File
@@ -18,9 +18,7 @@ namespace uavcan
/**
* Slow but memory efficient KV container.
*
* KV pairs can be allocated in a static buffer or in the node's memory pool if the static buffer is exhausted.
* When a KV pair 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.
* KV pairs will be allocated in the node's memory pool.
*
* 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).
@@ -32,10 +30,8 @@ namespace uavcan
* Size of Key + Value + padding must not exceed MemPoolBlockSize.
*/
template <typename Key, typename Value>
class UAVCAN_EXPORT MapBase : Noncopyable
class UAVCAN_EXPORT Map : Noncopyable
{
template <typename, typename, unsigned> friend class Map;
public:
struct KVPair
{
@@ -102,16 +98,9 @@ private:
LinkedListRoot<KVGroup> list_;
IPoolAllocator& allocator_;
#if !UAVCAN_TINY
KVPair* const static_;
const unsigned num_static_entries_;
#endif
KVPair* findKey(const Key& key);
#if !UAVCAN_TINY
void optimizeStorage();
#endif
void compact();
struct YesPredicate
@@ -119,30 +108,18 @@ private:
bool operator()(const Key&, const Value&) const { return true; }
};
protected:
#if UAVCAN_TINY
MapBase(IPoolAllocator& allocator)
: allocator_(allocator)
{
UAVCAN_ASSERT(Key() == Key());
}
#else
MapBase(KVPair* static_buf, unsigned num_static_entries, IPoolAllocator& allocator)
: allocator_(allocator)
, static_(static_buf)
, num_static_entries_(num_static_entries)
{
UAVCAN_ASSERT(Key() == Key());
}
#endif
/// Derived class destructor must call clear();
~MapBase()
{
UAVCAN_ASSERT(getSize() == 0);
}
public:
Map(IPoolAllocator& allocator) :
allocator_(allocator)
{
UAVCAN_ASSERT(Key() == Key());
}
~Map()
{
clear();
}
/**
* Returns null pointer if there's no such entry.
*/
@@ -192,69 +169,20 @@ public:
*/
bool isEmpty() const { return find(YesPredicate()) == NULL; }
unsigned getSize() const;
/**
* For testing, do not use directly.
* Complexity is O(N).
*/
unsigned getNumStaticPairs() const;
unsigned getNumDynamicPairs() const;
};
template <typename Key, typename Value, unsigned NumStaticEntries = 0>
class UAVCAN_EXPORT Map : public MapBase<Key, Value>
{
typename MapBase<Key, Value>::KVPair static_[NumStaticEntries];
public:
#if !UAVCAN_TINY
// This instantiation will not be valid in UAVCAN_TINY mode
explicit Map(IPoolAllocator& allocator)
: MapBase<Key, Value>(static_, NumStaticEntries, allocator)
{ }
~Map() { this->clear(); }
#endif // !UAVCAN_TINY
};
template <typename Key, typename Value>
class UAVCAN_EXPORT Map<Key, Value, 0> : public MapBase<Key, Value>
{
public:
explicit Map(IPoolAllocator& allocator)
#if UAVCAN_TINY
: MapBase<Key, Value>(allocator)
#else
: MapBase<Key, Value>(NULL, 0, allocator)
#endif
{ }
~Map() { this->clear(); }
unsigned getSize() const;
};
// ----------------------------------------------------------------------------
/*
* MapBase<>
* Map<>
*/
template <typename Key, typename Value>
typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::findKey(const Key& key)
typename Map<Key, Value>::KVPair* Map<Key, Value>::findKey(const Key& key)
{
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (static_[i].match(key))
{
return static_ + i;
}
}
#endif
KVGroup* p = list_.get();
while (p)
{
@@ -268,64 +196,8 @@ typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::findKey(const Key& ke
return NULL;
}
#if !UAVCAN_TINY
template <typename Key, typename Value>
void MapBase<Key, Value>::optimizeStorage()
{
while (true)
{
// Looking for first EMPTY static entry
KVPair* stat = NULL;
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (static_[i].match(Key()))
{
stat = static_ + i;
break;
}
}
if (stat == NULL)
{
break;
}
// Looking for the first NON-EMPTY dynamic entry, erasing immediately
KVGroup* p = list_.get();
KVPair dyn;
while (p)
{
bool stop = false;
for (int i = 0; i < KVGroup::NumKV; i++)
{
if (!p->kvs[i].match(Key())) // Non empty
{
dyn = p->kvs[i]; // Copy by value
p->kvs[i] = KVPair(); // Erase immediately
stop = true;
break;
}
}
if (stop)
{
break;
}
p = p->getNextListNode();
}
if (dyn.match(Key()))
{
break;
}
// Migrating
*stat = dyn;
}
}
#endif // !UAVCAN_TINY
template <typename Key, typename Value>
void MapBase<Key, Value>::compact()
void Map<Key, Value>::compact()
{
KVGroup* p = list_.get();
while (p)
@@ -350,7 +222,7 @@ void MapBase<Key, Value>::compact()
}
template <typename Key, typename Value>
Value* MapBase<Key, Value>::access(const Key& key)
Value* Map<Key, Value>::access(const Key& key)
{
UAVCAN_ASSERT(!(key == Key()));
KVPair* const kv = findKey(key);
@@ -358,7 +230,7 @@ Value* MapBase<Key, Value>::access(const Key& key)
}
template <typename Key, typename Value>
Value* MapBase<Key, Value>::insert(const Key& key, const Value& value)
Value* Map<Key, Value>::insert(const Key& key, const Value& value)
{
UAVCAN_ASSERT(!(key == Key()));
remove(key);
@@ -381,40 +253,23 @@ Value* MapBase<Key, Value>::insert(const Key& key, const Value& value)
}
template <typename Key, typename Value>
void MapBase<Key, Value>::remove(const Key& key)
void Map<Key, Value>::remove(const Key& key)
{
UAVCAN_ASSERT(!(key == Key()));
KVPair* const kv = findKey(key);
if (kv)
{
*kv = KVPair();
#if !UAVCAN_TINY
optimizeStorage();
#endif
compact();
}
}
template <typename Key, typename Value>
template <typename Predicate>
void MapBase<Key, Value>::removeAllWhere(Predicate predicate)
void Map<Key, Value>::removeAllWhere(Predicate predicate)
{
unsigned num_removed = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
if (predicate(static_[i].key, static_[i].value))
{
num_removed++;
static_[i] = KVPair();
}
}
}
#endif
KVGroup* p = list_.get();
while (p != NULL)
{
@@ -438,30 +293,14 @@ void MapBase<Key, Value>::removeAllWhere(Predicate predicate)
if (num_removed > 0)
{
#if !UAVCAN_TINY
optimizeStorage();
#endif
compact();
}
}
template <typename Key, typename Value>
template <typename Predicate>
const Key* MapBase<Key, Value>::find(Predicate predicate) const
const Key* Map<Key, Value>::find(Predicate predicate) const
{
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
if (predicate(static_[i].key, static_[i].value))
{
return &static_[i].key;
}
}
}
#endif
KVGroup* p = list_.get();
while (p != NULL)
{
@@ -485,29 +324,14 @@ const Key* MapBase<Key, Value>::find(Predicate predicate) const
}
template <typename Key, typename Value>
void MapBase<Key, Value>::clear()
void Map<Key, Value>::clear()
{
removeAllWhere(YesPredicate());
}
template <typename Key, typename Value>
typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned index)
typename Map<Key, Value>::KVPair* Map<Key, Value>::getByIndex(unsigned index)
{
#if !UAVCAN_TINY
// Checking the static storage
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
if (index == 0)
{
return static_ + i;
}
index--;
}
}
#endif
// Slowly crawling through the dynamic storage
KVGroup* p = list_.get();
while (p != NULL)
@@ -534,35 +358,13 @@ typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned i
}
template <typename Key, typename Value>
const typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned index) const
const typename Map<Key, Value>::KVPair* Map<Key, Value>::getByIndex(unsigned index) const
{
return const_cast<MapBase<Key, Value>*>(this)->getByIndex(index);
return const_cast<Map<Key, Value>*>(this)->getByIndex(index);
}
template <typename Key, typename Value>
unsigned MapBase<Key, Value>::getSize() const
{
return getNumStaticPairs() + getNumDynamicPairs();
}
template <typename Key, typename Value>
unsigned MapBase<Key, Value>::getNumStaticPairs() const
{
unsigned num = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < num_static_entries_; i++)
{
if (!static_[i].match(Key()))
{
num++;
}
}
#endif
return num;
}
template <typename Key, typename Value>
unsigned MapBase<Key, Value>::getNumDynamicPairs() const
unsigned Map<Key, Value>::getSize() const
{
unsigned num = 0;
KVGroup* p = list_.get();
+15 -79
View File
@@ -23,11 +23,9 @@ namespace uavcan
* 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.
*
* Number of static entries must not be less than 1.
* Items will be allocated in the node's memory pool.
*/
template <typename T, unsigned NumStaticEntries>
template <typename T>
class UAVCAN_EXPORT Multiset : Noncopyable
{
struct Item : ::uavcan::Noncopyable
@@ -122,7 +120,6 @@ private:
*/
LinkedListRoot<Chunk> list_;
IPoolAllocator& allocator_;
Item static_[NumStaticEntries];
/*
* Methods
@@ -333,13 +330,7 @@ public:
* Counts number of items stored.
* Best case complexity is O(N).
*/
unsigned getSize() const { return getNumStaticItems() + getNumDynamicItems(); }
/**
* For testing, do not use directly.
*/
unsigned getNumStaticItems() const;
unsigned getNumDynamicItems() const;
unsigned getSize() const;
};
// ----------------------------------------------------------------------------
@@ -347,21 +338,10 @@ public:
/*
* Multiset<>
*/
template <typename T, unsigned NumStaticEntries>
typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::findOrCreateFreeSlot()
template <typename T>
typename Multiset<T>::Item* Multiset<T>::findOrCreateFreeSlot()
{
#if !UAVCAN_TINY
// Search in static pool
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (!static_[i].isConstructed())
{
return &static_[i];
}
}
#endif
// Search in dynamic pool
// Search
{
Chunk* p = list_.get();
while (p)
@@ -375,7 +355,7 @@ typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::fin
}
}
// Create new dynamic chunk
// Create new chunk
Chunk* const chunk = Chunk::instantiate(allocator_);
if (chunk == NULL)
{
@@ -385,8 +365,8 @@ typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::fin
return &chunk->items[0];
}
template <typename T, unsigned NumStaticEntries>
void Multiset<T, NumStaticEntries>::compact()
template <typename T>
void Multiset<T>::compact()
{
Chunk* p = list_.get();
while (p)
@@ -410,30 +390,12 @@ void Multiset<T, NumStaticEntries>::compact()
}
}
template <typename T, unsigned NumStaticEntries>
template <typename T>
template <typename Predicate>
void Multiset<T, NumStaticEntries>::removeWhere(Predicate predicate, const RemoveStrategy strategy)
void Multiset<T>::removeWhere(Predicate predicate, const RemoveStrategy strategy)
{
unsigned num_removed = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (static_[i].isConstructed())
{
if (predicate(*static_[i].ptr))
{
num_removed++;
static_[i].destroy();
if (strategy == RemoveOne)
{
break;
}
}
}
}
#endif
Chunk* p = list_.get();
while (p != NULL)
{
@@ -470,23 +432,10 @@ void Multiset<T, NumStaticEntries>::removeWhere(Predicate predicate, const Remov
}
}
template <typename T, unsigned NumStaticEntries>
template <typename T>
template <typename Predicate>
T* Multiset<T, NumStaticEntries>::find(Predicate predicate)
T* Multiset<T>::find(Predicate predicate)
{
#if !UAVCAN_TINY
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (static_[i].isConstructed())
{
if (predicate(*static_[i].ptr))
{
return static_[i].ptr;
}
}
}
#endif
Chunk* p = list_.get();
while (p != NULL)
{
@@ -508,21 +457,8 @@ T* Multiset<T, NumStaticEntries>::find(Predicate predicate)
return NULL;
}
template <typename T, unsigned NumStaticEntries>
unsigned Multiset<T, NumStaticEntries>::getNumStaticItems() const
{
unsigned num = 0;
#if !UAVCAN_TINY
for (unsigned i = 0; i < NumStaticEntries; i++)
{
num += static_[i].isConstructed() ? 1U : 0U;
}
#endif
return num;
}
template <typename T, unsigned NumStaticEntries>
unsigned Multiset<T, NumStaticEntries>::getNumDynamicItems() const
template <typename T>
unsigned Multiset<T>::getSize() const
{
unsigned num = 0;
Chunk* p = list_.get();