Much space optimized Map<> container - saves 40kb of Flash for STM32 test (-O1)

This commit is contained in:
Pavel Kirienko
2014-04-08 18:43:40 +04:00
parent 20aa6e3056
commit a13e4de58a
+262 -216
View File
@@ -86,103 +86,11 @@ class UAVCAN_EXPORT Map : Noncopyable
IAllocator& allocator_; IAllocator& allocator_;
KVPair static_[NumStaticEntries]; KVPair static_[NumStaticEntries];
KVPair* find(const Key& key) KVPair* find(const Key& key);
{
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (static_[i].match(key))
{
return static_ + i;
}
}
KVGroup* p = list_.get(); void optimizeStorage();
while (p)
{
KVPair* const kv = p->find(key);
if (kv)
{
return kv;
}
p = p->getNextListNode();
}
return NULL;
}
void optimizeStorage() void compact();
{
while (true)
{
// Looking for first EMPTY static entry
KVPair* stat = NULL;
for (unsigned i = 0; i < NumStaticEntries; 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;
}
}
void compact()
{
KVGroup* p = list_.get();
while (p)
{
KVGroup* const next = p->getNextListNode();
bool remove_this = true;
for (int i = 0; i < KVGroup::NumKV; i++)
{
if (!p->kvs[i].match(Key()))
{
remove_this = false;
break;
}
}
if (remove_this)
{
list_.remove(p);
KVGroup::destroy(p, allocator_);
}
p = next;
}
}
struct YesPredicate struct YesPredicate
{ {
@@ -202,47 +110,12 @@ public:
~Map() { removeAll(); } ~Map() { removeAll(); }
Value* access(const Key& key) Value* access(const Key& key);
{
assert(!(key == Key()));
KVPair* const kv = find(key);
return kv ? &kv->value : NULL;
}
/// If entry with the same key already exists, it will be replaced /// If entry with the same key already exists, it will be replaced
Value* insert(const Key& key, const Value& value) Value* insert(const Key& key, const Value& value);
{
assert(!(key == Key()));
remove(key);
KVPair* const kv = find(Key()); void remove(const Key& key);
if (kv)
{
*kv = KVPair(key, value);
return &kv->value;
}
KVGroup* const kvg = KVGroup::instantiate(allocator_);
if (kvg == NULL)
{
return NULL;
}
list_.insert(kvg);
kvg->kvs[0] = KVPair(key, value);
return &kvg->kvs[0].value;
}
void remove(const Key& key)
{
assert(!(key == Key()));
KVPair* const kv = find(key);
if (kv)
{
*kv = KVPair();
optimizeStorage();
compact();
}
}
/** /**
* Remove entries where predicate returns true. * Remove entries where predicate returns true.
@@ -250,120 +123,293 @@ public:
* bool (const Key& key, const Value& value) * bool (const Key& key, const Value& value)
*/ */
template <typename Predicate> template <typename Predicate>
void removeWhere(Predicate predicate) void removeWhere(Predicate predicate);
{
unsigned num_removed = 0;
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (!static_[i].match(Key()))
{
if (predicate(static_[i].key, static_[i].value))
{
num_removed++;
static_[i] = KVPair();
}
}
}
KVGroup* p = list_.get();
while (p)
{
for (int i = 0; i < KVGroup::NumKV; i++)
{
const KVPair* const kv = p->kvs + i;
if (!kv->match(Key()))
{
if (predicate(kv->key, kv->value))
{
num_removed++;
p->kvs[i] = KVPair();
}
}
}
p = p->getNextListNode();
}
if (num_removed > 0)
{
optimizeStorage();
compact();
}
}
template <typename Predicate> template <typename Predicate>
const Key* findFirstKey(Predicate predicate) const const Key* findFirstKey(Predicate predicate) const;
void removeAll();
bool isEmpty() const;
/// For testing
unsigned getNumStaticPairs() const;
/// For testing
unsigned getNumDynamicPairs() const;
};
// ----------------------------------------------------------------------------
/*
* Map<>
*/
template <typename Key, typename Value, unsigned NumStaticEntries>
typename Map<Key, Value, NumStaticEntries>::KVPair* Map<Key, Value, NumStaticEntries>::find(const Key& key)
{
for (unsigned i = 0; i < NumStaticEntries; i++)
{ {
if (static_[i].match(key))
{
return static_ + i;
}
}
KVGroup* p = list_.get();
while (p)
{
KVPair* const kv = p->find(key);
if (kv)
{
return kv;
}
p = p->getNextListNode();
}
return NULL;
}
template <typename Key, typename Value, unsigned NumStaticEntries>
void Map<Key, Value, NumStaticEntries>::optimizeStorage()
{
while (true)
{
// Looking for first EMPTY static entry
KVPair* stat = NULL;
for (unsigned i = 0; i < NumStaticEntries; i++) for (unsigned i = 0; i < NumStaticEntries; i++)
{ {
if (!static_[i].match(Key())) if (static_[i].match(Key()))
{ {
if (predicate(static_[i].key, static_[i].value)) stat = static_ + i;
{ break;
return &static_[i].key;
}
} }
} }
if (stat == NULL)
{
break;
}
// Looking for the first NON-EMPTY dynamic entry, erasing immediately
KVGroup* p = list_.get(); KVGroup* p = list_.get();
KVPair dyn;
while (p) while (p)
{ {
bool stop = false;
for (int i = 0; i < KVGroup::NumKV; i++) for (int i = 0; i < KVGroup::NumKV; i++)
{ {
const KVPair* const kv = p->kvs + i; if (!p->kvs[i].match(Key())) // Non empty
if (!kv->match(Key()))
{ {
if (predicate(kv->key, kv->value)) dyn = p->kvs[i]; // Copy by value
{ p->kvs[i] = KVPair(); // Erase immediately
return &p->kvs[i].key; stop = true;
} break;
} }
} }
if (stop)
{
break;
}
p = p->getNextListNode(); p = p->getNextListNode();
} }
if (dyn.match(Key()))
{
break;
}
// Migrating
*stat = dyn;
}
}
template <typename Key, typename Value, unsigned NumStaticEntries>
void Map<Key, Value, NumStaticEntries>::compact()
{
KVGroup* p = list_.get();
while (p)
{
KVGroup* const next = p->getNextListNode();
bool remove_this = true;
for (int i = 0; i < KVGroup::NumKV; i++)
{
if (!p->kvs[i].match(Key()))
{
remove_this = false;
break;
}
}
if (remove_this)
{
list_.remove(p);
KVGroup::destroy(p, allocator_);
}
p = next;
}
}
template <typename Key, typename Value, unsigned NumStaticEntries>
Value* Map<Key, Value, NumStaticEntries>::access(const Key& key)
{
assert(!(key == Key()));
KVPair* const kv = find(key);
return kv ? &kv->value : NULL;
}
template <typename Key, typename Value, unsigned NumStaticEntries>
Value* Map<Key, Value, NumStaticEntries>::insert(const Key& key, const Value& value)
{
assert(!(key == Key()));
remove(key);
KVPair* const kv = find(Key());
if (kv)
{
*kv = KVPair(key, value);
return &kv->value;
}
KVGroup* const kvg = KVGroup::instantiate(allocator_);
if (kvg == NULL)
{
return NULL; return NULL;
} }
list_.insert(kvg);
kvg->kvs[0] = KVPair(key, value);
return &kvg->kvs[0].value;
}
void removeAll() template <typename Key, typename Value, unsigned NumStaticEntries>
void Map<Key, Value, NumStaticEntries>::remove(const Key& key)
{
assert(!(key == Key()));
KVPair* const kv = find(key);
if (kv)
{ {
removeWhere(YesPredicate()); *kv = KVPair();
optimizeStorage();
compact();
}
}
template <typename Key, typename Value, unsigned NumStaticEntries>
template <typename Predicate>
void Map<Key, Value, NumStaticEntries>::removeWhere(Predicate predicate)
{
unsigned num_removed = 0;
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (!static_[i].match(Key()))
{
if (predicate(static_[i].key, static_[i].value))
{
num_removed++;
static_[i] = KVPair();
}
}
} }
bool isEmpty() const { return (getNumStaticPairs() == 0) && (getNumDynamicPairs() == 0); } KVGroup* p = list_.get();
while (p)
/// For testing
unsigned getNumStaticPairs() const
{ {
unsigned num = 0; for (int i = 0; i < KVGroup::NumKV; i++)
for (unsigned i = 0; i < NumStaticEntries; i++)
{ {
if (!static_[i].match(Key())) const KVPair* const kv = p->kvs + i;
if (!kv->match(Key()))
{
if (predicate(kv->key, kv->value))
{
num_removed++;
p->kvs[i] = KVPair();
}
}
}
p = p->getNextListNode();
}
if (num_removed > 0)
{
optimizeStorage();
compact();
}
}
template <typename Key, typename Value, unsigned NumStaticEntries>
template <typename Predicate>
const Key* Map<Key, Value, NumStaticEntries>::findFirstKey(Predicate predicate) const
{
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (!static_[i].match(Key()))
{
if (predicate(static_[i].key, static_[i].value))
{
return &static_[i].key;
}
}
}
KVGroup* p = list_.get();
while (p)
{
for (int i = 0; i < KVGroup::NumKV; i++)
{
const KVPair* const kv = p->kvs + i;
if (!kv->match(Key()))
{
if (predicate(kv->key, kv->value))
{
return &p->kvs[i].key;
}
}
}
p = p->getNextListNode();
}
return NULL;
}
template <typename Key, typename Value, unsigned NumStaticEntries>
void Map<Key, Value, NumStaticEntries>::removeAll()
{
removeWhere(YesPredicate());
}
template <typename Key, typename Value, unsigned NumStaticEntries>
bool Map<Key, Value, NumStaticEntries>::isEmpty() const
{
return (getNumStaticPairs() == 0) && (getNumDynamicPairs() == 0);
}
template <typename Key, typename Value, unsigned NumStaticEntries>
unsigned Map<Key, Value, NumStaticEntries>::getNumStaticPairs() const
{
unsigned num = 0;
for (unsigned i = 0; i < NumStaticEntries; i++)
{
if (!static_[i].match(Key()))
{
num++;
}
}
return num;
}
template <typename Key, typename Value, unsigned NumStaticEntries>
unsigned Map<Key, Value, NumStaticEntries>::getNumDynamicPairs() const
{
unsigned num = 0;
KVGroup* p = list_.get();
while (p)
{
for (int i = 0; i < KVGroup::NumKV; i++)
{
const KVPair* const kv = p->kvs + i;
if (!kv->match(Key()))
{ {
num++; num++;
} }
} }
return num; p = p->getNextListNode();
} }
return num;
/// For testing }
unsigned getNumDynamicPairs() const
{
unsigned num = 0;
KVGroup* p = list_.get();
while (p)
{
for (int i = 0; i < KVGroup::NumKV; i++)
{
const KVPair* const kv = p->kvs + i;
if (!kv->match(Key()))
{
num++;
}
}
p = p->getNextListNode();
}
return num;
}
};
} }