Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

locale-aware byte size formatter #1542

Merged
merged 3 commits into from
Dec 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/build_and test_on_msys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
run: cmake --build build
- name: Run tests
run: "PATH=$PATH:$PWD/libosmscout:$PWD/libosmscout-import:$PWD/libosmscout-map:$PWD/libosmscout-test
LC_ALL=C
ctest -j 4 --output-on-failure --exclude-regex PerformanceTest"
working-directory: build

Expand Down Expand Up @@ -69,6 +70,7 @@ jobs:
- name: Build project
run: meson compile -C debug
- name: Run tests
run: meson test -C debug --print-errorlogs
run: "LC_ALL=C
meson test -C debug --print-errorlogs"
env:
LANG: en_US.utf8
15 changes: 15 additions & 0 deletions Tests/src/StringUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,23 @@ TEST_CASE("Local aware number to string")
{
osmscout::Locale locale;
locale.SetThousandsSeparator(" ");
locale.SetDecimalSeparator(".");
REQUIRE(osmscout::NumberToString(1002030, locale) == "1 002 030");
REQUIRE(osmscout::NumberToString(-1002030, locale) == "-1 002 030");

REQUIRE(osmscout::FloatToString(M_PI, locale, 2) == "3.14");
REQUIRE(osmscout::FloatToString(-1002030.123456, locale, 6) == "-1 002 030.123 456");
REQUIRE(osmscout::FloatToString(-1002030.125, locale, 2) == "-1 002 030.13");
}

TEST_CASE("Byte size to string")
{
osmscout::Locale locale;
locale.SetThousandsSeparator("'");
locale.SetDecimalSeparator(",");
locale.SetUnitsSeparator(" ");
REQUIRE(osmscout::ByteSizeToString(1063256064.0, locale) == "1'014,0 MiB");
REQUIRE(osmscout::ByteSizeToString(6406241158.0, locale) == "6,0 GiB");
}

TEST_CASE("Trim string")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public slots:
int textPixelSize=14;
int textPadding=4;

Locale locale=Locale::ByEnvironment();
Locale locale=Locale::ByEnvironmentSafe();
};

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public slots:
private:
QThread *thread;
SettingsRef settings;
DistanceUnitSystem units{Locale::ByEnvironment().GetDistanceUnits()}; // TODO: make possible to override
DistanceUnitSystem units{Locale::ByEnvironmentSafe().GetDistanceUnits()}; // TODO: make possible to override
DBThreadRef dbThread;
QTimer timer;
std::optional<Bearing> lastBearing;
Expand Down
2 changes: 1 addition & 1 deletion libosmscout-client/src/osmscoutclient/MapManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ CancelableFuture<bool> MapManager::LookupDatabases()
// https://en.cppreference.com/w/cpp/filesystem/is_directory
// https://en.cppreference.com/w/cpp/filesystem/status
if (!std::filesystem::exists(lookupDir) || !std::filesystem::is_directory(lookupDir)) {
osmscout::log.Warn() << "Lookup dir" << lookupDir.string() << "doesn't exist or isn't a directory";
osmscout::log.Warn() << "Lookup dir " << lookupDir.string() << " doesn't exist or isn't a directory";
continue;
}

Expand Down
2 changes: 1 addition & 1 deletion libosmscout-map/src/osmscoutmap/MapParameter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ namespace osmscout {
warnObjectCountLimit(0),
warnCoordCountLimit(0),
showAltLanguage(false),
locale{Locale::ByEnvironment()}
locale{Locale::ByEnvironmentSafe()}
{
// no code
}
Expand Down
29 changes: 28 additions & 1 deletion libosmscout/include/osmscout/util/Locale.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <osmscout/system/Compiler.h>

#include <osmscout/util/Distance.h>
#include <osmscout/log/Logger.h>

#include <string>

Expand Down Expand Up @@ -102,7 +103,33 @@ namespace osmscout {
}

public:
static Locale ByEnvironment(std::locale locale = std::locale(""));

/** Creates Locale from provided std::locale
*/
static Locale FromStdLocale(std::locale locale);

/** Creates Locale defined by current environment
*
* @throw std::runtime_error when locale defined by environment is undefined
*/
static Locale ByEnvironment()
{
return FromStdLocale(std::locale(""));
}

/** Creates Locale defined by current environment,
* it is not throwing exception when environment locale is incorrect,
* it just return default Locale instead.
*/
static Locale ByEnvironmentSafe()
{
try {
return ByEnvironment();
} catch (const std::runtime_error &e) {
log.Warn() << "Failed to get environment locale: " << e.what();
return Locale();
}
}
};
}

Expand Down
20 changes: 17 additions & 3 deletions libosmscout/include/osmscout/util/String.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,20 @@ namespace osmscout {
*
* @param value
* @param locale
* @return
* @return UTF-8 string
*/
extern OSMSCOUT_API std::string NumberToString(long value, const Locale &locale);

/**
* Returns locale-aware string representation of number
*
* @param value
* @param locale
* @param precision
* @return UTF-8 string
*/
extern OSMSCOUT_API std::string FloatToString(double value, const Locale &locale, uint32_t precision = 3);

/**
* \ingroup Util
* Returns the numerical value of the given character, if the character
Expand Down Expand Up @@ -403,9 +413,13 @@ namespace osmscout {
/**
* \ingroup Util
*
* Prints byte size with short, human readable form by ISO/IEC 80000 standard.
* It means that KiB stands for 1024 bytes, MiB for 1024^2, GiB 1024^3...
*
* Returned string is locale aware, UTF-8 encoded
*/
extern OSMSCOUT_API std::string ByteSizeToString(FileOffset size);
extern OSMSCOUT_API std::string ByteSizeToString(double size);
extern OSMSCOUT_API std::string ByteSizeToString(FileOffset size, const Locale &locale = Locale::ByEnvironmentSafe());
extern OSMSCOUT_API std::string ByteSizeToString(double size, const Locale &locale = Locale::ByEnvironmentSafe());

/**
* \ingroup Util
Expand Down
2 changes: 1 addition & 1 deletion libosmscout/src/osmscout/util/Locale.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace osmscout {
}
}

Locale Locale::ByEnvironment(std::locale cppLocale)
Locale Locale::FromStdLocale(std::locale cppLocale)
{
Locale locale;

Expand Down
50 changes: 34 additions & 16 deletions libosmscout/src/osmscout/util/String.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ namespace osmscout {
std::string NumberToString(long value, const Locale &locale)
{
std::stringstream ss;
ss.imbue(std::locale("C"));
if (std::abs(value) < 1000 || locale.GetThousandsSeparator().empty()){
ss << value;
}else{
Expand All @@ -214,6 +215,27 @@ namespace osmscout {
return ss.str();
}

extern OSMSCOUT_API std::string FloatToString(double value, const Locale &locale, uint32_t precision)
{
std::stringstream ss;
double order = std::pow(10, precision);
value = std::round(value * order) / order;
ss << NumberToString(static_cast<long>(value), locale);

if (precision > 0) {
ss << locale.GetDecimalSeparator();
double fractionNum = std::abs(value) - std::floor(std::abs(value));
std::string fraction = NumberToString(fractionNum * order, Locale());
for (size_t i = 0; i < fraction.size(); i++) {
if (i > 0 && i % 3 == 0 && i < fraction.size() - 1) {
ss << locale.GetThousandsSeparator();
}
ss << fraction[i];
}
}
return ss.str();
}

bool GetDigitValue(char digit, size_t& result)
{
switch (digit) {
Expand Down Expand Up @@ -329,36 +351,32 @@ namespace osmscout {
return wordCount;
}

std::string ByteSizeToString(FileOffset value)
std::string ByteSizeToString(FileOffset value, const Locale &locale)
{
return ByteSizeToString((double)value);
return ByteSizeToString(static_cast<double>(value), locale);
}

std::string ByteSizeToString(double value)
std::string ByteSizeToString(double value, const Locale &locale)
{
std::stringstream buffer;

buffer.setf(std::ios::fixed);
buffer << std::setprecision(1);

if (value<1.0 && value>-1) {
buffer << "0 B";
buffer << "0" << locale.GetUnitsSeparator() << "B";
}
else if (ceil(value)>=1024.0*1024*1024*1024) {
buffer << value/(1024.0*1024*1024*1024) << " TiB";
else if (ceil(value)>=std::pow(1024.0, 4.0)) {
buffer << FloatToString(value/std::pow(1024.0, 4.0), locale, 1) << locale.GetUnitsSeparator() << "TiB";
}
else if (ceil(value)>=1024.0*1024*1024) {
buffer << value/(1024.0*1024*1024) << " GiB";
else if (ceil(value)>=std::pow(1024.0, 3.0)) {
buffer << FloatToString(value/std::pow(1024.0, 3.0), locale, 1) << locale.GetUnitsSeparator() << "GiB";
}
else if (ceil(value)>=1024.0*1024) {
buffer << value/(1024.0*1024) << " MiB";
else if (ceil(value)>=std::pow(1024.0, 2.0)) {
buffer << FloatToString(value/std::pow(1024.0, 2.0), locale, 1) << locale.GetUnitsSeparator() << "MiB";
}
else if (ceil(value)>=1024.0) {
buffer << value/1024.0 << " KiB";
buffer << FloatToString(value/1024.0, locale, 1) << locale.GetUnitsSeparator() << "KiB";
}
else {
buffer << std::setprecision(0);
buffer << value << " B";
buffer << FloatToString(value, locale, 0) << locale.GetUnitsSeparator() << "B";
}

return buffer.str();
Expand Down