Improve conversion of bytes to human readable string

Fixes issues like showing 0 MiB when the value is just slightly less then 1 MiB.
This commit is contained in:
eelke 2024-04-12 13:26:10 +02:00
parent db2594a87c
commit 5bdd3fa95d
7 changed files with 125 additions and 44 deletions

View file

@ -5,6 +5,7 @@
#include "ResultTableModelUtil.h" #include "ResultTableModelUtil.h"
#include "CustomDataRole.h" #include "CustomDataRole.h"
#include "AbstractEditorFactory.h" #include "AbstractEditorFactory.h"
#include "utils/HumanReadableBytes.h"
PgLabItemDelegate::PgLabItemDelegate(QObject *parent) PgLabItemDelegate::PgLabItemDelegate(QObject *parent)
: QStyledItemDelegate(parent) : QStyledItemDelegate(parent)
@ -111,37 +112,11 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
else { else {
forground_color = GetDefaultColorForType(oid); forground_color = GetDefaultColorForType(oid);
if (meaning == DataMeaning::Bytes) { if (meaning == DataMeaning::Bytes) {
QString suffix; option->text = HandleBytes(value.toLongLong(), forground_color);
auto s = value.toLongLong();
double val;
if (s > 1024 * 1024 * 1000) {
val = s / (1024 * 1024 * 1024);
suffix = "GiB";
forground_color = QColorConstants::Svg::darkorange;
option->font.setBold(true);
}
else if (s > 1024 * 1000) {
val = s / (1024 * 1024);
suffix = "MiB";
forground_color = QColorConstants::Svg::darkgoldenrod;
}
else if (s > 1000) {
val = s / 1024;
suffix = "KiB";
forground_color = QColorConstants::Svg::darkgreen;
}
else {
val = s;
suffix = "B";
forground_color = QColorConstants::Svg::darkblue;
}
option->text = QString{ "%1 %2" }.arg(val, 0, 'g', 3).arg(suffix) ;
} }
else { else {
auto str = value.toString(); auto str = value.toString();
auto s = str.left(100); auto s = str.left(100);
// auto f = s.indexOf('\n');
// option->text = ((f > 0) ? s.left(f) : s).toString();
option->text = s; option->text = s;
} }
} }
@ -161,6 +136,24 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
option->styleObject = nullptr; option->styleObject = nullptr;
} }
QString PgLabItemDelegate::HandleBytes(qlonglong s, QColor &forground_color) const
{
auto str = HumanReadableBytes(s);
if (s > 1024 * 1024 * 1024) {
forground_color = QColorConstants::Svg::darkorange;
}
else if (s > 1024 * 1024) {
forground_color = QColorConstants::Svg::darkgoldenrod;
}
else if (s > 1024) {
forground_color = QColorConstants::Svg::darkgreen;
}
else {
forground_color = QColorConstants::Svg::darkblue;
}
return QString::fromStdString(str);
}
void PgLabItemDelegate::paint(QPainter *painter, void PgLabItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const const QStyleOptionViewItem &option, const QModelIndex &index) const
{ {

View file

@ -30,6 +30,7 @@ protected:
private: private:
AbstractEditorFactory *m_editorFactory = nullptr; AbstractEditorFactory *m_editorFactory = nullptr;
QString HandleBytes(qlonglong s, QColor &forground_color) const;
}; };
#endif // PGLABITEMDELEGATE_H #endif // PGLABITEMDELEGATE_H

View file

@ -87,7 +87,8 @@ SOURCES += \
catalog/PgLanguage.cpp \ catalog/PgLanguage.cpp \
catalog/PgAcl.cpp \ catalog/PgAcl.cpp \
catalog/PgSequence.cpp \ catalog/PgSequence.cpp \
catalog/PgSequenceContainer.cpp catalog/PgSequenceContainer.cpp \
utils/HumanReadableBytes.cpp
HEADERS += \ HEADERS += \
Pglablib.h \ Pglablib.h \
@ -162,7 +163,8 @@ HEADERS += \
catalog/PgLanguage.h \ catalog/PgLanguage.h \
catalog/PgAcl.h \ catalog/PgAcl.h \
catalog/PgSequence.h \ catalog/PgSequence.h \
catalog/PgSequenceContainer.h catalog/PgSequenceContainer.h \
utils/HumanReadableBytes.h
unix { unix {
target.path = /usr/lib target.path = /usr/lib

View file

@ -0,0 +1,56 @@
#include "HumanReadableBytes.h"
#include <iomanip>
#include <sstream>
using namespace std;
namespace {
struct scale {
int64_t scale;
const char* prefix;
};
void fmt(std::ostringstream &o, double d)
{
;
if (d < 10.0)
{
o << fixed << setprecision(2) << d;
//return std::format("{:.3g}", d);
}
else if (d < 100.0)
{
o << fixed << setprecision(1) << d;
//return std::format("{:.3g}", d);
}
else
{
o << fixed << setprecision(0) << d;
}
}
}
std::string HumanReadableBytes(uint64_t bytes)
{
static scale scales[] = {
{ 1024ll * 1024 * 1024 * 1024, "Ti" },
{ 1024ll * 1024 * 1024, "Gi" },
{ 1024ll * 1024, "Mi" },
{ 1024ll, "ki" },
};
std::ostringstream out;
for (int i = 0; i < (sizeof(scales) / sizeof(scale)); ++i)
{
if (bytes >= scales[i].scale)
{
fmt(out, bytes / double(scales[i].scale));
out << " " << scales[i].prefix << "B";
return out.str();
}
}
out << bytes << " B";
return out.str();
}

View file

@ -0,0 +1,5 @@
#pragma once
#include <string>
std::string HumanReadableBytes(uint64_t bytes);

View file

@ -18,6 +18,7 @@ SOURCES += main.cpp \
tst_ConvertLangToSqlString.cpp \ tst_ConvertLangToSqlString.cpp \
tst_ConvertToMultiLineCString.cpp \ tst_ConvertToMultiLineCString.cpp \
tst_ExplainJsonParser.cpp \ tst_ExplainJsonParser.cpp \
tst_HumanReadableBytes.cpp \
tst_escapeConnectionStringValue.cpp \ tst_escapeConnectionStringValue.cpp \
tst_expected.cpp \ tst_expected.cpp \
tst_SqlLexer.cpp \ tst_SqlLexer.cpp \

View file

@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "PrintTo_Qt.h"
#include "utils/HumanReadableBytes.h"
TEST(HumanReadableBytesTest, bytes7)
{
auto s = HumanReadableBytes(7);
ASSERT_EQ("7 B", s);
}
TEST(HumanReadableBytesTest, bytes1023)
{
auto s = HumanReadableBytes(1023);
ASSERT_EQ("1023 B", s);
}
TEST(HumanReadableBytesTest, bytes1k)
{
auto s = HumanReadableBytes(1024);
ASSERT_EQ("1.00 kiB", s);
}