From af49206a16a6de12de1ee948798f568a2295a78b Mon Sep 17 00:00:00 2001 From: modeco80 Date: Mon, 21 Oct 2024 12:22:35 -0400 Subject: [PATCH] Rewrite it in C++ rust people are going to be very mad at me now /j --- .clang-format | 44 +++++ Makefile | 15 ++ tree.py => python_refonly/tree.py | 0 tree_test.py => python_refonly/tree_test.py | 0 .../vxheaven_parse.py | 0 vxorg.py => python_refonly/vxorg.py | 0 tree.hpp | 106 +++++++++++ tree_test.cpp | 35 ++++ vxheaven_parse.cpp | 94 ++++++++++ vxheaven_parse.hpp | 16 ++ vxorg.cpp | 177 +++++------------- 11 files changed, 359 insertions(+), 128 deletions(-) create mode 100755 .clang-format create mode 100644 Makefile rename tree.py => python_refonly/tree.py (100%) rename tree_test.py => python_refonly/tree_test.py (100%) rename vxheaven_parse.py => python_refonly/vxheaven_parse.py (100%) rename vxorg.py => python_refonly/vxorg.py (100%) create mode 100644 tree.hpp create mode 100644 tree_test.cpp create mode 100644 vxheaven_parse.cpp create mode 100644 vxheaven_parse.hpp diff --git a/.clang-format b/.clang-format new file mode 100755 index 0000000..1323933 --- /dev/null +++ b/.clang-format @@ -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 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cddb22e --- /dev/null +++ b/Makefile @@ -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 $@ \ No newline at end of file diff --git a/tree.py b/python_refonly/tree.py similarity index 100% rename from tree.py rename to python_refonly/tree.py diff --git a/tree_test.py b/python_refonly/tree_test.py similarity index 100% rename from tree_test.py rename to python_refonly/tree_test.py diff --git a/vxheaven_parse.py b/python_refonly/vxheaven_parse.py similarity index 100% rename from vxheaven_parse.py rename to python_refonly/vxheaven_parse.py diff --git a/vxorg.py b/python_refonly/vxorg.py similarity index 100% rename from vxorg.py rename to python_refonly/vxorg.py diff --git a/tree.hpp b/tree.hpp new file mode 100644 index 0000000..b6993e3 --- /dev/null +++ b/tree.hpp @@ -0,0 +1,106 @@ +#pragma once +#include + +/// Port of tree.py to C++ +template +struct Tree { + + // FIXME make T not require default constructability + struct Node { + protected: + friend Tree; + Node* parent = nullptr; + std::vector 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 + void walk(Fn&& fn) { + fn(this); + if(!is_leaf()) { + for(auto& child: children) + child->walk(fn); + } + } + + template + 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 + 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; +}; \ No newline at end of file diff --git a/tree_test.cpp b/tree_test.cpp new file mode 100644 index 0000000..d46c4ec --- /dev/null +++ b/tree_test.cpp @@ -0,0 +1,35 @@ +#include "tree.hpp" +#include + +void test_tree() { + Tree 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; +} \ No newline at end of file diff --git a/vxheaven_parse.cpp b/vxheaven_parse.cpp new file mode 100644 index 0000000..03caa11 --- /dev/null +++ b/vxheaven_parse.cpp @@ -0,0 +1,94 @@ +#include "vxheaven_parse.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace vxorg { + + /// Wrapper for std::views::split. + /// Make sure line outlives the vector. + std::vector split_by(const std::string& string, char delim) { + auto res = std::vector {}; + 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 \ No newline at end of file diff --git a/vxheaven_parse.hpp b/vxheaven_parse.hpp new file mode 100644 index 0000000..6b1ce7d --- /dev/null +++ b/vxheaven_parse.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "tree.hpp" +#include + +namespace vxorg { + + struct VxHeavenItem { + std::string name; + // True if this item is also a sample + bool is_sample; + }; + + using VxHeavenTree = Tree; + + void parse_into_tree(VxHeavenTree& tree, std::istream& is); +} \ No newline at end of file diff --git a/vxorg.cpp b/vxorg.cpp index ae3dfc2..d327e33 100644 --- a/vxorg.cpp +++ b/vxorg.cpp @@ -1,134 +1,55 @@ -#include +#include +#include +#include #include +#include #include -#include - -/// Port of tree.py to C++ -template -struct Tree { - - // FIXME make T not require default constructability - struct Node { - protected: - friend Tree; - Node* parent = nullptr; - std::vector 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 - void walk(Fn&& fn) { - fn(this); - if(!is_leaf()) { - for(auto& child: children) - child->walk(fn); - } - } - - template - std::optional 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 - 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 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 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; } \ No newline at end of file