133 lines
3.7 KiB
C++
133 lines
3.7 KiB
C++
#include <unordered_map>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include "linked_helper.h"
|
|
|
|
using namespace std;
|
|
using namespace linked;
|
|
|
|
std::shared_ptr<entry> linked::build_chain(const std::deque<std::shared_ptr<linked::entry>> &entries, std::deque<std::string> &log) {
|
|
//TODO filter only for one layer
|
|
auto find_entry = [&](uint64_t id) -> shared_ptr<entry> {
|
|
for(const auto& entry : entries)
|
|
if(entry->entry_id == id)
|
|
return entry;
|
|
return nullptr;
|
|
};
|
|
|
|
deque<shared_ptr<entry>> heads;
|
|
{
|
|
//first linking
|
|
for(const auto& entry : entries) {
|
|
auto previous = find_entry(entry->previous_id);
|
|
if(!previous && entry->previous_id > 0) {
|
|
log.emplace_back("missing " + to_string(entry->entry_id) + "'s previous entry (" + to_string(entry->previous_id) + "). Removing previous");
|
|
entry->previous_id = 0;
|
|
entry->modified = true;
|
|
}
|
|
|
|
if(previous) {
|
|
/* validate previous stuff */
|
|
if((previous->next && previous->next != entry)) {
|
|
log.emplace_back(to_string(entry->entry_id) + "'s previous node has already someone linked (" + to_string(previous->next->entry_id) + "). Removing previous");
|
|
entry->previous_id = 0;
|
|
entry->modified = true;
|
|
previous = nullptr;
|
|
} else if(previous == entry) {
|
|
log.emplace_back(to_string(entry->entry_id) + "'s previous node references to himself. Removing previous");
|
|
entry->previous_id = 0;
|
|
entry->modified = true;
|
|
previous = nullptr;
|
|
}
|
|
}
|
|
if(previous) {
|
|
previous->next = entry;
|
|
entry->previous = previous;
|
|
} else {
|
|
heads.push_back(entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
/*
|
|
* Now test for circles (the heads could not contain a circle because they have one open end)
|
|
* But we've may nodes which are not within the heads
|
|
*/
|
|
|
|
unordered_map<void*, uint8_t> used_nodes;
|
|
deque<shared_ptr<entry>> unused_nodes;
|
|
|
|
for(auto head : heads) {
|
|
while(head) {
|
|
auto& value = used_nodes[&*head];
|
|
if(value) {
|
|
//Node has been already used
|
|
log.emplace_back(to_string(head->entry_id) + "'s has already been used, but is linked in another chain! We could not recover from that");
|
|
return nullptr;
|
|
} else
|
|
value = 1;
|
|
|
|
head = head->next;
|
|
}
|
|
}
|
|
|
|
for(const auto& node : entries) {
|
|
if(!used_nodes[&*node])
|
|
unused_nodes.push_back(node);
|
|
}
|
|
|
|
while(!unused_nodes.empty()) {
|
|
auto head = std::move(unused_nodes.front());
|
|
unused_nodes.pop_front();
|
|
|
|
log.emplace_back("Found circle. Cutting circle between " + to_string(head->previous->entry_id) + " and " + to_string(head->entry_id));
|
|
if(head->previous->next == head)
|
|
head->previous->next = nullptr;
|
|
|
|
head->previous = nullptr;
|
|
head->previous_id = 0;
|
|
head->modified = true;
|
|
|
|
heads.push_back(head);
|
|
while(head->next) {
|
|
auto it = find(unused_nodes.begin(), unused_nodes.end(), head->next);
|
|
if(it != unused_nodes.end()) {
|
|
unused_nodes.erase(it);
|
|
} else {
|
|
log.emplace_back(to_string(head->entry_id) + "'s has already been used, but is linked in another chain! We could not recover from that");
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(heads.empty()) {
|
|
if(!entries.empty())
|
|
log.emplace_back("failed to detect heads! We could not recover from that");
|
|
return nullptr;
|
|
}
|
|
|
|
auto head = heads.front();
|
|
heads.pop_front();
|
|
|
|
auto tail = head;
|
|
while(tail->next) tail = tail->next;
|
|
|
|
while(!heads.empty()) {
|
|
|
|
auto local_head = heads.front();
|
|
heads.pop_front();
|
|
|
|
log.emplace_back("Appending open begin (" + to_string(local_head->entry_id) + ") to current tail (" + to_string(tail->entry_id) + ")");
|
|
local_head->previous = tail;
|
|
local_head->previous_id = tail->entry_id;
|
|
local_head->modified = true;
|
|
|
|
tail->next = local_head;
|
|
|
|
while(tail->next) tail = tail->next; /* walk to the tail again */
|
|
}
|
|
|
|
return head;
|
|
}
|
|
} |