summaryrefslogtreecommitdiffstats
path: root/lib/string_helpers.c
blob: 8347925030ff51a0a98d1a81cb5ae9c0b06413fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/*
 * Helpers for formatting and printing strings
 *
 * Copyright 31 August 2008 James Bottomley
 */
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/string_helpers.h>

/**
 * 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);