#include "triton/common/table_printer.h"
#ifdef _WIN32
#define NOMINMAX
#include <Windows.h>
#else
#include <sys/ioctl.h>
#include <unistd.h>
#endif
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <memory>
#include <numeric>
#include <sstream>
#include <string>
#include <vector>
namespace triton { namespace common {
void
TablePrinter::InsertRow(const std::vector<std::string>& row)
{
std::vector<std::vector<std::string>> table_row;
size_t max_height = 0;
for (size_t i = 0; i < row.size(); ++i) {
table_row.push_back(std::vector<std::string>{});
std::stringstream ss(row[i]);
std::string line;
size_t max_width = 0;
while (std::getline(ss, line, '\n')) {
table_row[i].push_back(line);
if (line.size() > max_width)
max_width = line.size();
}
if (max_width > max_widths_[i])
max_widths_[i] = max_width;
size_t number_of_lines = table_row[i].size();
if (max_height < number_of_lines)
max_height = number_of_lines;
}
max_heights_.push_back(max_height);
data_.emplace_back(table_row);
}
void
TablePrinter::FairShare()
{
size_t array_size = max_widths_.size();
std::vector<size_t> idx(array_size);
iota(idx.begin(), idx.end(), 0);
stable_sort(idx.begin(), idx.end(), [this](size_t i1, size_t i2) {
return this->max_widths_[i1] < this->max_widths_[i2];
});
size_t loop_index = 1;
for (auto itr = idx.begin(); itr != idx.end(); ++itr) {
if (max_widths_[*itr] < shares_[*itr]) {
float excess = shares_[*itr] - max_widths_[*itr];
shares_[*itr] -= excess;
if (itr == idx.end() - 1)
break;
auto update_itr = idx.begin() + (itr - idx.begin() + 1);
float excess_per_column = excess / (array_size - loop_index);
for (; update_itr != idx.end(); ++update_itr) {
shares_[*update_itr] += excess_per_column;
excess -= excess_per_column;
}
}
++loop_index;
}
for (auto itr = idx.begin(); itr != idx.end(); ++itr) {
shares_[*itr] = (size_t)shares_[*itr];
}
for (size_t i = 0; i < data_.size(); i++) {
auto current_row = data_[i];
for (size_t j = 0; j < current_row.size(); j++) {
for (size_t line_index = 0; line_index < current_row[j].size();
line_index++) {
std::string line = current_row[j][line_index];
size_t num_rows = (line.size() + shares_[j] - 1) / shares_[j];
if (num_rows > 1) {
data_[i][j].erase(data_[i][j].begin() + line_index);
for (size_t k = 0; k < num_rows; k++) {
size_t start_index =
std::min((size_t)(k * shares_[j]), line.size());
size_t end_index =
std::min((size_t)((k + 1) * shares_[j]), line.size());
data_[i][j].insert(
data_[i][j].begin() + line_index + k,
line.substr(start_index, end_index - start_index));
}
line_index += num_rows - 1;
}
if (max_heights_[i] < (num_rows - 1 + current_row[j].size()))
max_heights_[i] += num_rows - 1;
}
}
}
}
void
TablePrinter::AddRow(std::stringstream& table, size_t row_index)
{
auto row = data_[row_index];
size_t max_height = max_heights_[row_index];
for (size_t j = 0; j < max_height; j++) {
table << "|" << std::left;
for (size_t i = 0; i < row.size(); i++) {
if (j < row[i].size())
table << " " << std::setw(shares_[i]) << row[i][j] << " |";
else
table << " " << std::setw(shares_[i]) << " "
<< " |";
}
if (j != max_height - 1)
table << "\n";
}
table << "\n";
}
void
TablePrinter::AddRowDivider(std::stringstream& table)
{
table << "+";
for (const auto& share : shares_) {
for (size_t i = 0; i < share + 2; i++) table << "-";
table << "+";
}
table << "\n";
}
std::string
TablePrinter::PrintTable()
{
std::stringstream table;
table << "\n";
FairShare();
AddRowDivider(table);
AddRow(table, 0);
AddRowDivider(table);
for (size_t j = 1; j < data_.size(); j++) {
AddRow(table, j);
}
AddRowDivider(table);
return table.str();
}
TablePrinter::TablePrinter(const std::vector<std::string>& headers)
{
size_t column_size = 500;
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
int ret = GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
if (ret && (csbi.dwSize.X != 0)) {
column_size = csbi.dwSize.X;
}
#else
struct winsize terminal_size;
int status = ioctl(STDOUT_FILENO, TIOCGWINSZ, &terminal_size);
if ((status == 0) && (terminal_size.ws_col != 0)) {
column_size = terminal_size.ws_col;
}
#endif
for (size_t i = 0; i < headers.size(); ++i) {
max_widths_.emplace_back(0);
}
size_t number_of_columns = headers.size();
size_t terminal_width =
column_size - (2 * number_of_columns) - (number_of_columns + 1);
int equal_share = terminal_width / headers.size();
for (size_t i = 0; i < headers.size(); ++i) {
shares_.emplace_back(equal_share);
terminal_width -= equal_share;
}
InsertRow(headers);
}
}}