From 1d52b211319c78724229e7fd43e5e35469643519 Mon Sep 17 00:00:00 2001 From: Brett Date: Fri, 15 Dec 2023 01:49:42 -0500 Subject: [PATCH] ascii box now fixed and back to where it was --- include/blt/std/format.h | 98 ++++++++++++++++++++++--------- include/blt/std/memory.h | 13 ++--- src/blt/std/format.cpp | 117 +++++++++++++++++++++++++------------ tests/src/utility_test.cpp | 23 +++++--- 4 files changed, 168 insertions(+), 83 deletions(-) diff --git a/include/blt/std/format.h b/include/blt/std/format.h index fcb11db..90d3e72 100755 --- a/include/blt/std/format.h +++ b/include/blt/std/format.h @@ -313,15 +313,18 @@ namespace blt::string static inline constexpr size_t MAX_CHILDREN = 16; - struct box_format + /** + * Structure which provides variables for the internal spacing of ASCII objects + */ + struct ascii_padding_format { - size_t boxHPadding = 4; - size_t boxVPadding = 2; + size_t horizontalPadding = 4; + size_t verticalPadding = 2; }; struct tree_format { - box_format boxFormat; + ascii_padding_format boxFormat; int verticalPadding; int horizontalPadding; @@ -429,56 +432,93 @@ namespace blt::string } }; - class ascii_box + class ascii_object { - public: - std::string_view title; - std::string_view data; - const box_format& format; - private: + protected: size_t width_; size_t height_; public: - ascii_box(std::string_view title, std::string_view data, const box_format& format): title(title), data(data), format(format) - { - width_ = std::max(data.length(), title.length()) + (format.boxHPadding * 2); - height_ = 5 + (format.boxVPadding * 2); - } + ascii_object(size_t width, size_t height): width_(width), height_(height) + {} + /** + * @return Internal width of the ascii object. This does not include the bordering box + */ [[nodiscard]] inline size_t width() const { - return width_ + 2; + return width_; } + /** + * @return Internal height of the ascii object. This does not include the border. + */ [[nodiscard]] inline size_t height() const { return height_; } - [[nodiscard]] inline size_t raw_width() const + /** + * @return full height of the ascii box, includes the expected border around the box + */ + [[nodiscard]] inline size_t full_height() const { - return width_; + return height_ + 2; + } + + /** + * @return full width of the ascii box, includes the expected border around the box. + */ + [[nodiscard]] inline size_t full_width() const + { + return width_ + 2; } }; + class ascii_box : public ascii_object + { + public: + std::string_view data; + const ascii_padding_format& format; + public: + explicit ascii_box(std::string_view data, + const ascii_padding_format& format = {}): ascii_object(data.length() + (format.horizontalPadding * 2), + 1 + (format.verticalPadding * 2)), data(data), format(format) + {} + }; + + class ascii_titled_box : public ascii_object + { + public: + std::string_view title; + std::string_view data; + const ascii_padding_format& format; + public: + ascii_titled_box(std::string_view title, std::string_view data, + const ascii_padding_format& format = {}): + ascii_object(std::max(data.length(), title.length()) + (format.horizontalPadding * 2), + 3 + (format.verticalPadding * 2)), title(title), data(data), format(format) + {} + }; + + typedef std::variant box_type; + class ascii_boxes { private: - std::vector boxes_; + std::vector boxes_; size_t width_ = 1; size_t height_ = 0; public: ascii_boxes() = default; - inline void push_back(ascii_box&& box) + void push_back(box_type&& box); + + inline void push_back(const box_type& box) { - width_ += box.raw_width() + 1; - // should all be the same - height_ = std::max(box.height(), height_); - boxes_.push_back(box); + push_back(box_type(box)); } - inline std::vector& boxes() + inline std::vector& boxes() { return boxes_; } @@ -494,9 +534,9 @@ namespace blt::string } }; - typedef std::variant box_type; + typedef std::variant box_container; - ascii_data constructBox(const box_type& box); + ascii_data constructBox(const box_container& box); class BinaryTreeFormatter { @@ -532,8 +572,8 @@ namespace blt::string Node* root = nullptr; public: - explicit BinaryTreeFormatter(std::string rootData, tree_format format = {}): - format(std::move(format)), root(new Node(std::move(rootData))) + explicit BinaryTreeFormatter(std::string rootData, const tree_format& format = {}): + format(format), root(new Node(std::move(rootData))) {} std::vector generateBox(Node* node) const; diff --git a/include/blt/std/memory.h b/include/blt/std/memory.h index 2f6e70d..c3fb4e8 100755 --- a/include/blt/std/memory.h +++ b/include/blt/std/memory.h @@ -525,19 +525,14 @@ namespace blt void expand() { size_t new_size = m_size * 2; - T* data = new T[new_size]; + T* data = static_cast(malloc(sizeof(T) * new_size)); if constexpr (std::is_trivially_copyable_v) std::memcpy(data, m_data, m_size); - else if constexpr (std::is_move_assignable_v) + else if constexpr (std::is_move_assignable_v || std::is_move_constructible_v) { for (size_t i = 0; i < m_size; i++) data[i] = std::move(m_data[i]); - } else if constexpr (std::is_move_constructible_v) - { - // is this bad? probably - for (size_t i = 0; i < m_size; i++) - data[i] = T(std::move(m_data)); - } else if constexpr (std::is_copy_assignable_v) + } else if constexpr (std::is_copy_assignable_v || std::is_copy_constructible_v) { for (size_t i = 0; i < m_size; i++) data[i] = m_data[i]; @@ -545,7 +540,7 @@ namespace blt { static_assert("Unable to use this type with this allocator!"); } - delete[] m_data; + free(m_data); m_data = data; m_size = new_size; } diff --git a/src/blt/std/format.cpp b/src/blt/std/format.cpp index e0c3ba9..65c861c 100755 --- a/src/blt/std/format.cpp +++ b/src/blt/std/format.cpp @@ -17,6 +17,15 @@ inline constexpr char SEPARATOR = '-'; inline constexpr char CONNECTOR = '+'; inline constexpr char BAR = '|'; +/** + * Returns information from a box_type variant, useful for getting width()/height() + */ +#define getBoxData(variant, getter) \ + std::visit(blt::lambda_visitor{ \ + [](const blt::string::ascii_box& box) -> auto { return box.getter; }, \ + [](const blt::string::ascii_titled_box& box) -> auto { return box.getter; } \ + }, variant) + std::vector blt::string::TableFormatter::createTable(bool top, bool bottom) { std::vector table; @@ -224,7 +233,8 @@ std::vector blt::string::BinaryTreeFormatter::construct() size_t lastLineLength = 0; const size_t lineHeight = format.verticalPadding * 2 + 3; //std::cout << levels.size() << "\n"; - const size_t verticalSpacing = format.boxFormat.boxHPadding % 2 == 0 ? format.boxFormat.boxHPadding + 1 : format.boxFormat.boxHPadding; + const size_t verticalSpacing = + format.boxFormat.horizontalPadding % 2 == 0 ? format.boxFormat.horizontalPadding + 1 : format.boxFormat.horizontalPadding; while (!levels.empty()) { std::vector currentLines; @@ -247,7 +257,7 @@ std::vector blt::string::BinaryTreeFormatter::construct() BLT_ASSERT(currentLines.size() == box.size() && "Box lines should match current lines!"); for (size_t i = 0; i < currentLines.size(); i++) { - currentLines[i] += createPadding(format.boxFormat.boxVPadding); + currentLines[i] += createPadding(format.boxFormat.verticalPadding); //currentLines[i] += createPadding(format.horizontalSpacing + static_cast(lineLength / (n.level.size() + 1))); currentLines[i] += box[i]; } @@ -420,58 +430,86 @@ std::string blt::string::createPadding(size_t length, char spacing) return padding; } -namespace blt +namespace blt::string { void constructVerticalSeparator(blt::string::ascii_data& data, size_t offset, size_t height) { for (size_t i = 0; i < height; i++) { - if (!(i == 0 || i == height - 1 || i == 2)) + if (data.at(offset, i) != '+') data.at(offset, i) = BAR; } } - void addBox(blt::string::ascii_data& data, const blt::string::ascii_box& box, size_t offset) + void addBox(blt::string::ascii_data& data, const blt::string::box_type& box, size_t width_offset, bool has_titled_friends = false, + size_t height_offset = 1) { - // create the horizontal separators - for (size_t i = 0; i < box.width(); i++) + size_t vertical_offset = 0; + // get box data + auto width = getBoxData(box, width()); + auto full_width = getBoxData(box, full_width()); + auto height = getBoxData(box, height()); + auto full_height = getBoxData(box, full_height()); + const auto& box_format = getBoxData(box, format); + const auto& box_data = getBoxData(box, data); + + size_t titlePad = box_format.horizontalPadding + 1; + size_t dataPad = box_format.horizontalPadding + 1; + + // construct titled box's title + if (has_titled_friends || std::holds_alternative(box)) { - char c = SEPARATOR; - if (i == 0 || i == box.width() - 1) - c = CONNECTOR; - data.at(offset + i, 0) = c; - data.at(offset + i, 2) = c; - data.at(offset + i, box.height() - 1) = c; + for (size_t i = 0; i < full_width; i++) + { + char c = SEPARATOR; + if (i == 0 || i == full_width - 1) + c = CONNECTOR; + data.at(width_offset + i, 2) = c; + } + // TODO: this is ugly + if (std::holds_alternative(box)){ + const auto& box_title = std::get(box).title; + + // if one of the strings are larger than there will be a misalignment as the width of the box is based on the largest string, + // so we need to add an offset to the smallest string for centering. + if (box_data.length() > box_title.length()) + titlePad += (box_data.length() - box_title.length()) / 2; + else + dataPad += (box_title.length() - box_data.length()) / 2; + + // copy in the title and data string + for (size_t i = 0; i < box_title.size(); i++) + data.at(width_offset + titlePad + i, 1) = box_title[i]; + } else + full_height += 3; + height_offset = 3; } - size_t titlePad = box.format.boxHPadding + 1; - size_t dataPad = box.format.boxHPadding + 1; + // create the horizontal separators + for (size_t i = 0; i < full_width; i++) + { + char c = SEPARATOR; + if (i == 0 || i == full_width - 1) + c = CONNECTOR; + data.at(width_offset + i, 0) = c; + data.at(width_offset + i, full_height - 1) = c; + } - // if one of the strings are larger than there will be a misalignment as the width of the box is based on the largest string - // so we need to add an offset to the smallest string for centering. - if (box.data.length() > box.title.length()) - titlePad += (box.data.length() - box.title.length()) / 2; - else - dataPad += (box.title.length() - box.data.length()) / 2; - - // copy in the title and data string - for (size_t i = 0; i < box.title.size(); i++) - data.at(offset + titlePad + i, 1) = box.title[i]; - for (size_t i = 0; i < box.data.size(); i++) - data.at(offset + dataPad + i, 3 + box.format.boxVPadding) = box.data[i]; + for (size_t i = 0; i < box_data.size(); i++) + data.at(width_offset + dataPad + i, height_offset + box_format.verticalPadding) = box_data[i]; // add the vertical separator - constructVerticalSeparator(data, offset + box.raw_width() + 1, box.height()); + constructVerticalSeparator(data, width_offset + width + 1, full_height); } - blt::string::ascii_data blt::string::constructBox(const blt::string::box_type& box) + blt::string::ascii_data constructBox(const blt::string::box_container& box) { auto width = std::visit(blt::lambda_visitor{ - [](const blt::string::ascii_box& box) -> size_t { return box.width(); }, + [](const blt::string::box_type& box) -> size_t { return getBoxData(box, full_width()); }, [](const blt::string::ascii_boxes& boxes) -> size_t { return boxes.width(); } }, box); auto height = std::visit(blt::lambda_visitor{ - [](const blt::string::ascii_box& box) -> size_t { return box.height(); }, + [](const blt::string::box_type& box) -> size_t { return getBoxData(box, full_height()); }, [](const blt::string::ascii_boxes& boxes) -> size_t { return boxes.height(); } }, box); @@ -479,22 +517,27 @@ namespace blt constructVerticalSeparator(data, 0, height); - if (std::holds_alternative(box)) - { - auto b = std::get(box); - addBox(data, b, 0); - } else + if (std::holds_alternative(box)) + addBox(data, std::get(box), 0); + else { auto bv = std::get(box); size_t offset = 0; for (const auto& b : bv.boxes()) { addBox(data, b, offset); - offset += b.raw_width() + 1; + offset += getBoxData(b, width()) + 1; } } return data; } + void string::ascii_boxes::push_back(string::box_type&& box) + { + width_ += getBoxData(box, width()) + 1; + // should all be the same + height_ = std::max(getBoxData(box, full_height()), height_); + boxes_.push_back(box); + } } \ No newline at end of file diff --git a/tests/src/utility_test.cpp b/tests/src/utility_test.cpp index 93702b0..4803198 100644 --- a/tests/src/utility_test.cpp +++ b/tests/src/utility_test.cpp @@ -108,7 +108,7 @@ void blt::test::utility::run() blt::string::tree_format format; format.horizontalPadding = 3; format.verticalPadding = 0; - format.boxFormat.boxVPadding = 3; + format.boxFormat.verticalPadding = 3; format.collapse = true; blt::string::BinaryTreeFormatter treeFormatter("I love Men", format); treeFormatter.getRoot()->with( @@ -148,22 +148,29 @@ void blt::test::utility::run() testEnumerate(in); } - blt::string::box_format bf; - bf.boxHPadding = 1; - bf.boxVPadding = 1; - blt::string::ascii_box b1{"", "I sold your child", bf}; - blt::string::ascii_box b2{"", "Your my whole world", bf}; + blt::string::ascii_padding_format bf; + bf.horizontalPadding = 1; + bf.verticalPadding = 1; + blt::string::ascii_titled_box bt1{"I have sex", "I sold your child", bf}; + blt::string::ascii_titled_box bt2{"With your child", "Your my whole world", bf}; + blt::string::ascii_box b1{"Single Love", bf}; + blt::string::ascii_box b2{"Never Was Never Will", bf}; + + auto btd = blt::string::constructBox(bt1); + auto btd1 = blt::string::constructBox(bt2); auto bd = blt::string::constructBox(b1); auto bd1 = blt::string::constructBox(b2); blt::string::ascii_boxes boxes; - boxes.push_back(std::move(b1)); - boxes.push_back(std::move(b2)); + boxes.push_back(std::move(bt1)); + boxes.push_back(std::move(bt2)); auto bd2 = blt::string::constructBox(boxes); + printLines(btd.toVec()); printLines(bd.toVec()); + printLines(btd1.toVec()); printLines(bd1.toVec()); printLines(bd2.toVec());