summaryrefslogtreecommitdiff
path: root/src/set.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/set.cpp')
-rw-r--r--src/set.cpp171
1 files changed, 171 insertions, 0 deletions
diff --git a/src/set.cpp b/src/set.cpp
new file mode 100644
index 0000000..001bce3
--- /dev/null
+++ b/src/set.cpp
@@ -0,0 +1,171 @@
+#include "set.h"
+
+#include <unordered_map>
+#include <unordered_set>
+#include <utility>
+
+namespace erebos {
+
+using std::pair;
+using std::unordered_map;
+using std::unordered_set;
+using std::move;
+
+SetBase::SetBase():
+ p(make_shared<Priv>())
+{
+}
+
+SetBase::SetBase(const vector<Ref> & refs)
+{
+ vector<Stored<SetItem>> items;
+ for (const auto & r : refs)
+ items.push_back(Stored<SetItem>::load(r));
+
+ p = shared_ptr<Priv>(new Priv {
+ .items = move(items),
+ });
+}
+
+SetBase::SetBase(shared_ptr<const Priv> p_):
+ p(move(p_))
+{
+}
+
+shared_ptr<const SetBase::Priv> SetBase::add(Storage & st, const vector<Ref> & refs) const
+{
+ auto item = st.store(SetItem {
+ .prev = p->items,
+ .item = refs,
+ });
+
+ return shared_ptr<const Priv>(new Priv {
+ .items = { move(item) },
+ });
+}
+
+static void gatherSetItems(unordered_set<Digest> & seenSet, unordered_set<Digest> & seenElem,
+ vector<Ref> & res, const Stored<SetItem> & item)
+{
+ if (!seenElem.insert(item.ref().digest()).second)
+ return;
+
+ for (const auto & r : item->item)
+ if (seenSet.insert(r.digest()).second)
+ res.push_back(r);
+
+ for (const auto & p : item->prev)
+ gatherSetItems(seenSet, seenElem, res, p);
+}
+
+vector<vector<Ref>> SetBase::toList() const
+{
+ /* Splits the graph starting from all set item refs into connected
+ * components (partitions), each such partition makes one set item,
+ * merged together in the templated SetView constructor. */
+
+ // Gather all item references
+ vector<Ref> items;
+ {
+ unordered_set<Digest> seenSet, seenElem;
+ for (const auto & i : p->items)
+ gatherSetItems(seenSet, seenElem, items, i);
+ }
+
+ unordered_map<Digest, unsigned> partMap; // maps item ref to partition number
+ vector<unsigned> partMerge; // maps partitions to resulting one after partition merge
+
+ // Use (cached) root set for assigning partition numbers
+ for (const auto & item : items) {
+ const auto roots = item.roots();
+ unsigned part = partMerge.size();
+
+ // If any root has partition number already, pick the smallest one
+ for (const auto & rdgst : roots) {
+ auto it = partMap.find(rdgst);
+ if (it != partMap.end() && it->second < part)
+ part = it->second;
+ }
+
+ // Update partition number for the roots and if this item
+ // merges some partitions, also update the merge info
+ for (const auto & rdgst : roots) {
+ auto it = partMap.find(rdgst);
+ if (it == partMap.end()) {
+ partMap.emplace(rdgst, part);
+ } else if (it->second != part) {
+ partMerge[it->second] = part;
+ it->second = part;
+ }
+ }
+
+ // If no existing partition has been touched, mark a new one
+ if (part == partMerge.size())
+ partMerge.push_back(part);
+
+ // And store resulting partition number
+ partMap.emplace(item.digest(), part);
+ }
+
+ // Get all the refs for each partition
+ vector<vector<Ref>> res(partMerge.size());
+ for (const auto & item : items) {
+ unsigned part = partMap[item.digest()];
+ for (unsigned p = partMerge[part]; p != part; p = partMerge[p])
+ part = p;
+ res[part].push_back(item);
+ }
+
+ // Remove empty elements (merged partitions) from result list
+ res.erase(std::remove(res.begin(), res.end(), vector<Ref>()), res.end());
+
+ return res;
+}
+
+vector<Digest> SetBase::digests() const
+{
+ vector<Digest> res;
+ res.reserve(p->items.size());
+ for (const auto & i : p->items)
+ res.push_back(i.ref().digest());
+ return res;
+}
+
+SetItem SetItem::load(const Ref & ref)
+{
+ if (auto rec = ref->asRecord()) {
+ vector<Stored<SetItem>> prev;
+ for (auto p : rec->items("PREV"))
+ if (const auto & x = p.as<SetItem>())
+ prev.push_back(*x);
+
+ vector<Ref> item;
+ for (auto i : rec->items("item"))
+ if (const auto & x = i.asRef())
+ item.push_back(*x);
+
+ return SetItem {
+ .prev = std::move(prev),
+ .item = std::move(item),
+ };
+ }
+
+ return SetItem {
+ .prev = {},
+ .item = {},
+ };
+}
+
+Ref SetItem::store(const Storage & st) const
+{
+ vector<Record::Item> items;
+
+ for (const auto & p : prev)
+ items.emplace_back("PREV", p.ref());
+ for (const auto & r : item)
+ items.emplace_back("item", r);
+
+ return st.storeObject(Record(std::move(items)));
+}
+
+}