/*
* Copyright (c) [2004-2014] Novell, Inc.
- * Copyright (c) [2016-2018] SUSE LLC
+ * Copyright (c) [2016-2020] SUSE LLC
*
* All Rights Reserved.
*
*/
-#include <locale>
#include <cmath>
+#include <vector>
#include <sstream>
#include <stdexcept>
+#include <boost/algorithm/string.hpp>
#include "HumanString.h"
#include "text.h"
+#include <snapper/Exception.h>
+
namespace snapper
{
- using std::string;
+ using namespace std;
/*
- * These are simplified versions of the functions in libstorage-ng.
+ * These are simplified versions of the functions in libstorage-ng,
+ * https://github.com/openSUSE/libstorage-ng/blob/master/storage/Utils/HumanString.h.
*/
}
- string
- get_suffix(int i)
+ vector<string>
+ get_all_suffixes(int i, bool all = true)
{
+ vector<string> ret;
+
switch (i)
{
case 0:
// TRANSLATORS: symbol for "bytes" (best keep untranslated)
- return _("B");
+ ret.push_back(_("B"));
+ if (all)
+ {
+ ret.push_back("");
+ }
+ break;
case 1:
// TRANSLATORS: symbol for "kibi bytes" (best keep untranslated)
- return _("KiB");
+ ret.push_back(_("KiB"));
+ if (all)
+ {
+ // TRANSLATORS: symbol for "kilo bytes" (best keep untranslated)
+ ret.push_back(_("kB"));
+ // TRANSLATORS: symbol for "kilo" (best keep untranslated)
+ ret.push_back(_("k"));
+ }
+ break;
case 2:
// TRANSLATORS: symbol for "mebi bytes" (best keep untranslated)
- return _("MiB");
+ ret.push_back(_("MiB"));
+ if (all)
+ {
+ // TRANSLATORS: symbol for "mega bytes" (best keep untranslated)
+ ret.push_back(_("MB"));
+ // TRANSLATORS: symbol for "mega" (best keep untranslated)
+ ret.push_back(_("M"));
+ }
+ break;
case 3:
// TRANSLATORS: symbol for "gibi bytes" (best keep untranslated)
- return _("GiB");
+ ret.push_back(_("GiB"));
+ if (all)
+ {
+ // TRANSLATORS: symbol for "giga bytes" (best keep untranslated)
+ ret.push_back(_("GB"));
+ // TRANSLATORS: symbol for "giga" (best keep untranslated)
+ ret.push_back(_("G"));
+ }
+ break;
case 4:
// TRANSLATORS: symbol for "tebi bytes" (best keep untranslated)
- return _("TiB");
+ ret.push_back(_("TiB"));
+ if (all)
+ {
+ // TRANSLATORS: symbol for "tera bytes" (best keep untranslated)
+ ret.push_back(_("TB"));
+ // TRANSLATORS: symbol for "tera" (best keep untranslated)
+ ret.push_back(_("T"));
+ }
+ break;
case 5:
// TRANSLATORS: symbol for "pebi bytes" (best keep untranslated)
- return _("PiB");
+ ret.push_back(_("PiB"));
+ if (all)
+ {
+ // TRANSLATORS: symbol for "peta bytes" (best keep untranslated)
+ ret.push_back(_("PB"));
+ // TRANSLATORS: symbol for "peta" (best keep untranslated)
+ ret.push_back(_("P"));
+ }
+ break;
case 6:
// TRANSLATORS: symbol for "exbi bytes" (best keep untranslated)
- return _("EiB");
-
- default:
- throw std::logic_error("invalid prefix");
+ ret.push_back(_("EiB"));
+ if (all)
+ {
+ // TRANSLATORS: symbol for "exa bytes" (best keep untranslated)
+ ret.push_back(_("EB"));
+ // TRANSLATORS: symbol for "exa" (best keep untranslated)
+ ret.push_back(_("E"));
+ }
+ break;
}
+
+ return ret;
}
- bool
- is_multiple_of(unsigned long long i, unsigned long long j)
+ string
+ get_suffix(int i)
{
- return i % j == 0;
+ return get_all_suffixes(i, false).front();
}
- int
- clz(unsigned long long i)
+ namespace
{
- return __builtin_clzll(i);
+
+ int
+ clz(unsigned long long i)
+ {
+ return __builtin_clzll(i);
+ }
+
+
+ // Helper functions to parse a number as int or float, multiply
+ // according to the suffix. Do all required error checks.
+
+ pair<bool, unsigned long long>
+ parse_i(const string& str, int i)
+ {
+ istringstream s(str);
+
+ unsigned long long v;
+ s >> v;
+
+ if (!s.eof())
+ return make_pair(false, 0);
+
+ if (s.fail())
+ {
+ if (v == std::numeric_limits<unsigned long long>::max())
+ SN_THROW(Exception("overflow"));
+
+ return make_pair(false, 0);
+ }
+
+ if (v != 0 && str[0] == '-')
+ SN_THROW(Exception("overflow"));
+
+ if (v > 0 && clz(v) < 10 * i)
+ SN_THROW(Exception("overflow"));
+
+ v <<= 10 * i;
+
+ return make_pair(true, v);
+ }
+
+
+ pair<bool, unsigned long long>
+ parse_f(const string& str, int i)
+ {
+ istringstream s(str);
+
+ long double v;
+ s >> v;
+
+ if (!s.eof())
+ return make_pair(false, 0);
+
+ if (s.fail())
+ {
+ if (v == std::numeric_limits<long double>::max())
+ SN_THROW(Exception("overflow"));
+
+ return make_pair(false, 0);
+ }
+
+ if (v < 0.0)
+ SN_THROW(Exception("overflow"));
+
+ v = std::round(std::ldexp(v, 10 * i));
+
+ if (v > std::numeric_limits<unsigned long long>::max())
+ SN_THROW(Exception("overflow"));
+
+ return make_pair(true, v);
+ }
+
}
string
byte_to_humanstring(unsigned long long size, int precision)
{
- const std::locale loc = std::locale();
-
// Calculate the index of the suffix to use. The index increases by 1
// when the number of leading zeros decreases by 10.
unsigned long long v = size >> 10 * i;
std::ostringstream s;
- s.imbue(loc);
s << v << ' ' << get_suffix(i);
return s.str();
}
long double v = std::ldexp((long double)(size), - 10 * i);
std::ostringstream s;
- s.imbue(loc);
s.setf(std::ios::fixed);
s.precision(precision);
s << v << ' ' << get_suffix(i);
}
}
+
+ unsigned long long
+ humanstring_to_byte(const string& str)
+ {
+ const string str_trimmed = boost::trim_copy(str);
+
+ for (int i = 0; i < num_suffixes(); ++i)
+ {
+ for (const string& suffix : get_all_suffixes(i))
+ {
+ if (boost::iends_with(str_trimmed, suffix))
+ {
+ string number = boost::trim_copy(str_trimmed.substr(0, str_trimmed.size() - suffix.size()));
+
+ pair<bool, unsigned long long> v;
+
+ v = parse_i(number, i);
+ if (v.first)
+ return v.second;
+
+ v = parse_f(number, i);
+ if (v.first)
+ return v.second;
+ }
+ }
+ }
+
+ SN_THROW(Exception("failed to parse size"));
+ __builtin_unreachable();
+ }
+
}
/*
* Copyright (c) [2004-2014] Novell, Inc.
- * Copyright (c) [2016,2018] SUSE LLC
+ * Copyright (c) [2016-2020] SUSE LLC
*
* All Rights Reserved.
*
* Return a suffix.
*
* @param i index of suffix
- * @param classic use classic locale instead of global C++ locale
* @return suffix
*/
std::string get_suffix(int i);
/**
- * Return a pretty description of a size with required precision and using
- * B, KiB, MiB, GiB, TiB, PiB or EiB as unit as appropriate. Supported
- * range is 0 B to (16 EiB - 1 B).
+ * Return a pretty description of a size with required precision and using B, KiB,
+ * MiB, GiB, TiB, PiB or EiB as unit as appropriate. Supported range is 0 B to (16 EiB
+ * - 1 B).
*
* @param size size in bytes
* @param precision number of fraction digits in output
*/
std::string byte_to_humanstring(unsigned long long size, int precision);
+
+ /**
+ * Converts a size description using B, KiB, MiB, GiB, TiB, PiB or EiB into an
+ * integer. Decimal suffixes are also allowed and treated as binary. Supported range
+ * is 0 B to (16 EiB - 1 B).
+ *
+ * @param str size string
+ * @return bytes
+ *
+ * The conversion is always case-insensitive.
+ */
+ unsigned long long humanstring_to_byte(const std::string& str);
+
}
/*
* Copyright (c) [2004-2015] Novell, Inc.
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016-2020] SUSE LLC
*
* All Rights Reserved.
*
return s << fixed << sw.read() << "s";
}
+
+ bool
+ Uuid::operator==(const Uuid& rhs) const
+ {
+ return std::equal(std::begin(value), std::end(value), std::begin(rhs.value));
+ }
+
}
/*
* Copyright (c) [2004-2015] Novell, Inc.
+ * Copyright (c) 2020 SUSE LLC
*
* All Rights Reserved.
*
~FdCloser()
{
- if (fd > -1 )
+ if (fd > -1)
::close(fd);
}
const int error_number;
};
+
+ class Uuid
+ {
+ public:
+
+ uint8_t value[16];
+
+ bool operator==(const Uuid& rhs) const;
+
+ };
+
}
#endif
/*
* Copyright (c) [2011-2015] Novell, Inc.
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016-2020] SUSE LLC
*
* All Rights Reserved.
*
size_t size = sizeof(btrfs_qgroup_inherit) + sizeof(((btrfs_qgroup_inherit*) 0)->qgroups[0]);
vector<char> buffer(size, 0);
-
+
if (qgroup != no_qgroup)
{
struct btrfs_qgroup_inherit* inherit = (btrfs_qgroup_inherit*) &buffer[0];
throw runtime_error_with_errno("ioctl(BTRFS_IOC_SYNC) failed", errno);
}
+
+ Uuid
+ get_uuid(int fd)
+ {
+ static_assert(BTRFS_UUID_SIZE == 16);
+
+ struct btrfs_ioctl_fs_info_args fs_info_args;
+ if (ioctl(fd, BTRFS_IOC_FS_INFO, &fs_info_args) < 0)
+ throw runtime_error_with_errno("ioctl(BTRFS_IOC_FS_INFO) failed", errno);
+
+ Uuid uuid;
+ std::copy(std::begin(fs_info_args.fsid), std::end(fs_info_args.fsid), std::begin(uuid.value));
+ return uuid;
+ }
+
+
+ Uuid
+ get_uuid(const string& path)
+ {
+ int fd = open(path.c_str(), O_RDONLY);
+ if (fd < 0)
+ throw runtime_error_with_errno("open failed", errno);
+
+ FdCloser fd_closer(fd);
+
+ return BtrfsUtils::get_uuid(fd);
+ }
+
}
}
/*
* Copyright (c) [2011-2015] Novell, Inc.
- * Copyright (c) 2016 SUSE LLC
+ * Copyright (c) [2016-2020] SUSE LLC
*
* All Rights Reserved.
*
#include <string>
#include <vector>
+#include "snapper/AppUtil.h"
+
namespace snapper
{
struct QGroupUsage
{
- QGroupUsage() : referenced(0), referenced_compressed(0), exclusive(0),
- exclusive_compressed(0) {}
-
- uint64_t referenced;
- uint64_t referenced_compressed;
- uint64_t exclusive;
- uint64_t exclusive_compressed;
+ uint64_t referenced = 0;
+ uint64_t referenced_compressed = 0;
+ uint64_t exclusive = 0;
+ uint64_t exclusive_compressed = 0;
};
QGroupUsage qgroup_query_usage(int fd, qgroup_t qgroup);
void sync(int fd);
+ Uuid get_uuid(int fd);
+
+ Uuid get_uuid(const string& path);
+
}
}
#include <boost/test/unit_test.hpp>
-#include <iostream>
#include <locale>
+#include <snapper/Exception.h>
#include "../client/utils/HumanString.h"
}
+unsigned long long
+test(const char* loc, const char* str)
+{
+ locale::global(locale(loc));
+
+ return humanstring_to_byte(str);
+}
+
+
BOOST_AUTO_TEST_CASE(test_byte_to_humanstring)
{
BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 0, 2), "0 B");
}
+BOOST_AUTO_TEST_CASE(test_humanstring_to_byte)
+{
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "hello"), Exception);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "0 B"), 0);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "-0 B"), 0);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "+0 B"), 0);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "42B"), 42);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "42 b"), 42);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "42"), 42);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "42b"), 42);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "42 B"), 42);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "12.4GB"), 13314398618);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "12.4 GB"), 13314398618);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "12.4 gb"), 13314398618);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "12.4g"), 13314398618);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "12.4 G"), 13314398618);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "123,456 kB"), 126418944);
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", "123.456 kB"), 126418944);
+ BOOST_CHECK_EQUAL(test("de_CH.UTF-8", "123'456 kB"), 126418944);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "123,456.789kB"), 126419752);
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", "123.456,789kB"), 126419752);
+ BOOST_CHECK_EQUAL(test("de_CH.UTF-8", "123'456.789kB"), 126419752);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "123,456.789 kB"), 126419752);
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", "123.456,789 kB"), 126419752);
+ BOOST_CHECK_EQUAL(test("de_CH.UTF-8", "123'456.789 kB"), 126419752);
+
+ BOOST_CHECK_THROW(test("en_US.UTF-8", "5 G B"), Exception);
+
+ BOOST_CHECK_THROW(test("de_DE.UTF-8", "12.34 kB"), Exception);
+ BOOST_CHECK_THROW(test("de_DE.UTF-8", "12'34 kB"), Exception);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "3.14 G"), 3371549327);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "3.14 GB"), 3371549327);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "3.14 GiB"), 3371549327);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "12345 GB"), 13255342817280);
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", "12345 GB"), 13255342817280);
+ BOOST_CHECK_EQUAL(test("de_CH.UTF-8", "12345 GB"), 13255342817280);
+
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", ".5 GiB"), 512 * MiB);
+ BOOST_CHECK_EQUAL(test("de_DE.UTF-8", ",5 GiB"), 512 * MiB);
+}
+
+
BOOST_AUTO_TEST_CASE(test_big_numbers)
{
// 1 EiB - 1 B
// 1 EiB
BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1 * EiB, 2), "1.00 EiB");
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "1 EiB"), 1 * EiB);
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "1.00 EiB"), 1 * EiB);
// 1 EiB + 1 B
BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 1 * EiB + 1 * B, 2), "1.00 EiB");
// 16 EiB - 1 B
BOOST_CHECK_EQUAL(test("en_GB.UTF-8", 16 * EiB - 1 * B, 2), "16.00 EiB");
+ BOOST_CHECK_EQUAL(test("en_GB.UTF-8", "18446744073709551615 B"), 16 * EiB - 1 * B);
+
+ // 16 EiB
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "16 EiB"), Exception);
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "18446744073709551616 B"), Exception);
+}
+
+
+BOOST_AUTO_TEST_CASE(test_ridiculous_high_numbers)
+{
+ // The unshifted value fits 64-bit IEEE but the shifted value
+ // overflows. Tests error handling if long double is 64-bit IEEE.
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "1.0E305 EiB"), Exception);
+
+ // The unshifted value fits 80-bit IEEE but the shifted value
+ // overflows. Tests error handling if long double is 80-bit IEEE.
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "1.0E4930 EiB"), Exception);
+
+ // Even the unshifted value is too high for 80-bit (and even 128-bit) IEEE.
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "1.0E5000 B"), Exception);
+}
+
+
+BOOST_AUTO_TEST_CASE(test_negative_numbers)
+{
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "-1 B"), Exception);
+ BOOST_CHECK_THROW(test("en_GB.UTF-8", "-1.0 B"), Exception);
}