diff options
Diffstat (limited to 'lib/string_helpers.c')
| -rw-r--r-- | lib/string_helpers.c | 271 | 
1 files changed, 131 insertions, 140 deletions
| diff --git a/lib/string_helpers.c b/lib/string_helpers.c index 58b78ba57439..c98ae818eb4e 100644 --- a/lib/string_helpers.c +++ b/lib/string_helpers.c @@ -4,6 +4,7 @@   * Copyright 31 August 2008 James Bottomley   * Copyright (C) 2013, Intel Corporation   */ +#include <linux/bug.h>  #include <linux/kernel.h>  #include <linux/math64.h>  #include <linux/export.h> @@ -14,25 +15,25 @@  /**   * string_get_size - get the size in the specified units - * @size:	The size to be converted + * @size:	The size to be converted in blocks + * @blk_size:	Size of the block (use 1 for size in bytes)   * @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. + * giving the size in the required units.  @buf should have room for + * at least 9 bytes and will always be zero terminated.   *   */ -int string_get_size(u64 size, const enum string_size_units units, -		    char *buf, int len) +void string_get_size(u64 size, u64 blk_size, const enum string_size_units units, +		     char *buf, int len)  {  	static const char *const units_10[] = { -		"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", NULL +		"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"  	};  	static const char *const units_2[] = { -		"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", -		NULL +		"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"  	};  	static const char *const *const units_str[] = {  		[STRING_UNITS_10] = units_10, @@ -43,34 +44,57 @@ int string_get_size(u64 size, const enum string_size_units units,  		[STRING_UNITS_2] = 1024,  	};  	int i, j; -	u64 remainder = 0, sf_cap; +	u32 remainder = 0, sf_cap, exp;  	char tmp[8]; +	const char *unit;  	tmp[0] = '\0';  	i = 0; -	if (size >= divisor[units]) { -		while (size >= divisor[units] && units_str[units][i]) { -			remainder = do_div(size, divisor[units]); -			i++; -		} +	if (!size) +		goto out; -		sf_cap = size; -		for (j = 0; sf_cap*10 < 1000; j++) -			sf_cap *= 10; +	while (blk_size >= divisor[units]) { +		remainder = do_div(blk_size, divisor[units]); +		i++; +	} -		if (j) { -			remainder *= 1000; -			do_div(remainder, divisor[units]); -			snprintf(tmp, sizeof(tmp), ".%03lld", -				 (unsigned long long)remainder); -			tmp[j+1] = '\0'; -		} +	exp = divisor[units] / (u32)blk_size; +	if (size >= exp) { +		remainder = do_div(size, divisor[units]); +		remainder *= blk_size; +		i++; +	} else { +		remainder *= size; +	} + +	size *= blk_size; +	size += remainder / divisor[units]; +	remainder %= divisor[units]; + +	while (size >= divisor[units]) { +		remainder = do_div(size, divisor[units]); +		i++;  	} -	snprintf(buf, len, "%lld%s %s", (unsigned long long)size, -		 tmp, units_str[units][i]); +	sf_cap = size; +	for (j = 0; sf_cap*10 < 1000; j++) +		sf_cap *= 10; -	return 0; +	if (j) { +		remainder *= 1000; +		remainder /= divisor[units]; +		snprintf(tmp, sizeof(tmp), ".%03u", remainder); +		tmp[j+1] = '\0'; +	} + + out: +	if (i >= ARRAY_SIZE(units_2)) +		unit = "UNK"; +	else +		unit = units_str[units][i]; + +	snprintf(buf, len, "%u%s %s", (u32)size, +		 tmp, unit);  }  EXPORT_SYMBOL(string_get_size); @@ -243,29 +267,21 @@ int string_unescape(char *src, char *dst, size_t size, unsigned int flags)  }  EXPORT_SYMBOL(string_unescape); -static int escape_passthrough(unsigned char c, char **dst, size_t *osz) +static bool escape_passthrough(unsigned char c, char **dst, char *end)  {  	char *out = *dst; -	if (*osz < 1) -		return -ENOMEM; - -	*out++ = c; - -	*dst = out; -	*osz -= 1; - -	return 1; +	if (out < end) +		*out = c; +	*dst = out + 1; +	return true;  } -static int escape_space(unsigned char c, char **dst, size_t *osz) +static bool escape_space(unsigned char c, char **dst, char *end)  {  	char *out = *dst;  	unsigned char to; -	if (*osz < 2) -		return -ENOMEM; -  	switch (c) {  	case '\n':  		to = 'n'; @@ -283,26 +299,25 @@ static int escape_space(unsigned char c, char **dst, size_t *osz)  		to = 'f';  		break;  	default: -		return 0; +		return false;  	} -	*out++ = '\\'; -	*out++ = to; +	if (out < end) +		*out = '\\'; +	++out; +	if (out < end) +		*out = to; +	++out;  	*dst = out; -	*osz -= 2; - -	return 1; +	return true;  } -static int escape_special(unsigned char c, char **dst, size_t *osz) +static bool escape_special(unsigned char c, char **dst, char *end)  {  	char *out = *dst;  	unsigned char to; -	if (*osz < 2) -		return -ENOMEM; -  	switch (c) {  	case '\\':  		to = '\\'; @@ -314,71 +329,78 @@ static int escape_special(unsigned char c, char **dst, size_t *osz)  		to = 'e';  		break;  	default: -		return 0; +		return false;  	} -	*out++ = '\\'; -	*out++ = to; +	if (out < end) +		*out = '\\'; +	++out; +	if (out < end) +		*out = to; +	++out;  	*dst = out; -	*osz -= 2; - -	return 1; +	return true;  } -static int escape_null(unsigned char c, char **dst, size_t *osz) +static bool escape_null(unsigned char c, char **dst, char *end)  {  	char *out = *dst; -	if (*osz < 2) -		return -ENOMEM; -  	if (c) -		return 0; +		return false; -	*out++ = '\\'; -	*out++ = '0'; +	if (out < end) +		*out = '\\'; +	++out; +	if (out < end) +		*out = '0'; +	++out;  	*dst = out; -	*osz -= 2; - -	return 1; +	return true;  } -static int escape_octal(unsigned char c, char **dst, size_t *osz) +static bool escape_octal(unsigned char c, char **dst, char *end)  {  	char *out = *dst; -	if (*osz < 4) -		return -ENOMEM; - -	*out++ = '\\'; -	*out++ = ((c >> 6) & 0x07) + '0'; -	*out++ = ((c >> 3) & 0x07) + '0'; -	*out++ = ((c >> 0) & 0x07) + '0'; +	if (out < end) +		*out = '\\'; +	++out; +	if (out < end) +		*out = ((c >> 6) & 0x07) + '0'; +	++out; +	if (out < end) +		*out = ((c >> 3) & 0x07) + '0'; +	++out; +	if (out < end) +		*out = ((c >> 0) & 0x07) + '0'; +	++out;  	*dst = out; -	*osz -= 4; - -	return 1; +	return true;  } -static int escape_hex(unsigned char c, char **dst, size_t *osz) +static bool escape_hex(unsigned char c, char **dst, char *end)  {  	char *out = *dst; -	if (*osz < 4) -		return -ENOMEM; - -	*out++ = '\\'; -	*out++ = 'x'; -	*out++ = hex_asc_hi(c); -	*out++ = hex_asc_lo(c); +	if (out < end) +		*out = '\\'; +	++out; +	if (out < end) +		*out = 'x'; +	++out; +	if (out < end) +		*out = hex_asc_hi(c); +	++out; +	if (out < end) +		*out = hex_asc_lo(c); +	++out;  	*dst = out; -	*osz -= 4; - -	return 1; +	return true;  }  /** @@ -430,19 +452,17 @@ static int escape_hex(unsigned char c, char **dst, size_t *osz)   * it if needs.   *   * Return: - * The amount of the characters processed to the destination buffer, or - * %-ENOMEM if the size of buffer is not enough to put an escaped character is - * returned. - * - * Even in the case of error @dst pointer will be updated to point to the byte - * after the last processed character. + * The total size of the escaped output that would be generated for + * the given input and flags. To check whether the output was + * truncated, compare the return value to osz. There is room left in + * dst for a '\0' terminator if and only if ret < osz.   */ -int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz, +int string_escape_mem(const char *src, size_t isz, char *dst, size_t osz,  		      unsigned int flags, const char *esc)  { -	char *out = *dst, *p = out; +	char *p = dst; +	char *end = p + osz;  	bool is_dict = esc && *esc; -	int ret = 0;  	while (isz--) {  		unsigned char c = *src++; @@ -462,55 +482,26 @@ int string_escape_mem(const char *src, size_t isz, char **dst, size_t osz,  		    (is_dict && !strchr(esc, c))) {  			/* do nothing */  		} else { -			if (flags & ESCAPE_SPACE) { -				ret = escape_space(c, &p, &osz); -				if (ret < 0) -					break; -				if (ret > 0) -					continue; -			} - -			if (flags & ESCAPE_SPECIAL) { -				ret = escape_special(c, &p, &osz); -				if (ret < 0) -					break; -				if (ret > 0) -					continue; -			} - -			if (flags & ESCAPE_NULL) { -				ret = escape_null(c, &p, &osz); -				if (ret < 0) -					break; -				if (ret > 0) -					continue; -			} +			if (flags & ESCAPE_SPACE && escape_space(c, &p, end)) +				continue; + +			if (flags & ESCAPE_SPECIAL && escape_special(c, &p, end)) +				continue; + +			if (flags & ESCAPE_NULL && escape_null(c, &p, end)) +				continue;  			/* ESCAPE_OCTAL and ESCAPE_HEX always go last */ -			if (flags & ESCAPE_OCTAL) { -				ret = escape_octal(c, &p, &osz); -				if (ret < 0) -					break; +			if (flags & ESCAPE_OCTAL && escape_octal(c, &p, end))  				continue; -			} -			if (flags & ESCAPE_HEX) { -				ret = escape_hex(c, &p, &osz); -				if (ret < 0) -					break; + +			if (flags & ESCAPE_HEX && escape_hex(c, &p, end))  				continue; -			}  		} -		ret = escape_passthrough(c, &p, &osz); -		if (ret < 0) -			break; +		escape_passthrough(c, &p, end);  	} -	*dst = p; - -	if (ret < 0) -		return ret; - -	return p - out; +	return p - dst;  }  EXPORT_SYMBOL(string_escape_mem); | 
