From ab4c5f54a6d0516dedae8e44ac5f830437c3b617 Mon Sep 17 00:00:00 2001 From: Pavel Kirienko Date: Mon, 23 Mar 2015 04:27:59 +0300 Subject: [PATCH] Map<>::getByIndex() --- libuavcan/include/uavcan/util/map.hpp | 70 +++++++++++++++++++++++++-- libuavcan/test/util/map.cpp | 29 ++++++++++- 2 files changed, 94 insertions(+), 5 deletions(-) diff --git a/libuavcan/include/uavcan/util/map.hpp b/libuavcan/include/uavcan/util/map.hpp index d72ca18578..f387f51fbb 100644 --- a/libuavcan/include/uavcan/util/map.hpp +++ b/libuavcan/include/uavcan/util/map.hpp @@ -36,6 +36,7 @@ class UAVCAN_EXPORT MapBase : Noncopyable template friend class Map; UAVCAN_PACKED_BEGIN +public: struct KVPair { Key key; @@ -48,6 +49,7 @@ class UAVCAN_EXPORT MapBase : Noncopyable bool match(const Key& rhs) const { return rhs == key; } }; +private: struct KVGroup : LinkedListNode { enum { NumKV = (MemPoolBlockSize - sizeof(LinkedListNode)) / sizeof(KVPair) }; @@ -166,12 +168,22 @@ public: void removeAll(); + /** + * Returns a key-value pair located at the specified position from the beginning. + * 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 pairs, null pointer will be returned. + */ + KVPair* getByIndex(unsigned index); + const KVPair* getByIndex(unsigned index) const; + bool isEmpty() const; - /// For testing - unsigned getNumStaticPairs() const; + unsigned getSize() const; - /// For testing + /** + * For testing, do not use directly. + */ + unsigned getNumStaticPairs() const; unsigned getNumDynamicPairs() const; }; @@ -458,10 +470,60 @@ void MapBase::removeAll() removeWhere(YesPredicate()); } +template +typename MapBase::KVPair* MapBase::getByIndex(unsigned index) +{ + // 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--; + } + } + + // Slowly crawling through the dynamic storage + KVGroup* p = list_.get(); + while (p) + { + for (int i = 0; i < KVGroup::NumKV; i++) + { + KVPair* const kv = p->kvs + i; + if (!kv->match(Key())) + { + if (index == 0) + { + return kv; + } + index--; + } + } + p = p->getNextListNode(); + } + + return NULL; +} + +template +const typename MapBase::KVPair* MapBase::getByIndex(unsigned index) const +{ + return const_cast*>(this)->getByIndex(index); +} + template bool MapBase::isEmpty() const { - return (getNumStaticPairs() == 0) && (getNumDynamicPairs() == 0); + return getSize() == 0; +} + +template +unsigned MapBase::getSize() const +{ + return getNumStaticPairs() + getNumDynamicPairs(); } template diff --git a/libuavcan/test/util/map.cpp b/libuavcan/test/util/map.cpp index 574ebeb40a..2a0eba20a4 100644 --- a/libuavcan/test/util/map.cpp +++ b/libuavcan/test/util/map.cpp @@ -55,6 +55,9 @@ TEST(Map, Basic) ASSERT_FALSE(map->access("hi")); map->remove("foo"); ASSERT_EQ(0, pool.getNumUsedBlocks()); + ASSERT_FALSE(map->getByIndex(0)); + ASSERT_FALSE(map->getByIndex(1)); + ASSERT_FALSE(map->getByIndex(10000)); // Static insertion ASSERT_EQ("a", *map->insert("1", "a")); @@ -63,6 +66,10 @@ TEST(Map, Basic) ASSERT_EQ(2, map->getNumStaticPairs()); ASSERT_EQ(0, map->getNumDynamicPairs()); + // Ordering + ASSERT_TRUE(map->getByIndex(0)->match("1")); + ASSERT_TRUE(map->getByIndex(1)->match("2")); + // Dynamic insertion ASSERT_EQ("c", *map->insert("3", "c")); ASSERT_EQ(1, pool.getNumUsedBlocks()); @@ -79,6 +86,14 @@ TEST(Map, Basic) ASSERT_EQ("d", *map->access("4")); ASSERT_FALSE(map->access("hi")); + // Ordering + ASSERT_TRUE(map->getByIndex(0)->match("1")); + ASSERT_TRUE(map->getByIndex(1)->match("2")); + ASSERT_TRUE(map->getByIndex(2)->match("3")); + ASSERT_TRUE(map->getByIndex(3)->match("4")); + ASSERT_FALSE(map->getByIndex(4)); + ASSERT_FALSE(map->getByIndex(1000)); + // Modifying existing entries *map->access("1") = "A"; *map->access("2") = "B"; @@ -115,6 +130,11 @@ TEST(Map, Basic) ASSERT_EQ("C", *map->access("3")); ASSERT_EQ("D", *map->access("4")); + // Ordering has not changed - first dynamic entry has moved to the first static slot + ASSERT_TRUE(map->getByIndex(0)->match("3")); + ASSERT_TRUE(map->getByIndex(1)->match("2")); + ASSERT_TRUE(map->getByIndex(2)->match("4")); + // Removing another static map->remove("2"); ASSERT_EQ(2, map->getNumStaticPairs()); @@ -198,11 +218,18 @@ TEST(Map, NoStatic) ASSERT_FALSE(map->access("hi")); map->remove("foo"); ASSERT_EQ(0, pool.getNumUsedBlocks()); + ASSERT_FALSE(map->getByIndex(0)); - // Static insertion + // Insertion ASSERT_EQ("a", *map->insert("1", "a")); ASSERT_EQ("b", *map->insert("2", "b")); ASSERT_EQ(1, pool.getNumUsedBlocks()); ASSERT_EQ(0, map->getNumStaticPairs()); ASSERT_EQ(2, map->getNumDynamicPairs()); + + // Ordering + ASSERT_TRUE(map->getByIndex(0)->match("1")); + ASSERT_TRUE(map->getByIndex(1)->match("2")); + ASSERT_FALSE(map->getByIndex(3)); + ASSERT_FALSE(map->getByIndex(1000)); }