From 3c9f3681d0b4af09c1cbf04f92fdfb72bd81ad7b Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 31 Aug 2008 10:13:54 -0500 Subject: [SCSI] lib: add generic helper to print sizes rounded to the correct SI range This patch adds the ability to print sizes in either units of 10^3 (SI) or 2^10 (Binary) units. It rounds up to three significant figures and can be used for either memory or storage capacities. Oh, and I'm fully aware that 64 bits is only 16EiB ... the Zetta and Yotta units are added for future proofing against the day we have 128 bit computers ... [fujita.tomonori@lab.ntt.co.jp: fix missed unsigned long long cast] Signed-off-by: James Bottomley --- lib/string_helpers.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 lib/string_helpers.c (limited to 'lib/string_helpers.c') diff --git a/lib/string_helpers.c b/lib/string_helpers.c new file mode 100644 index 00000000000..8347925030f --- /dev/null +++ b/lib/string_helpers.c @@ -0,0 +1,64 @@ +/* + * Helpers for formatting and printing strings + * + * Copyright 31 August 2008 James Bottomley + */ +#include +#include +#include +#include + +/** + * string_get_size - get the size in the specified units + * @size: The size to be converted + * @units: units to use (powers of 1000 or 1024) + * @buf: buffer to format to + * @len: length of buffer + * + * This function returns a string formatted to 3 significant figures + * giving the size in the required units. Returns 0 on success or + * error on failure. @buf is always zero terminated. + * + */ +int string_get_size(u64 size, const enum string_size_units units, + char *buf, int len) +{ + const char *units_10[] = { "B", "KB", "MB", "GB", "TB", "PB", + "EB", "ZB", "YB", NULL}; + const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", + "EiB", "ZiB", "YiB", NULL }; + const char **units_str[] = { + [STRING_UNITS_10] = units_10, + [STRING_UNITS_2] = units_2, + }; + const int divisor[] = { + [STRING_UNITS_10] = 1000, + [STRING_UNITS_2] = 1024, + }; + int i, j; + u64 remainder = 0, sf_cap; + char tmp[8]; + + tmp[0] = '\0'; + + for (i = 0; size > divisor[units] && units_str[units][i]; i++) + remainder = do_div(size, divisor[units]); + + sf_cap = size; + for (j = 0; sf_cap*10 < 1000; j++) + sf_cap *= 10; + + if (j) { + remainder *= 1000; + do_div(remainder, divisor[units]); + snprintf(tmp, sizeof(tmp), ".%03lld", + (unsigned long long)remainder); + tmp[j+1] = '\0'; + } + + snprintf(buf, len, "%lld%s%s", (unsigned long long)size, + tmp, units_str[units][i]); + + return 0; +} +EXPORT_SYMBOL(string_get_size); -- cgit v1.2.3-70-g09d2 From a8659597bf744b0f8d2560e2a734b5c941569e0e Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 14 Oct 2008 11:34:21 -0700 Subject: [SCSI] lib: string_get_size(): don't hang on zero; no decimals on exact We would hang forever when passing a zero to string_get_size(). Furthermore, string_get_size() would produce decimals on a value small enough to be exact. Finally, a few formatting issues are inconsistent with standard SI style guidelines. - If the value is less than the divisor, skip the entire rounding step. This prints out all small values including zero as integers, without decimals. - Add a space between the value and the symbol for the unit, consistent with standard SI practice. - Lower case k in kB since we are talking about powers of 10. - Finally, change "int" to "unsigned int" in one place to shut up a gcc warning when compiling the code out-of-kernel for testing. Signed-off-by: H. Peter Anvin Signed-off-by: James Bottomley --- lib/string_helpers.c | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) (limited to 'lib/string_helpers.c') diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 8347925030f..ab431d4cc97 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -23,7 +23,7 @@ int string_get_size(u64 size, const enum string_size_units units, char *buf, int len) { - const char *units_10[] = { "B", "KB", "MB", "GB", "TB", "PB", + const char *units_10[] = { "B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL}; const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", NULL }; @@ -31,7 +31,7 @@ int string_get_size(u64 size, const enum string_size_units units, [STRING_UNITS_10] = units_10, [STRING_UNITS_2] = units_2, }; - const int divisor[] = { + const unsigned int divisor[] = { [STRING_UNITS_10] = 1000, [STRING_UNITS_2] = 1024, }; @@ -40,23 +40,27 @@ int string_get_size(u64 size, const enum string_size_units units, char tmp[8]; tmp[0] = '\0'; + i = 0; + if (size >= divisor[units]) { + while (size >= divisor[units] && units_str[units][i]) { + remainder = do_div(size, divisor[units]); + i++; + } - for (i = 0; size > divisor[units] && units_str[units][i]; i++) - remainder = do_div(size, divisor[units]); + sf_cap = size; + for (j = 0; sf_cap*10 < 1000; j++) + sf_cap *= 10; - sf_cap = size; - for (j = 0; sf_cap*10 < 1000; j++) - sf_cap *= 10; - - if (j) { - remainder *= 1000; - do_div(remainder, divisor[units]); - snprintf(tmp, sizeof(tmp), ".%03lld", - (unsigned long long)remainder); - tmp[j+1] = '\0'; + if (j) { + remainder *= 1000; + do_div(remainder, divisor[units]); + snprintf(tmp, sizeof(tmp), ".%03lld", + (unsigned long long)remainder); + tmp[j+1] = '\0'; + } } - snprintf(buf, len, "%lld%s%s", (unsigned long long)size, + snprintf(buf, len, "%lld%s %s", (unsigned long long)size, tmp, units_str[units][i]); return 0; -- cgit v1.2.3-70-g09d2