xref: /linux/lib/string_helpers.c (revision a8659597bf744b0f8d2560e2a734b5c941569e0e)
13c9f3681SJames Bottomley /*
23c9f3681SJames Bottomley  * Helpers for formatting and printing strings
33c9f3681SJames Bottomley  *
43c9f3681SJames Bottomley  * Copyright 31 August 2008 James Bottomley
53c9f3681SJames Bottomley  */
63c9f3681SJames Bottomley #include <linux/kernel.h>
73c9f3681SJames Bottomley #include <linux/math64.h>
83c9f3681SJames Bottomley #include <linux/module.h>
93c9f3681SJames Bottomley #include <linux/string_helpers.h>
103c9f3681SJames Bottomley 
113c9f3681SJames Bottomley /**
123c9f3681SJames Bottomley  * string_get_size - get the size in the specified units
133c9f3681SJames Bottomley  * @size:	The size to be converted
143c9f3681SJames Bottomley  * @units:	units to use (powers of 1000 or 1024)
153c9f3681SJames Bottomley  * @buf:	buffer to format to
163c9f3681SJames Bottomley  * @len:	length of buffer
173c9f3681SJames Bottomley  *
183c9f3681SJames Bottomley  * This function returns a string formatted to 3 significant figures
193c9f3681SJames Bottomley  * giving the size in the required units.  Returns 0 on success or
203c9f3681SJames Bottomley  * error on failure.  @buf is always zero terminated.
213c9f3681SJames Bottomley  *
223c9f3681SJames Bottomley  */
233c9f3681SJames Bottomley int string_get_size(u64 size, const enum string_size_units units,
243c9f3681SJames Bottomley 		    char *buf, int len)
253c9f3681SJames Bottomley {
26*a8659597SH. Peter Anvin 	const char *units_10[] = { "B", "kB", "MB", "GB", "TB", "PB",
273c9f3681SJames Bottomley 				   "EB", "ZB", "YB", NULL};
283c9f3681SJames Bottomley 	const char *units_2[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB",
293c9f3681SJames Bottomley 				 "EiB", "ZiB", "YiB", NULL };
303c9f3681SJames Bottomley 	const char **units_str[] = {
313c9f3681SJames Bottomley 		[STRING_UNITS_10] =  units_10,
323c9f3681SJames Bottomley 		[STRING_UNITS_2] = units_2,
333c9f3681SJames Bottomley 	};
34*a8659597SH. Peter Anvin 	const unsigned int divisor[] = {
353c9f3681SJames Bottomley 		[STRING_UNITS_10] = 1000,
363c9f3681SJames Bottomley 		[STRING_UNITS_2] = 1024,
373c9f3681SJames Bottomley 	};
383c9f3681SJames Bottomley 	int i, j;
393c9f3681SJames Bottomley 	u64 remainder = 0, sf_cap;
403c9f3681SJames Bottomley 	char tmp[8];
413c9f3681SJames Bottomley 
423c9f3681SJames Bottomley 	tmp[0] = '\0';
43*a8659597SH. Peter Anvin 	i = 0;
44*a8659597SH. Peter Anvin 	if (size >= divisor[units]) {
45*a8659597SH. Peter Anvin 		while (size >= divisor[units] && units_str[units][i]) {
463c9f3681SJames Bottomley 			remainder = do_div(size, divisor[units]);
47*a8659597SH. Peter Anvin 			i++;
48*a8659597SH. Peter Anvin 		}
493c9f3681SJames Bottomley 
503c9f3681SJames Bottomley 		sf_cap = size;
513c9f3681SJames Bottomley 		for (j = 0; sf_cap*10 < 1000; j++)
523c9f3681SJames Bottomley 			sf_cap *= 10;
533c9f3681SJames Bottomley 
543c9f3681SJames Bottomley 		if (j) {
553c9f3681SJames Bottomley 			remainder *= 1000;
563c9f3681SJames Bottomley 			do_div(remainder, divisor[units]);
573c9f3681SJames Bottomley 			snprintf(tmp, sizeof(tmp), ".%03lld",
583c9f3681SJames Bottomley 				 (unsigned long long)remainder);
593c9f3681SJames Bottomley 			tmp[j+1] = '\0';
603c9f3681SJames Bottomley 		}
61*a8659597SH. Peter Anvin 	}
623c9f3681SJames Bottomley 
633c9f3681SJames Bottomley 	snprintf(buf, len, "%lld%s %s", (unsigned long long)size,
643c9f3681SJames Bottomley 		 tmp, units_str[units][i]);
653c9f3681SJames Bottomley 
663c9f3681SJames Bottomley 	return 0;
673c9f3681SJames Bottomley }
683c9f3681SJames Bottomley EXPORT_SYMBOL(string_get_size);
69