Rewrite it in C++
rust people are going to be very mad at me now /j
This commit is contained in:
parent
20d72e0cd2
commit
af49206a16
11 changed files with 359 additions and 128 deletions
44
.clang-format
Executable file
44
.clang-format
Executable file
|
@ -0,0 +1,44 @@
|
|||
BasedOnStyle: Google
|
||||
|
||||
# force T* or T&
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Left
|
||||
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
UseTab: Always
|
||||
IndentPPDirectives: BeforeHash
|
||||
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakStringLiterals: false
|
||||
|
||||
ColumnLimit: 150
|
||||
CompactNamespaces: false
|
||||
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ContinuationIndentWidth: 0
|
||||
|
||||
# turning this on causes major issues with initializer lists
|
||||
Cpp11BracedListStyle: false
|
||||
SpaceBeforeCpp11BracedList: true
|
||||
|
||||
FixNamespaceComments: true
|
||||
|
||||
NamespaceIndentation: All
|
||||
ReflowComments: true
|
||||
|
||||
SortIncludes: CaseInsensitive
|
||||
SortUsingDeclarations: true
|
||||
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeParens: Never
|
||||
SpacesBeforeTrailingComments: 1
|
15
Makefile
Normal file
15
Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
CXX = g++ -std=c++23 -O3
|
||||
|
||||
all: vxorg tree_test
|
||||
|
||||
clean:
|
||||
rm vxorg tree_test *.o
|
||||
|
||||
vxorg: vxorg.o vxheaven_parse.o
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
tree_test: tree_test.o
|
||||
$(CXX) $^ -o $@
|
||||
|
||||
%.o: %.cpp
|
||||
$(CXX) -c $< -o $@
|
106
tree.hpp
Normal file
106
tree.hpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
/// Port of tree.py to C++
|
||||
template<class T>
|
||||
struct Tree {
|
||||
|
||||
// FIXME make T not require default constructability
|
||||
struct Node {
|
||||
protected:
|
||||
friend Tree;
|
||||
Node* parent = nullptr;
|
||||
std::vector<Node*> children{};
|
||||
T item{};
|
||||
public:
|
||||
~Node() {
|
||||
for(auto& child: children)
|
||||
delete child;
|
||||
}
|
||||
|
||||
T& data() {
|
||||
return item;
|
||||
}
|
||||
|
||||
const T& data() const {
|
||||
return item;
|
||||
}
|
||||
|
||||
bool is_leaf() const {
|
||||
return children.size() == 0;
|
||||
}
|
||||
|
||||
Node* parent_node() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
Node* create_leaf(const T& item) {
|
||||
auto* node = new Node;
|
||||
node->parent = this;
|
||||
node->item = item;
|
||||
children.push_back(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
template<class Fn>
|
||||
void walk(Fn&& fn) {
|
||||
fn(this);
|
||||
if(!is_leaf()) {
|
||||
for(auto& child: children)
|
||||
child->walk(fn);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Pred>
|
||||
Node* find_child(Pred&& predicate) {
|
||||
if(predicate(this) == true)
|
||||
return this;
|
||||
|
||||
for(auto& child: children)
|
||||
if(predicate(child) == true)
|
||||
return child;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::size_t parent_count() const {
|
||||
auto* parent = this->parent;
|
||||
auto parent_count = 0z;
|
||||
while(parent) {
|
||||
parent_count ++;
|
||||
parent = parent->parent;
|
||||
}
|
||||
return parent_count;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Tree() {
|
||||
root = new Node;
|
||||
}
|
||||
|
||||
~Tree() {
|
||||
delete root;
|
||||
}
|
||||
|
||||
// Trees are not copyable but they can move
|
||||
Tree(const Tree&) = delete;
|
||||
Tree(Tree&&) = default;
|
||||
|
||||
template<class Fn>
|
||||
void walk(Fn&& fn) {
|
||||
root->walk(fn);
|
||||
}
|
||||
|
||||
|
||||
Node* create_leaf(const T& item) {
|
||||
return root->create_leaf(item);
|
||||
}
|
||||
|
||||
Node* root_node() {
|
||||
return root;
|
||||
}
|
||||
|
||||
private:
|
||||
Node* root;
|
||||
};
|
35
tree_test.cpp
Normal file
35
tree_test.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include "tree.hpp"
|
||||
#include <string>
|
||||
|
||||
void test_tree() {
|
||||
Tree<std::string> tree;
|
||||
|
||||
auto* virus = tree.create_leaf("Virus");
|
||||
auto* worm = tree.create_leaf("Worm");
|
||||
|
||||
auto* test = virus->create_leaf("test");
|
||||
|
||||
test->create_leaf("a");
|
||||
test->create_leaf("b");
|
||||
test->create_leaf("c");
|
||||
test->create_leaf("884");
|
||||
|
||||
tree.walk([](auto* node) {
|
||||
auto tab_count = node->parent_count();
|
||||
auto& data = node->data();
|
||||
|
||||
for(auto i = 0; i < tab_count; ++i)
|
||||
std::printf("\t");
|
||||
|
||||
if(data.empty()) {
|
||||
std::printf("(root)\n");
|
||||
} else {
|
||||
std::printf("%s\n", data.c_str());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int main() {
|
||||
test_tree();
|
||||
return 0;
|
||||
}
|
94
vxheaven_parse.cpp
Normal file
94
vxheaven_parse.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#include "vxheaven_parse.hpp"
|
||||
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace vxorg {
|
||||
|
||||
/// Wrapper for std::views::split.
|
||||
/// Make sure line outlives the vector.
|
||||
std::vector<std::string_view> split_by(const std::string& string, char delim) {
|
||||
auto res = std::vector<std::string_view> {};
|
||||
for(auto word : std::views::split(string, delim)) {
|
||||
res.push_back(std::string_view(word));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void parse_into_tree(VxHeavenTree& tree, std::istream& is) {
|
||||
std::string line {};
|
||||
while(std::getline(is, line)) {
|
||||
auto split = split_by(line, '.');
|
||||
|
||||
VxHeavenTree::Node* type_leaf { nullptr };
|
||||
VxHeavenTree::Node* platform_leaf { nullptr };
|
||||
VxHeavenTree::Node* family_leaf { nullptr };
|
||||
VxHeavenTree::Node* sample_leaf { nullptr };
|
||||
|
||||
if(auto* node = tree.root_node()->find_child([&](auto* node) { return node->data().name == split[0]; }); node == nullptr) {
|
||||
// std::printf("making leaf for type %.*s\n", split[0].length(), split[0].data());
|
||||
type_leaf = tree.create_leaf({ .name = std::string(split[0].data(), split[0].length()), .is_sample = false });
|
||||
} else {
|
||||
// std::printf("using existing leaf for type %.*s\n", split[0].length(), split[0].data());
|
||||
type_leaf = node;
|
||||
}
|
||||
|
||||
if(split.size() == 1) {
|
||||
type_leaf->data().is_sample = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(auto* n = type_leaf->find_child([&](auto* node) {
|
||||
// auto matches = node->data().name == split[1];
|
||||
// std::printf("trying to find %.*s in node %s's child %s: %s\n", split[1].length(), split[1].data(),
|
||||
// type_leaf->data().name.c_str(), node->data().name.c_str(), matches ? "matches": "doesnt fucking match god damn it");
|
||||
return node->data().name == split[1];
|
||||
});
|
||||
n == nullptr) {
|
||||
// std::printf("making leaf for platform %s %.*s\n", type_leaf->data().name.c_str(), split[1].length(), split[1].data());
|
||||
platform_leaf = type_leaf->create_leaf({ .name = std::string(split[1].data(), split[1].length()), .is_sample = false });
|
||||
} else {
|
||||
// std::printf("using existing leaf for platform %.*s\n", split[1].length(), split[1].data());
|
||||
platform_leaf = n;
|
||||
}
|
||||
|
||||
if(auto* n = platform_leaf->find_child([&](auto* node) { return node->data().name == split[2]; }); n == nullptr) {
|
||||
// std::printf("making leaf for platform %s %.*s\n", type_leaf->data().name.c_str(), split[1].length(), split[1].data());
|
||||
family_leaf = platform_leaf->create_leaf({ .name = std::string(split[2].data(), split[2].length()), .is_sample = false });
|
||||
} else {
|
||||
// std::printf("using existing leaf for platform %.*s\n", split[1].length(), split[1].data());
|
||||
family_leaf = n;
|
||||
}
|
||||
|
||||
// Handle famlies with a variantless sample inside of them
|
||||
if(split.size() == 3) {
|
||||
family_leaf->data().is_sample = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(split.size() > 4) {
|
||||
auto subvariants = std::span(split.data() + 4, split.size() - 4);
|
||||
auto leaf = family_leaf;
|
||||
|
||||
for(auto& subvariant : subvariants) {
|
||||
if(auto* node = leaf->find_child([&](auto* node) { return node->data().name == subvariant; }); node == nullptr) {
|
||||
leaf = leaf->create_leaf({ .name = std::string(subvariant.data(), subvariant.length()), .is_sample = false });
|
||||
}
|
||||
}
|
||||
|
||||
leaf->data().is_sample = true;
|
||||
} else {
|
||||
auto subvariant = split[3];
|
||||
if(auto* node = family_leaf->find_child([&](auto* node) { return node->data().name == subvariant; }); node == nullptr) {
|
||||
family_leaf->create_leaf({ .name = std::string(subvariant.data(), subvariant.length()), .is_sample = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace vxorg
|
16
vxheaven_parse.hpp
Normal file
16
vxheaven_parse.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
#include "tree.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace vxorg {
|
||||
|
||||
struct VxHeavenItem {
|
||||
std::string name;
|
||||
// True if this item is also a sample
|
||||
bool is_sample;
|
||||
};
|
||||
|
||||
using VxHeavenTree = Tree<VxHeavenItem>;
|
||||
|
||||
void parse_into_tree(VxHeavenTree& tree, std::istream& is);
|
||||
}
|
177
vxorg.cpp
177
vxorg.cpp
|
@ -1,134 +1,55 @@
|
|||
#include <optional>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
/// Port of tree.py to C++
|
||||
template<class T>
|
||||
struct Tree {
|
||||
|
||||
// FIXME make T not require default constructability
|
||||
struct Node {
|
||||
protected:
|
||||
friend Tree;
|
||||
Node* parent = nullptr;
|
||||
std::vector<Node*> children{};
|
||||
T item{};
|
||||
public:
|
||||
~Node() {
|
||||
for(auto& child: children)
|
||||
delete child;
|
||||
}
|
||||
|
||||
T& data() {
|
||||
return item;
|
||||
}
|
||||
|
||||
const T& data() const {
|
||||
return item;
|
||||
}
|
||||
|
||||
bool is_leaf() const {
|
||||
return children.size() == 0;
|
||||
}
|
||||
|
||||
Node* create_leaf(const T& item) {
|
||||
auto* node = new Node;
|
||||
node->parent = this;
|
||||
node->item = item;
|
||||
children.push_back(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
template<class Fn>
|
||||
void walk(Fn&& fn) {
|
||||
fn(this);
|
||||
if(!is_leaf()) {
|
||||
for(auto& child: children)
|
||||
child->walk(fn);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Pred>
|
||||
std::optional<Node*> find_child(Pred&& predicate) {
|
||||
if(predicate(*this))
|
||||
return this;
|
||||
|
||||
for(auto& child: children)
|
||||
if(predicate(*child))
|
||||
return child;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t parent_count() const {
|
||||
auto* parent = this->parent;
|
||||
auto parent_count = 0z;
|
||||
while(parent) {
|
||||
parent_count ++;
|
||||
parent = parent->parent;
|
||||
}
|
||||
return parent_count;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Tree() {
|
||||
root = new Node;
|
||||
}
|
||||
|
||||
~Tree() {
|
||||
delete root;
|
||||
}
|
||||
|
||||
// Trees are not copyable but they can move
|
||||
Tree(const Tree&) = delete;
|
||||
Tree(Tree&&) = default;
|
||||
|
||||
template<class Fn>
|
||||
void walk(Fn&& fn) {
|
||||
root->walk(fn);
|
||||
}
|
||||
|
||||
|
||||
Node* create_leaf(const T& item) {
|
||||
return root->create_leaf(item);
|
||||
}
|
||||
|
||||
private:
|
||||
Node* root;
|
||||
};
|
||||
|
||||
void test_tree() {
|
||||
Tree<std::string> tree;
|
||||
|
||||
auto* virus = tree.create_leaf("Virus");
|
||||
auto* worm = tree.create_leaf("Worm");
|
||||
|
||||
auto* test = virus->create_leaf("test");
|
||||
|
||||
test->create_leaf("a");
|
||||
test->create_leaf("b");
|
||||
test->create_leaf("c");
|
||||
test->create_leaf("884");
|
||||
|
||||
tree.walk([](auto* node) {
|
||||
auto tab_count = node->parent_count();
|
||||
auto& data = node->data();
|
||||
|
||||
for(auto i = 0; i < tab_count; ++i)
|
||||
std::printf("\t");
|
||||
|
||||
if(data.empty()) {
|
||||
std::printf("(root)\n");
|
||||
} else {
|
||||
std::printf("%s\n", data.c_str());
|
||||
}
|
||||
});
|
||||
}
|
||||
#include "tree.hpp"
|
||||
#include "vxheaven_parse.hpp"
|
||||
|
||||
int main() {
|
||||
test_tree();
|
||||
return 0;
|
||||
std::ifstream ifs("./samples.sort");
|
||||
vxorg::VxHeavenTree tree;
|
||||
|
||||
vxorg::parse_into_tree(tree, ifs);
|
||||
|
||||
// walk the resulting tree
|
||||
tree.walk([](auto* node) {
|
||||
auto tab_count = node->parent_count();
|
||||
auto& data = node->data();
|
||||
|
||||
for(auto i = 0; i < tab_count; ++i)
|
||||
std::printf("\t");
|
||||
|
||||
if(data.name.empty()) {
|
||||
std::printf("(root)\n");
|
||||
} else {
|
||||
if(data.is_sample) {
|
||||
std::string sample_name {};
|
||||
|
||||
std::vector<vxorg::VxHeavenItem*> parent_list {};
|
||||
vxorg::VxHeavenTree::Node* parent = node->parent_node();
|
||||
while(parent) {
|
||||
if(parent->data().name.empty())
|
||||
break;
|
||||
|
||||
parent_list.push_back(&parent->data());
|
||||
parent = parent->parent_node();
|
||||
}
|
||||
|
||||
for(auto& item : std::views::reverse(parent_list)) {
|
||||
sample_name += std::format("{}.", item->name);
|
||||
}
|
||||
|
||||
sample_name += data.name;
|
||||
|
||||
std::printf("%s (sample %s)\n", data.name.c_str(), sample_name.c_str());
|
||||
|
||||
} else {
|
||||
std::printf("%s\n", data.name.c_str());
|
||||
}
|
||||
}
|
||||
});
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue