Skip to content

Commit aac282b

Browse files
authored
Merge pull request #29878 from gottesmm/pr-a084c54a01e290d0552ef3ce96a8a6146cb7d446
Revert "Revert "[basic] Add a simple vector backed 2 stage multi map.""
2 parents 48f24b0 + bc8a4db commit aac282b

File tree

3 files changed

+384
-0
lines changed

3 files changed

+384
-0
lines changed

include/swift/Basic/FrozenMultiMap.h

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
//===--- FrozenMultiMap.h ----------------------------------*- C++ --------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
///
13+
/// \file
14+
///
15+
/// A 2 stage multi-map. Initially the multimap is mutable and can only be
16+
/// initialized. Once complete, the map is frozen and can be only used for map
17+
/// operations. It is guaranteed that all values are still in insertion order.
18+
///
19+
/// DISCUSSION: These restrictions flow from the internal implementation of the
20+
/// multi-map being a pair of keys, values. We form the map property by
21+
/// performing a stable_sort of the (key, value) in the process of freezing the
22+
/// map.
23+
///
24+
//===----------------------------------------------------------------------===//
25+
26+
#ifndef SWIFT_BASIC_FROZENMULTIMAP_H
27+
#define SWIFT_BASIC_FROZENMULTIMAP_H
28+
29+
#include "swift/Basic/LLVM.h"
30+
#include "swift/Basic/STLExtras.h"
31+
#include "llvm/ADT/SmallVector.h"
32+
#include <vector>
33+
34+
namespace swift {
35+
36+
template <typename Key, typename Value,
37+
typename VectorStorage = std::vector<std::pair<Key, Value>>>
38+
class FrozenMultiMap {
39+
VectorStorage storage;
40+
bool frozen = false;
41+
42+
private:
43+
struct PairToSecondElt;
44+
45+
public:
46+
using PairToSecondEltRange =
47+
TransformRange<ArrayRef<std::pair<Key, Value>>, PairToSecondElt>;
48+
49+
FrozenMultiMap() = default;
50+
51+
void insert(const Key &key, const Value &value) {
52+
assert(!isFrozen() && "Can not insert new keys once map is frozen");
53+
storage.emplace_back(key, value);
54+
}
55+
56+
Optional<PairToSecondEltRange> find(const Key &key) const {
57+
assert(isFrozen() &&
58+
"Can not perform a find operation until the map is frozen");
59+
// Since our array is sorted, we need to first find the first pair with our
60+
// inst as the first element.
61+
auto start = std::lower_bound(
62+
storage.begin(), storage.end(), std::make_pair(key, Value()),
63+
[&](const std::pair<Key, Value> &p1, const std::pair<Key, Value> &p2) {
64+
return p1.first < p2.first;
65+
});
66+
if (start == storage.end() || start->first != key) {
67+
return None;
68+
}
69+
70+
// Ok, we found our first element. Now scan forward until we find a pair
71+
// whose instruction is not our own instruction.
72+
auto end = find_if_not(
73+
start, storage.end(),
74+
[&](const std::pair<Key, Value> &pair) { return pair.first == key; });
75+
unsigned count = std::distance(start, end);
76+
ArrayRef<std::pair<Key, Value>> slice(&*start, count);
77+
return PairToSecondEltRange(slice, PairToSecondElt());
78+
}
79+
80+
bool isFrozen() const { return frozen; }
81+
82+
/// Set this map into its frozen state when we
83+
void setFrozen() {
84+
std::stable_sort(storage.begin(), storage.end(),
85+
[&](const std::pair<Key, Value> &lhs,
86+
const std::pair<Key, Value> &rhs) {
87+
// Only compare the first entry so that we preserve
88+
// insertion order.
89+
return lhs.first < rhs.first;
90+
});
91+
frozen = true;
92+
}
93+
94+
unsigned size() const { return storage.size(); }
95+
bool empty() const { return storage.empty(); }
96+
97+
struct iterator : std::iterator<std::forward_iterator_tag,
98+
std::pair<Key, ArrayRef<Value>>> {
99+
using base_iterator = typename decltype(storage)::iterator;
100+
101+
FrozenMultiMap &map;
102+
base_iterator baseIter;
103+
Optional<std::pair<Key, PairToSecondEltRange>> currentValue;
104+
105+
iterator(FrozenMultiMap &map, base_iterator iter)
106+
: map(map), baseIter(iter), currentValue() {
107+
updateCurrentValue();
108+
}
109+
110+
void updateCurrentValue() {
111+
base_iterator end = map.storage.end();
112+
113+
// If we are end, set currentValue to be None.
114+
if (baseIter == end) {
115+
currentValue = None;
116+
return;
117+
}
118+
119+
// Otherwise, determine the next range that we are visiting.
120+
auto rangeEnd = std::find_if_not(std::next(baseIter), end,
121+
[&](const std::pair<Key, Value> &elt) {
122+
return elt.first == baseIter->first;
123+
});
124+
unsigned count = std::distance(baseIter, rangeEnd);
125+
ArrayRef<std::pair<Key, Value>> slice(&*baseIter, count);
126+
currentValue = {baseIter->first,
127+
PairToSecondEltRange(slice, PairToSecondElt())};
128+
}
129+
130+
iterator &operator++() {
131+
baseIter = std::find_if_not(std::next(baseIter), map.storage.end(),
132+
[&](const std::pair<Key, Value> &elt) {
133+
return elt.first == baseIter->first;
134+
});
135+
updateCurrentValue();
136+
return *this;
137+
}
138+
139+
iterator operator++(int) {
140+
auto tmp = *this;
141+
baseIter = std::find_if_not(std::next(baseIter), map.storage.end(),
142+
[&](const std::pair<Key, Value> &elt) {
143+
return elt.first == baseIter->first;
144+
});
145+
updateCurrentValue();
146+
return tmp;
147+
}
148+
149+
std::pair<Key, PairToSecondEltRange> operator*() const {
150+
return *currentValue;
151+
}
152+
153+
bool operator==(const iterator &RHS) const {
154+
return baseIter == RHS.baseIter;
155+
}
156+
157+
bool operator!=(const iterator &RHS) const {
158+
return baseIter != RHS.baseIter;
159+
}
160+
};
161+
162+
/// Return a range of (key, ArrayRef<Value>) pairs. The keys are guaranteed to
163+
/// be in key sorted order and the ArrayRef<Value> are in insertion order.
164+
llvm::iterator_range<iterator> getRange() const {
165+
assert(isFrozen() &&
166+
"Can not create range until data structure is frozen?!");
167+
auto *self = const_cast<FrozenMultiMap *>(this);
168+
iterator iter1 = iterator(*self, self->storage.begin());
169+
iterator iter2 = iterator(*self, self->storage.end());
170+
return llvm::make_range(iter1, iter2);
171+
}
172+
};
173+
174+
template <typename Key, typename Value, typename Storage>
175+
struct FrozenMultiMap<Key, Value, Storage>::PairToSecondElt {
176+
PairToSecondElt() {}
177+
178+
Value operator()(const std::pair<Key, Value> &pair) const {
179+
return pair.second;
180+
}
181+
};
182+
183+
template <typename Key, typename Value, unsigned SmallSize>
184+
using SmallFrozenMultiMap =
185+
FrozenMultiMap<Key, Value, SmallVector<std::pair<Key, Value>, SmallSize>>;
186+
187+
} // namespace swift
188+
189+
#endif

unittests/Basic/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_swift_unittest(SwiftBasicTests
1515
EncodedSequenceTest.cpp
1616
ExponentialGrowthAppendingBinaryByteStreamTests.cpp
1717
FileSystemTest.cpp
18+
FrozenMultiMapTest.cpp
1819
ImmutablePointerSetTest.cpp
1920
JSONSerialization.cpp
2021
OptionSetTest.cpp
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
//===--- FrozenMultiMapTest.cpp -------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#define DEBUG_TYPE "swift-frozen-multi-map-test"
14+
15+
#include "swift/Basic/FrozenMultiMap.h"
16+
#include "swift/Basic/LLVM.h"
17+
#include "swift/Basic/Lazy.h"
18+
#include "swift/Basic/NullablePtr.h"
19+
#include "swift/Basic/Range.h"
20+
#include "swift/Basic/STLExtras.h"
21+
#include "llvm/ADT/Optional.h"
22+
#include "llvm/ADT/STLExtras.h"
23+
#include "llvm/ADT/SmallString.h"
24+
#include "llvm/ADT/StringExtras.h"
25+
#include "llvm/Support/Debug.h"
26+
#include "llvm/Support/raw_ostream.h"
27+
#include "gtest/gtest.h"
28+
#include <map>
29+
#include <random>
30+
#include <set>
31+
32+
using namespace swift;
33+
34+
namespace {
35+
36+
class Canary {
37+
static unsigned currentID;
38+
unsigned id;
39+
40+
public:
41+
static void resetIDs() { currentID = 0; }
42+
Canary(unsigned id) : id(id) {}
43+
Canary() {
44+
id = currentID;
45+
++currentID;
46+
}
47+
48+
unsigned getID() const { return id; }
49+
bool operator<(const Canary &other) const { return id < other.id; }
50+
51+
bool operator==(const Canary &other) const { return id == other.id; }
52+
53+
bool operator!=(const Canary &other) const { return !(*this == other); }
54+
};
55+
56+
unsigned Canary::currentID = 0;
57+
58+
} // namespace
59+
60+
TEST(FrozenMultiMapCustomTest, SimpleFind) {
61+
Canary::resetIDs();
62+
FrozenMultiMap<Canary, Canary> map;
63+
64+
auto key1 = Canary();
65+
auto key2 = Canary();
66+
map.insert(key1, Canary());
67+
map.insert(key1, Canary());
68+
map.insert(key1, Canary());
69+
map.insert(key2, Canary());
70+
map.insert(key2, Canary());
71+
72+
map.setFrozen();
73+
74+
EXPECT_EQ(map.size(), 5u);
75+
{
76+
auto r = map.find(key1);
77+
EXPECT_TRUE(r.hasValue());
78+
EXPECT_EQ(r->size(), 3u);
79+
EXPECT_EQ((*r)[0].getID(), 2u);
80+
EXPECT_EQ((*r)[1].getID(), 3u);
81+
EXPECT_EQ((*r)[2].getID(), 4u);
82+
}
83+
84+
{
85+
auto r = map.find(key2);
86+
EXPECT_TRUE(r.hasValue());
87+
EXPECT_EQ(r->size(), 2u);
88+
EXPECT_EQ((*r)[0].getID(), 5u);
89+
EXPECT_EQ((*r)[1].getID(), 6u);
90+
}
91+
}
92+
93+
TEST(FrozenMultiMapCustomTest, SimpleIter) {
94+
Canary::resetIDs();
95+
FrozenMultiMap<Canary, Canary> map;
96+
97+
auto key1 = Canary();
98+
auto key2 = Canary();
99+
map.insert(key1, Canary());
100+
map.insert(key1, Canary());
101+
map.insert(key1, Canary());
102+
map.insert(key2, Canary());
103+
map.insert(key2, Canary());
104+
105+
map.setFrozen();
106+
107+
EXPECT_EQ(map.size(), 5u);
108+
109+
auto range = map.getRange();
110+
EXPECT_EQ(std::distance(range.begin(), range.end()), 2);
111+
112+
{
113+
auto begin = range.begin();
114+
auto end = range.end();
115+
++begin;
116+
++begin;
117+
EXPECT_EQ(std::distance(begin, end), 0);
118+
}
119+
120+
auto iter = range.begin();
121+
{
122+
auto p = *iter;
123+
EXPECT_EQ(p.first.getID(), key1.getID());
124+
EXPECT_EQ(p.second.size(), 3u);
125+
EXPECT_EQ(p.second[0].getID(), 2u);
126+
EXPECT_EQ(p.second[1].getID(), 3u);
127+
EXPECT_EQ(p.second[2].getID(), 4u);
128+
}
129+
130+
++iter;
131+
{
132+
auto p = *iter;
133+
EXPECT_EQ(p.first.getID(), key2.getID());
134+
EXPECT_EQ(p.second.size(), 2u);
135+
EXPECT_EQ(p.second[0].getID(), 5u);
136+
EXPECT_EQ(p.second[1].getID(), 6u);
137+
}
138+
}
139+
140+
TEST(FrozenMultiMapCustomTest, RandomAgainstStdMultiMap) {
141+
Canary::resetIDs();
142+
FrozenMultiMap<unsigned, unsigned> map;
143+
std::multimap<unsigned, unsigned> stdMultiMap;
144+
145+
auto seed =
146+
std::chrono::high_resolution_clock::now().time_since_epoch().count();
147+
std::mt19937 mt_rand(seed);
148+
149+
std::vector<unsigned> keyIdList;
150+
for (unsigned i = 0; i < 1024; ++i) {
151+
unsigned keyID = mt_rand() % 20;
152+
keyIdList.push_back(keyID);
153+
for (unsigned valueID = (mt_rand()) % 15; valueID < 15; ++valueID) {
154+
map.insert(keyID, valueID);
155+
stdMultiMap.emplace(keyID, valueID);
156+
}
157+
}
158+
159+
map.setFrozen();
160+
161+
// Then for each key.
162+
for (unsigned i : keyIdList) {
163+
// Make sure that we have the same elements in the same order for each key.
164+
auto range = *map.find(i);
165+
auto stdRange = stdMultiMap.equal_range(i);
166+
EXPECT_EQ(std::distance(range.begin(), range.end()),
167+
std::distance(stdRange.first, stdRange.second));
168+
auto modernStdRange = llvm::make_range(stdRange.first, stdRange.second);
169+
for (auto p : llvm::zip(range, modernStdRange)) {
170+
unsigned lhs = std::get<0>(p);
171+
unsigned rhs = std::get<1>(p).second;
172+
EXPECT_EQ(lhs, rhs);
173+
}
174+
}
175+
176+
// Then check that when we iterate over both ranges, we get the same order.
177+
{
178+
auto range = map.getRange();
179+
auto rangeIter = range.begin();
180+
auto stdRangeIter = stdMultiMap.begin();
181+
182+
while (rangeIter != range.end()) {
183+
auto rangeElt = *rangeIter;
184+
185+
for (unsigned i : indices(rangeElt.second)) {
186+
EXPECT_EQ(rangeElt.first, stdRangeIter->first);
187+
EXPECT_EQ(rangeElt.second[i], stdRangeIter->second);
188+
++stdRangeIter;
189+
}
190+
191+
++rangeIter;
192+
}
193+
}
194+
}

0 commit comments

Comments
 (0)