Skip to content

Commit 4b9e20e

Browse files
authored
Merge pull request #4665 from danpoe/refactor/sharing-map
Add sharing map documentation and move unit test sections
2 parents 375e9a8 + 4db3e62 commit 4b9e20e

File tree

2 files changed

+140
-104
lines changed

2 files changed

+140
-104
lines changed

src/util/sharing_map.h

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -535,8 +535,26 @@ class sharing_mapt
535535
return lp;
536536
}
537537

538+
/// Move a container node (containing a single leaf) further down the tree
539+
/// such as to resolve a collision with another key-value pair. This method is
540+
/// called by `insert()` to resolve a collision between a key-value pair to be
541+
/// newly inserted, and a key-value pair existing in the map.
542+
///
543+
/// \param starting_level: the depth of the inner node pointing to the
544+
/// container node with a single leaf
545+
/// \param key_suffix: hash code of the existing key in the map, shifted to
546+
/// the right by `chunk * starting_level` bits (i.e., \p key_suffix is the
547+
/// rest of the hash code used to determine the position of the key-value
548+
/// pair below level \p starting_level
549+
/// \param bit_last: last portion of the hash code of the key existing in the
550+
/// map (`inner[bit_last]` points to the container node to move further down
551+
/// the tree)
552+
/// \param inner: inner node of which the child `inner[bit_last]` is the
553+
/// container node to move further down the tree
554+
/// \return pointer to the container to which the element to be newly inserted
555+
/// can be added
538556
innert *migrate(
539-
const std::size_t i,
557+
const std::size_t starting_level,
540558
const std::size_t key_suffix,
541559
const std::size_t bit_last,
542560
innert &inner);
@@ -545,6 +563,20 @@ class sharing_mapt
545563
const innert &n,
546564
std::function<void(const key_type &k, const mapped_type &m)> f) const;
547565

566+
/// Add a delta item to the delta view if the value in the \p container (which
567+
/// must only contain a single leaf) is not shared with any of the values in
568+
/// the subtree below \p inner. This method is called by `get_delta_view()`
569+
/// when a container containing a single leaf is encountered in the first map,
570+
/// and the corresponding node in the second map is an inner node.
571+
///
572+
/// \param container: container node containing a single leaf, part of the
573+
/// first map in a call `map1.get_delta_view(map2, ...)`
574+
/// \param inner: inner node which is part of the second map
575+
/// \param level: depth of the nodes in the maps (both \p container and \p
576+
/// inner must be at the same depth in their respective maps)
577+
/// \param delta_view: delta view to add delta items to
578+
/// \param only_common: flag indicating if only items are added to the delta
579+
/// view for which the keys are in both maps
548580
void add_item_if_not_shared(
549581
const innert &container,
550582
const innert &inner,
@@ -572,7 +604,7 @@ class sharing_mapt
572604

573605
// derived config
574606
static const std::size_t mask;
575-
static const std::size_t steps;
607+
static const std::size_t levels;
576608

577609
// key-value map
578610
innert map;
@@ -1108,12 +1140,12 @@ SHARING_MAPT(void)::erase(const key_type &k)
11081140
}
11091141

11101142
SHARING_MAPT2(, innert *)::migrate(
1111-
const std::size_t step,
1143+
const std::size_t starting_level,
11121144
const std::size_t key_suffix,
11131145
const std::size_t bit_last,
11141146
innert &inner)
11151147
{
1116-
SM_ASSERT(step < steps - 1);
1148+
SM_ASSERT(starting_level < levels - 1);
11171149
SM_ASSERT(inner.is_defined_internal());
11181150

11191151
const innert &child = *inner.find_child(bit_last);
@@ -1127,7 +1159,7 @@ SHARING_MAPT2(, innert *)::migrate(
11271159
const leaft &leaf = ll.front();
11281160
std::size_t key_existing = hash()(leaf.get_key());
11291161

1130-
key_existing >>= chunk * step;
1162+
key_existing >>= chunk * starting_level;
11311163

11321164
// Copy the container
11331165
innert container_copy(child);
@@ -1141,13 +1173,13 @@ SHARING_MAPT2(, innert *)::migrate(
11411173

11421174
// Find place for both elements
11431175

1144-
std::size_t i = step + 1;
1176+
std::size_t level = starting_level + 1;
11451177
std::size_t key = key_suffix;
11461178

11471179
key_existing >>= chunk;
11481180
key >>= chunk;
11491181

1150-
SM_ASSERT(i < steps);
1182+
SM_ASSERT(level < levels);
11511183

11521184
do
11531185
{
@@ -1171,8 +1203,8 @@ SHARING_MAPT2(, innert *)::migrate(
11711203
key >>= chunk;
11721204
key_existing >>= chunk;
11731205

1174-
i++;
1175-
} while(i < steps);
1206+
level++;
1207+
} while(level < levels);
11761208

11771209
leaft leaf_copy(as_const(&container_copy)->get_container().front());
11781210
ip->get_container().push_front(leaf_copy);
@@ -1191,15 +1223,15 @@ ::insert(const key_type &k, valueU &&m)
11911223
// The root cannot be a container node
11921224
SM_ASSERT(ip->is_internal());
11931225

1194-
std::size_t i = 0;
1226+
std::size_t level = 0;
11951227

11961228
while(true)
11971229
{
11981230
std::size_t bit = key & mask;
11991231

12001232
SM_ASSERT(ip != nullptr);
12011233
SM_ASSERT(ip->is_internal());
1202-
SM_ASSERT(i == 0 || !ip->empty());
1234+
SM_ASSERT(level == 0 || !ip->empty());
12031235

12041236
innert *child = ip->add_child(bit);
12051237

@@ -1218,10 +1250,10 @@ ::insert(const key_type &k, valueU &&m)
12181250

12191251
if(child->is_container())
12201252
{
1221-
if(i < steps - 1)
1253+
if(level < levels - 1)
12221254
{
12231255
// Migrate the elements downwards
1224-
innert *cp = migrate(i, key, bit, *ip);
1256+
innert *cp = migrate(level, key, bit, *ip);
12251257

12261258
cp->place_leaf(k, std::forward<valueU>(m));
12271259
}
@@ -1236,11 +1268,11 @@ ::insert(const key_type &k, valueU &&m)
12361268
return;
12371269
}
12381270

1239-
SM_ASSERT(i == steps - 1 || child->is_defined_internal());
1271+
SM_ASSERT(level == levels - 1 || child->is_defined_internal());
12401272

12411273
ip = child;
12421274
key >>= chunk;
1243-
i++;
1275+
level++;
12441276
}
12451277
}
12461278

@@ -1298,6 +1330,6 @@ SHARING_MAPT(const std::size_t)::bits = 30;
12981330
SHARING_MAPT(const std::size_t)::chunk = 3;
12991331

13001332
SHARING_MAPT(const std::size_t)::mask = 0xffff >> (16 - chunk);
1301-
SHARING_MAPT(const std::size_t)::steps = bits / chunk;
1333+
SHARING_MAPT(const std::size_t)::levels = bits / chunk;
13021334

13031335
#endif

unit/util/sharing_map.cpp

Lines changed: 92 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ void sharing_map_internals_test()
111111
REQUIRE(marked.size() >= 2);
112112
}
113113

114+
// These tests check whether the internal representation of the map has the
115+
// expected shape after a series of operations. This is done by checking
116+
// whether the map has a number of nodes (inner nodes, container nodes, and
117+
// leaf nodes) that is consistent with the expected shape.
114118
SECTION("single map shape")
115119
{
116120
std::set<const void *> marked;
@@ -172,94 +176,7 @@ void sharing_map_internals_test()
172176
}
173177
}
174178

175-
SECTION("two maps shape")
176-
{
177-
std::set<const void *> marked;
178-
std::size_t chunk = 3;
179-
180-
sharing_map_unsignedt sm1;
181-
sharing_map_unsignedt sm2;
182-
183-
SECTION("left map deeper")
184-
{
185-
sm1.insert(0, "a");
186-
sm1.insert(1 << (2 * chunk), "b");
187-
188-
sm2.insert(0, "c");
189-
sm2.insert(1, "d");
190-
191-
sharing_map_unsignedt::delta_viewt delta_view;
192-
sm1.get_delta_view(sm2, delta_view, false);
193-
194-
REQUIRE(delta_view.size() == 2);
195-
196-
const bool b0 = delta_view[0].is_in_both_maps();
197-
const bool b1 = delta_view[1].is_in_both_maps();
198-
199-
REQUIRE((b0 || b1));
200-
REQUIRE(!(b0 && b1));
201-
202-
if(b0)
203-
{
204-
REQUIRE(delta_view[0].k == 0);
205-
REQUIRE(delta_view[0].m == "a");
206-
REQUIRE(delta_view[0].get_other_map_value() == "c");
207-
208-
REQUIRE(delta_view[1].k == 1 << (2 * chunk));
209-
REQUIRE(delta_view[1].m == "b");
210-
}
211-
else
212-
{
213-
REQUIRE(delta_view[0].k == 1 << (2 * chunk));
214-
REQUIRE(delta_view[0].m == "b");
215-
216-
REQUIRE(delta_view[1].k == 0);
217-
REQUIRE(delta_view[1].m == "a");
218-
REQUIRE(delta_view[1].get_other_map_value() == "c");
219-
}
220-
}
221-
222-
SECTION("right map deeper")
223-
{
224-
sm1.insert(0, "c");
225-
sm1.insert(1, "d");
226-
227-
sm2.insert(0, "a");
228-
sm2.insert(1 << (2 * chunk), "b");
229-
230-
sharing_map_unsignedt::delta_viewt delta_view;
231-
sm1.get_delta_view(sm2, delta_view, false);
232-
233-
REQUIRE(delta_view.size() == 2);
234-
235-
const bool b0 = delta_view[0].is_in_both_maps();
236-
const bool b1 = delta_view[1].is_in_both_maps();
237-
238-
REQUIRE((b0 || b1));
239-
REQUIRE(!(b0 && b1));
240-
241-
if(b0)
242-
{
243-
REQUIRE(delta_view[0].k == 0);
244-
REQUIRE(delta_view[0].m == "c");
245-
REQUIRE(delta_view[0].get_other_map_value() == "a");
246-
247-
REQUIRE(delta_view[1].k == 1);
248-
REQUIRE(delta_view[1].m == "d");
249-
}
250-
else
251-
{
252-
REQUIRE(delta_view[0].k == 1);
253-
REQUIRE(delta_view[0].m == "d");
254-
255-
REQUIRE(delta_view[1].k == 0);
256-
REQUIRE(delta_view[1].m == "c");
257-
REQUIRE(delta_view[1].get_other_map_value() == "a");
258-
}
259-
}
260-
}
261-
262-
SECTION("two maps shape, sharing")
179+
SECTION("delta view (sharing, one of the maps is deeper)")
263180
{
264181
std::set<const void *> marked;
265182
std::size_t chunk = 3;
@@ -680,6 +597,93 @@ TEST_CASE("Sharing map views and iteration", "[core][util]")
680597
sm1.get_delta_view(sm2, delta_view, false);
681598
REQUIRE(delta_view.size() == 3);
682599
}
600+
601+
SECTION("delta view (no sharing, one of the maps is deeper)")
602+
{
603+
std::set<const void *> marked;
604+
std::size_t chunk = 3;
605+
606+
sharing_map_unsignedt sm1;
607+
sharing_map_unsignedt sm2;
608+
609+
SECTION("left map deeper")
610+
{
611+
sm1.insert(0, "a");
612+
sm1.insert(1 << (2 * chunk), "b");
613+
614+
sm2.insert(0, "c");
615+
sm2.insert(1, "d");
616+
617+
sharing_map_unsignedt::delta_viewt delta_view;
618+
sm1.get_delta_view(sm2, delta_view, false);
619+
620+
REQUIRE(delta_view.size() == 2);
621+
622+
const bool b0 = delta_view[0].is_in_both_maps();
623+
const bool b1 = delta_view[1].is_in_both_maps();
624+
625+
REQUIRE((b0 || b1));
626+
REQUIRE(!(b0 && b1));
627+
628+
if(b0)
629+
{
630+
REQUIRE(delta_view[0].k == 0);
631+
REQUIRE(delta_view[0].m == "a");
632+
REQUIRE(delta_view[0].get_other_map_value() == "c");
633+
634+
REQUIRE(delta_view[1].k == 1 << (2 * chunk));
635+
REQUIRE(delta_view[1].m == "b");
636+
}
637+
else
638+
{
639+
REQUIRE(delta_view[0].k == 1 << (2 * chunk));
640+
REQUIRE(delta_view[0].m == "b");
641+
642+
REQUIRE(delta_view[1].k == 0);
643+
REQUIRE(delta_view[1].m == "a");
644+
REQUIRE(delta_view[1].get_other_map_value() == "c");
645+
}
646+
}
647+
648+
SECTION("right map deeper")
649+
{
650+
sm1.insert(0, "c");
651+
sm1.insert(1, "d");
652+
653+
sm2.insert(0, "a");
654+
sm2.insert(1 << (2 * chunk), "b");
655+
656+
sharing_map_unsignedt::delta_viewt delta_view;
657+
sm1.get_delta_view(sm2, delta_view, false);
658+
659+
REQUIRE(delta_view.size() == 2);
660+
661+
const bool b0 = delta_view[0].is_in_both_maps();
662+
const bool b1 = delta_view[1].is_in_both_maps();
663+
664+
REQUIRE((b0 || b1));
665+
REQUIRE(!(b0 && b1));
666+
667+
if(b0)
668+
{
669+
REQUIRE(delta_view[0].k == 0);
670+
REQUIRE(delta_view[0].m == "c");
671+
REQUIRE(delta_view[0].get_other_map_value() == "a");
672+
673+
REQUIRE(delta_view[1].k == 1);
674+
REQUIRE(delta_view[1].m == "d");
675+
}
676+
else
677+
{
678+
REQUIRE(delta_view[0].k == 1);
679+
REQUIRE(delta_view[0].m == "d");
680+
681+
REQUIRE(delta_view[1].k == 0);
682+
REQUIRE(delta_view[1].m == "c");
683+
REQUIRE(delta_view[1].get_other_map_value() == "a");
684+
}
685+
}
686+
}
683687
}
684688

685689
TEST_CASE("Sharing map view validity", "[core][util]")

0 commit comments

Comments
 (0)