From e01c24cef73d2cb70088af1381b90924129a112f Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sat, 19 Oct 2019 18:20:00 +0100 Subject: [PATCH] improve human_friendly_bytesize to cope with sizes up to 16 exibytes --- resources/FatMessages | 9 +++++- test/utils.c | 33 ++++++++++++-------- utils/string.h | 2 +- utils/utils.c | 72 ++++++++++++++++++++++++++++++++++--------- 4 files changed, 87 insertions(+), 29 deletions(-) diff --git a/resources/FatMessages b/resources/FatMessages index 912586674..12d81bd5e 100644 --- a/resources/FatMessages +++ b/resources/FatMessages @@ -119,6 +119,7 @@ nl.all.FrameDrag:frames aan het aanpassen # Units # ===== # +# Decimal prefix en.all.Bytes: B de.all.Bytes: B fr.all.Bytes: octets @@ -139,7 +140,13 @@ de.all.GBytes: GB fr.all.GBytes: Go it.all.GBytes: GB nl.all.GBytes: GB - +# Binary prefix +en.all.KiBytes: KiB +en.all.MiBytes: MiB +en.all.GiBytes: GiB +en.all.TiBytes: TiB +en.all.PiBytes: PiB +en.all.EiBytes: EiB # Content Forms # ============= diff --git a/test/utils.c b/test/utils.c index 3d5319a28..9fe6747c3 100644 --- a/test/utils.c +++ b/test/utils.c @@ -37,22 +37,31 @@ #define SLEN(x) (sizeof((x)) - 1) struct test_pairs { - const unsigned long test; + const unsigned long long int test; const char* res; }; static const struct test_pairs human_friendly_bytesize_test_vec[] = { - { 0, "0.00Bytes" }, - { 1024, "1024.00Bytes" }, - { 1025, "1.00kBytes" }, - { 1048576, "1024.00kBytes" }, - { 1048577, "1.00MBytes" }, - { 1073741824, "1024.00MBytes" }, - { 1073741888, "1024.00MBytes" }, /* spot the rounding error */ - { 1073741889, "1.00GBytes" }, - { 2147483648, "2.00GBytes" }, - { 3221225472, "3.00GBytes" }, - { 4294967295, "4.00GBytes" }, + { 0ULL, "0Bytes" }, + { 0x2AULL, "42Bytes" }, + { 0x400ULL, "1024Bytes" }, + { 0x401ULL, "1.00KiBytes" }, + { 0xA9AEULL, "42.42KiBytes" }, + { 0x100000ULL, "1024.00KiBytes" }, + { 0x100001ULL, "1.00MiBytes" }, + { 0x2A6B852ULL, "42.42MiBytes" }, + { 0x40000000ULL, "1024.00MiBytes" }, + { 0x40000001ULL, "1.00GiBytes" }, + { 0x80000000ULL, "2.00GiBytes" }, + { 0xC0000000ULL, "3.00GiBytes" }, + { 0x100000000ULL, "4.00GiBytes" }, + { 0x10000000000ULL, "1024.00GiBytes" }, + { 0x10000000001ULL, "1.00TiBytes" }, + { 0x4000000000000ULL, "1024.00TiBytes" }, + { 0x4000000000001ULL, "1.00PiBytes" }, + { 0x1000000000000000ULL, "1024.00PiBytes" }, + { 0x1000000000000100ULL, "1.00EiBytes" }, /* precision loss */ + { 0xFFFFFFFFFFFFFFFFULL, "16.00EiBytes" }, }; /** diff --git a/utils/string.h b/utils/string.h index 03d891700..abb343154 100644 --- a/utils/string.h +++ b/utils/string.h @@ -64,7 +64,7 @@ char *cnv_space2nbsp(const char *s); * @param bytesize The size in bytes. * @return A human readable string representing the size. */ -char *human_friendly_bytesize(unsigned long bytesize); +char *human_friendly_bytesize(unsigned long long int bytesize); /** diff --git a/utils/utils.c b/utils/utils.c index aec0116a0..c5c1529a3 100644 --- a/utils/utils.c +++ b/utils/utils.c @@ -192,26 +192,43 @@ nserror snstrjoin(char **str, size_t *size, char sep, size_t nelm, ...) /** * The size of buffers within human_friendly_bytesize. * - * We can have a fairly good estimate of how long the buffer needs to - * be. The unsigned long can store a value representing a maximum - * size of around 4 GB. Therefore the greatest space required is to - * represent 1023MB. Currently that would be represented as "1023MB" - * so 12 including a null terminator. Ideally we would be able to - * know this value for sure, in the mean time the following should - * suffice. + * We can have a fairly good estimate of the output buffers maximum length. + * + * The unsigned long long int can store a value representing a maximum + * size of 16 EiB (exibytes). Therefore the greatest space required is to + * represent 1023 PiB. + * Currently that would be represented as "1023.00PiBytes" in english + * giving a 15 byte length including a null terminator. + * Ideally we would be able to accurately know this length for other + * languages, in the mean time a largeish buffer size is selected + * and should suffice. */ -#define BYTESIZE_BUFFER_SIZE 20 +#define BYTESIZE_BUFFER_SIZE 32 /* exported interface documented in utils/string.h */ -char *human_friendly_bytesize(unsigned long bsize) { +char *human_friendly_bytesize(unsigned long long int bsize) { static char buffer1[BYTESIZE_BUFFER_SIZE]; static char buffer2[BYTESIZE_BUFFER_SIZE]; static char buffer3[BYTESIZE_BUFFER_SIZE]; static char *curbuffer = buffer3; - enum {bytes, kilobytes, megabytes, gigabytes} unit = bytes; - static char units[][7] = {"Bytes", "kBytes", "MBytes", "GBytes"}; - - float bytesize = (float)bsize; + enum { + bytes, + kilobytes, + megabytes, + gibibytes, + tebibytes, + pebibytes, + exbibytes } unit = bytes; + static const char *const units[] = { + "Bytes", + "KiBytes", + "MiBytes", + "GiBytes", + "TiBytes", + "PiBytes", + "EiBytes" }; + double bytesize = (double)bsize; + const char *fmt; if (curbuffer == buffer1) curbuffer = buffer2; @@ -232,10 +249,35 @@ char *human_friendly_bytesize(unsigned long bsize) { if (bytesize > 1024) { bytesize /= 1024; - unit = gigabytes; + unit = gibibytes; } - snprintf(curbuffer, BYTESIZE_BUFFER_SIZE, "%3.2f%s", bytesize, messages_get(units[unit])); + if (bytesize > 1024) { + bytesize /= 1024; + unit = tebibytes; + } + + if (bytesize > 1024) { + bytesize /= 1024; + unit = pebibytes; + } + + if (bytesize > 1024) { + bytesize /= 1024; + unit = exbibytes; + } + + if (unit == bytes) { + fmt = "%.0f%s"; + } else { + fmt = "%3.2f%s"; + } + + snprintf(curbuffer, + BYTESIZE_BUFFER_SIZE, + fmt, + bytesize, + messages_get(units[unit])); return curbuffer; }