2023-01-27 00:49:44 -05:00
|
|
|
/*
|
|
|
|
* Created by Brett on 26/01/23.
|
|
|
|
* Licensed under GNU General Public License V3.0
|
|
|
|
* See LICENSE file for license detail
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifndef BLT_TESTS_FORMAT_H
|
|
|
|
#define BLT_TESTS_FORMAT_H
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <utility>
|
|
|
|
#include <vector>
|
2023-07-24 02:44:25 -04:00
|
|
|
#include <blt/math/math.h>
|
2023-11-22 20:39:46 -05:00
|
|
|
#include <algorithm>
|
2023-01-27 00:49:44 -05:00
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
namespace blt::string
|
|
|
|
{
|
2023-03-05 17:21:14 -05:00
|
|
|
|
2023-11-22 20:39:46 -05:00
|
|
|
template<typename T>
|
2023-11-29 21:36:22 -05:00
|
|
|
static inline std::string withGrouping(T t, size_t group = 3)
|
|
|
|
{
|
2023-11-22 20:39:46 -05:00
|
|
|
// TODO: all this + make it faster
|
|
|
|
static_assert(std::is_integral_v<T>, "Must be integer type! (Floats currently not supported!)");
|
|
|
|
auto str = std::to_string(t);
|
|
|
|
std::string ret;
|
|
|
|
ret.reserve(str.size());
|
|
|
|
size_t count = 0;
|
2023-11-29 21:36:22 -05:00
|
|
|
for (int64_t i = str.size() - 1; i >= 0; i--)
|
|
|
|
{
|
2023-11-22 20:39:46 -05:00
|
|
|
ret += str[i];
|
2023-11-29 21:36:22 -05:00
|
|
|
if (count++ % (group) == group - 1 && i != 0)
|
2023-11-22 20:39:46 -05:00
|
|
|
ret += ',';
|
|
|
|
}
|
|
|
|
std::reverse(ret.begin(), ret.end());
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
static inline std::string fromBytes(unsigned long bytes)
|
|
|
|
{
|
|
|
|
if (bytes > 1073741824)
|
|
|
|
{
|
2023-03-05 17:06:07 -05:00
|
|
|
// gigabyte
|
2023-03-05 17:21:14 -05:00
|
|
|
return std::to_string(round_up<3>((double) bytes / 1024.0 / 1024.0 / 1024.0)) += "gb";
|
2023-11-29 21:36:22 -05:00
|
|
|
} else if (bytes > 1048576)
|
|
|
|
{
|
2023-03-05 17:06:07 -05:00
|
|
|
// megabyte
|
2023-11-29 21:36:22 -05:00
|
|
|
return std::to_string(round_up<3>((double) bytes / 1024.0 / 1024.0)) += "mb";
|
|
|
|
} else if (bytes > 1024)
|
|
|
|
{
|
2023-03-05 17:06:07 -05:00
|
|
|
// kilobyte
|
2023-11-29 21:36:22 -05:00
|
|
|
return std::to_string(round_up<3>((double) bytes / 1024.0)) += "kb";
|
|
|
|
} else
|
|
|
|
{
|
2023-03-05 17:06:07 -05:00
|
|
|
return std::to_string(bytes) += "b";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
// TODO: update table formatter to use these!
|
|
|
|
/**
|
|
|
|
* creates a line starting/ending with ending char filled between with spacing char
|
|
|
|
* @param totalLength total length to generate
|
|
|
|
* @param endingChar beginning and ending char to use
|
|
|
|
* @param spacingChar char to use for spacing
|
|
|
|
* @return a generated line string eg: +--------+
|
|
|
|
*/
|
|
|
|
std::string createLine(size_t totalLength, char endingChar, char spacingChar);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a padding string using length and spacing char
|
|
|
|
* @param length length of string to generate
|
|
|
|
* @param spacing char to use to generate padding
|
|
|
|
* @return a padding string
|
|
|
|
*/
|
|
|
|
std::string createPadding(size_t length, char spacing = ' ');
|
|
|
|
|
2023-02-08 17:22:27 -05:00
|
|
|
// TODO template the padding functions:
|
2023-01-27 00:49:44 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure that string str has expected length, pad after the string otherwise.
|
|
|
|
* @param str string to pad
|
|
|
|
* @param expectedLength expected length of the string.
|
|
|
|
* @return a space padded string
|
|
|
|
*/
|
2023-11-29 21:36:22 -05:00
|
|
|
static inline std::string postPadWithSpaces(const std::string& str, size_t expectedLength)
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
auto currentSize = (int) (str.length() - 1);
|
|
|
|
if ((int) expectedLength - currentSize <= 0)
|
|
|
|
return str;
|
|
|
|
auto paddedString = str;
|
2023-11-29 21:36:22 -05:00
|
|
|
paddedString += createPadding(expectedLength - currentSize);
|
2023-01-27 00:49:44 -05:00
|
|
|
return paddedString;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensure that string str has expected length, pad before the string otherwise.
|
|
|
|
* @param str string to pad
|
|
|
|
* @param expectedLength expected length of the string.
|
|
|
|
* @return a space padded string
|
|
|
|
*/
|
2023-11-29 21:36:22 -05:00
|
|
|
static inline std::string prePadWithSpaces(const std::string& str, size_t expectedLength)
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
auto currentSize = str.length() - 1;
|
|
|
|
auto paddedString = std::string();
|
2023-11-29 21:36:22 -05:00
|
|
|
paddedString += createPadding(expectedLength - currentSize);
|
2023-01-27 00:49:44 -05:00
|
|
|
paddedString += str;
|
|
|
|
return paddedString;
|
|
|
|
}
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
struct utf8_string
|
|
|
|
{
|
2023-07-24 02:55:03 -04:00
|
|
|
char* characters;
|
|
|
|
unsigned int size;
|
2023-01-29 15:24:33 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
// taken from java, adapted for c++.
|
2023-11-29 21:36:22 -05:00
|
|
|
static inline utf8_string createUTFString(const std::string& str)
|
|
|
|
{
|
2023-07-24 02:52:11 -04:00
|
|
|
const auto strlen = (unsigned int) str.size();
|
2023-01-29 15:24:33 -05:00
|
|
|
unsigned int utflen = strlen;
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
for (unsigned int i = 0; i < strlen; i++)
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
unsigned char c = str[i];
|
|
|
|
if (c >= 0x80 || c == 0)
|
2023-02-06 21:54:01 -05:00
|
|
|
utflen += 1;
|
2023-01-29 15:24:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (utflen > 65535 || /* overflow */ utflen < strlen)
|
|
|
|
throw "UTF Error";
|
|
|
|
|
2023-07-24 02:55:03 -04:00
|
|
|
utf8_string chars{};
|
|
|
|
chars.size = utflen + 2;
|
|
|
|
chars.characters = new char[chars.size];
|
2023-01-29 15:24:33 -05:00
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
chars.characters[count++] = (char) ((utflen >> 8) & 0xFF);
|
2023-07-27 01:47:11 -04:00
|
|
|
chars.characters[count++] = (char) ((utflen >> 0) & 0xFF);
|
2023-01-29 15:24:33 -05:00
|
|
|
|
2023-04-05 17:21:19 -04:00
|
|
|
unsigned int i = 0;
|
2023-11-29 21:36:22 -05:00
|
|
|
for (i = 0; i < strlen; i++)
|
|
|
|
{ // optimized for initial run of ASCII
|
2023-01-29 15:24:33 -05:00
|
|
|
int c = (unsigned char) str[i];
|
|
|
|
if (c >= 0x80 || c == 0) break;
|
|
|
|
chars.characters[count++] = (char) c;
|
|
|
|
}
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
for (; i < strlen; i++)
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
int c = (unsigned char) str[i];
|
2023-11-29 21:36:22 -05:00
|
|
|
if (c < 0x80 && c != 0)
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
chars.characters[count++] = (char) c;
|
2023-11-29 21:36:22 -05:00
|
|
|
} else if (c >= 0x800)
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
chars.characters[count++] = (char) (0xE0 | ((c >> 12) & 0x0F));
|
|
|
|
chars.characters[count++] = (char) (0x80 | ((c >> 6) & 0x3F));
|
|
|
|
chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F));
|
2023-11-29 21:36:22 -05:00
|
|
|
} else
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
chars.characters[count++] = (char) (0xC0 | ((c >> 6) & 0x1F));
|
|
|
|
chars.characters[count++] = (char) (0x80 | ((c >> 0) & 0x3F));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return chars;
|
|
|
|
}
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
static inline std::string getStringFromUTF8(const utf8_string& str)
|
|
|
|
{
|
2023-04-05 17:21:19 -04:00
|
|
|
int utflen = (int) str.size;
|
2023-01-29 15:24:33 -05:00
|
|
|
int c, char2, char3;
|
|
|
|
int count = 0;
|
|
|
|
int chararr_count = 0;
|
|
|
|
|
|
|
|
auto chararr = new char[utflen + 1];
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
while (count < utflen)
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
c = (int) str.characters[count] & 0xff;
|
|
|
|
if (c > 127) break;
|
|
|
|
count++;
|
|
|
|
chararr[chararr_count++] = (char) c;
|
|
|
|
}
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
while (count < utflen)
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
c = (int) str.characters[count] & 0xff;
|
2023-11-29 21:36:22 -05:00
|
|
|
switch (c >> 4)
|
|
|
|
{
|
2023-01-29 15:24:33 -05:00
|
|
|
case 0:
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
case 5:
|
|
|
|
case 6:
|
|
|
|
case 7:
|
|
|
|
/* 0xxxxxxx*/
|
|
|
|
count++;
|
|
|
|
chararr[chararr_count++] = (char) c;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
case 13:
|
|
|
|
/* 110x xxxx 10xx xxxx*/
|
|
|
|
count += 2;
|
|
|
|
if (count > utflen)
|
|
|
|
throw "malformed input: partial character at end";
|
|
|
|
char2 = (int) str.characters[count - 1];
|
|
|
|
if ((char2 & 0xC0) != 0x80)
|
|
|
|
throw "malformed input around byte " + std::to_string(count);
|
|
|
|
chararr[chararr_count++] = (char) (((c & 0x1F) << 6) |
|
|
|
|
(char2 & 0x3F));
|
2023-04-05 17:21:19 -04:00
|
|
|
break;
|
2023-01-29 15:24:33 -05:00
|
|
|
case 14:
|
|
|
|
/* 1110 xxxx 10xx xxxx 10xx xxxx */
|
|
|
|
count += 3;
|
|
|
|
if (count > utflen)
|
|
|
|
throw "malformed input: partial character at end";
|
|
|
|
char2 = (int) str.characters[count - 2];
|
|
|
|
char3 = (int) str.characters[count - 1];
|
|
|
|
if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80))
|
|
|
|
throw "malformed input around byte " + std::to_string(count - 1);
|
|
|
|
chararr[chararr_count++] = (char) (((c & 0x0F) << 12) |
|
|
|
|
((char2 & 0x3F) << 6) |
|
|
|
|
((char3 & 0x3F) << 0));
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* 10xx xxxx, 1111 xxxx */
|
|
|
|
throw "malformed input around byte " + std::to_string(count);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
chararr[utflen] = '\0';
|
2023-11-29 21:36:22 -05:00
|
|
|
std::string strs{chararr};
|
2023-01-29 15:24:33 -05:00
|
|
|
delete[] chararr;
|
|
|
|
return strs;
|
|
|
|
}
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
struct TableColumn
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
std::string columnName;
|
|
|
|
size_t maxColumnLength = 0;
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
TableColumn(std::string columnName): columnName(std::move(columnName))
|
|
|
|
{}
|
2023-01-27 00:49:44 -05:00
|
|
|
};
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
struct TableRow
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
std::vector<std::string> rowValues;
|
|
|
|
};
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
class TableFormatter
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
private:
|
2023-01-29 17:10:36 -05:00
|
|
|
std::string m_tableName;
|
2023-01-27 00:49:44 -05:00
|
|
|
int m_columnPadding;
|
|
|
|
int m_maxColumnWidth;
|
|
|
|
std::vector<TableColumn> columns;
|
|
|
|
std::vector<TableRow> rows;
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-01-29 17:10:36 -05:00
|
|
|
std::string generateTopSeparator(size_t size);
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-01-27 00:49:44 -05:00
|
|
|
std::string generateColumnHeader();
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-01-27 00:49:44 -05:00
|
|
|
std::string generateSeparator(size_t size);
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-01-27 00:49:44 -05:00
|
|
|
void updateMaxColumnLengths();
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
[[nodiscard]] inline size_t columnSize(const TableColumn& column) const
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
return column.columnName.size() + m_columnPadding * 2;
|
|
|
|
}
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-01-27 00:49:44 -05:00
|
|
|
public:
|
2023-01-29 17:10:36 -05:00
|
|
|
explicit TableFormatter(std::string tableName = "", int columnPadding = 2, int maxColumnWidth = 500):
|
2023-11-29 21:36:22 -05:00
|
|
|
m_tableName(std::move(tableName)), m_columnPadding(columnPadding), m_maxColumnWidth(maxColumnWidth)
|
|
|
|
{}
|
2023-01-27 00:49:44 -05:00
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
inline void addColumn(const TableColumn& column)
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
columns.push_back(column);
|
|
|
|
}
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
inline void addRow(TableRow row)
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
if (row.rowValues.size() > columns.size())
|
|
|
|
throw "Cannot insert more rows than columns!\n";
|
|
|
|
// ensure every row populates every column. This is important as the table generator assumes that all rows are complete!
|
|
|
|
if (row.rowValues.size() < columns.size())
|
|
|
|
for (auto i = row.rowValues.size(); i < columns.size(); i++)
|
|
|
|
row.rowValues.emplace_back(" ");
|
|
|
|
rows.push_back(std::move(row));
|
|
|
|
}
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
inline void addRow(const std::initializer_list<std::string>& values)
|
|
|
|
{
|
2023-01-27 00:49:44 -05:00
|
|
|
TableRow row;
|
|
|
|
for (const auto& value : values)
|
|
|
|
row.rowValues.push_back(value);
|
|
|
|
addRow(row);
|
|
|
|
}
|
2023-01-27 10:45:18 -05:00
|
|
|
|
2023-01-27 00:49:44 -05:00
|
|
|
std::vector<std::string> createTable(bool top = false, bool bottom = false);
|
|
|
|
};
|
|
|
|
|
2023-12-01 12:02:20 -05:00
|
|
|
class BinaryTreeFormatter
|
2023-11-29 21:36:22 -05:00
|
|
|
{
|
|
|
|
public:
|
2023-12-01 12:02:20 -05:00
|
|
|
// data classes
|
|
|
|
struct TreeFormat
|
|
|
|
{
|
|
|
|
int verticalSpacing;
|
|
|
|
int horizontalSpacing;
|
|
|
|
|
|
|
|
int verticalPadding;
|
|
|
|
int horizontalPadding;
|
|
|
|
|
|
|
|
// should we remove preceding spaces?
|
|
|
|
bool collapse = false;
|
|
|
|
|
|
|
|
TreeFormat(): verticalSpacing(2), horizontalSpacing(4), verticalPadding(1), horizontalPadding(4)
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2023-11-29 21:36:22 -05:00
|
|
|
struct Node
|
|
|
|
{
|
|
|
|
std::string data;
|
|
|
|
Node* left = nullptr;
|
|
|
|
Node* right = nullptr;
|
|
|
|
|
|
|
|
explicit Node(std::string data): data(std::move(data))
|
|
|
|
{}
|
|
|
|
|
|
|
|
Node* with(Node* l, Node* r = nullptr)
|
|
|
|
{
|
|
|
|
left = l;
|
|
|
|
right = r;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
~Node()
|
|
|
|
{
|
|
|
|
delete left;
|
|
|
|
delete right;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
private:
|
2023-12-01 12:02:20 -05:00
|
|
|
TreeFormat format;
|
2023-11-29 21:36:22 -05:00
|
|
|
|
2023-12-01 15:03:47 -05:00
|
|
|
Node* root = nullptr;
|
2023-11-29 21:36:22 -05:00
|
|
|
public:
|
2023-12-01 15:03:47 -05:00
|
|
|
explicit BinaryTreeFormatter(std::string rootData, TreeFormat format = {}): format(std::move(format)), root(new Node(std::move(rootData)))
|
2023-11-29 21:36:22 -05:00
|
|
|
{}
|
|
|
|
|
|
|
|
std::vector<std::string> generateBox(Node* node) const;
|
|
|
|
|
|
|
|
inline Node* getRoot()
|
|
|
|
{
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<std::string> construct();
|
|
|
|
|
2023-12-01 12:02:20 -05:00
|
|
|
BinaryTreeFormatter()
|
2023-11-29 21:36:22 -05:00
|
|
|
{
|
|
|
|
delete root;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-01-27 00:49:44 -05:00
|
|
|
}
|
|
|
|
#endif //BLT_TESTS_FORMAT_H
|