mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-23 02:43:33 +08:00
First stab at global refactoring of memory management - the library builds, but unit tests are failing horribly
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user