Changeset - r27491:3efcd928d306
[Not reviewed]
master
0 1 0
Rubidium - 13 months ago 2023-05-11 19:34:21
rubidium@openttd.org
Codechange: let number formatting use StringBuilder
1 file changed with 73 insertions and 62 deletions:
0 comments (0 inline, 0 general)
src/strings.cpp
Show inline comments
 
@@ -43,6 +43,8 @@
 
#include "table/strings.h"
 
#include "table/control_codes.h"
 

	
 
#include "strings_internal.h"
 

	
 
#include "safeguards.h"
 

	
 
std::string _config_language_file;                ///< The file (name) stored in the configuration.
 
@@ -315,16 +317,15 @@ void SetDParamStr(uint n, const std::str
 

	
 
/**
 
 * Format a number into a string.
 
 * @param buff      the buffer to write to
 
 * @param builder   the string builder to write to
 
 * @param number    the number to write down
 
 * @param last      the last element in the buffer
 
 * @param separator the thousands-separator to use
 
 * @param zerofill  minimum number of digits to print for the integer part. The number will be filled with zeros at the front if necessary.
 
 * @param fractional_digits number of fractional digits to display after a decimal separator. The decimal separator is inserted
 
 *                          in front of the \a fractional_digits last digit of \a number.
 
 * @return till where we wrote
 
 */
 
static char *FormatNumber(char *buff, int64 number, const char *last, const char *separator, int zerofill = 1, int fractional_digits = 0)
 
static void FormatNumber(StringBuilder &builder, int64 number, const char *separator, int zerofill = 1, int fractional_digits = 0)
 
{
 
	static const int max_digits = 20;
 
	uint64 divisor = 10000000000000000000ULL;
 
@@ -332,7 +333,7 @@ static char *FormatNumber(char *buff, in
 
	int thousands_offset = (max_digits - fractional_digits - 1) % 3;
 

	
 
	if (number < 0) {
 
		if (buff != last) *buff++ = '-';
 
		builder += '-';
 
		number = -number;
 
	}
 

	
 
@@ -342,7 +343,7 @@ static char *FormatNumber(char *buff, in
 
		if (i == max_digits - fractional_digits) {
 
			const char *decimal_separator = _settings_game.locale.digit_decimal_separator.c_str();
 
			if (StrEmpty(decimal_separator)) decimal_separator = _langpack.langpack->digit_decimal_separator;
 
			buff = strecpy(buff, decimal_separator, last);
 
			builder += decimal_separator;
 
		}
 

	
 
		uint64 quot = 0;
 
@@ -351,48 +352,42 @@ static char *FormatNumber(char *buff, in
 
			num = num % divisor;
 
		}
 
		if ((tot |= quot) || i >= max_digits - zerofill) {
 
			if (buff != last) *buff++ = '0' + quot; // quot is a single digit
 
			if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) buff = strecpy(buff, separator, last);
 
			builder += '0' + quot; // quot is a single digit
 
			if ((i % 3) == thousands_offset && i < max_digits - 1 - fractional_digits) builder += separator;
 
		}
 

	
 
		divisor /= 10;
 
	}
 

	
 
	*buff = '\0';
 

	
 
	return buff;
 
}
 

	
 
static char *FormatCommaNumber(char *buff, int64 number, const char *last, int fractional_digits = 0)
 
static void FormatCommaNumber(StringBuilder &builder, int64 number, int fractional_digits = 0)
 
{
 
	const char *separator = _settings_game.locale.digit_group_separator.c_str();
 
	if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator;
 
	return FormatNumber(buff, number, last, separator, 1, fractional_digits);
 
	FormatNumber(builder, number, separator, 1, fractional_digits);
 
}
 

	
 
static char *FormatNoCommaNumber(char *buff, int64 number, const char *last)
 
static void FormatNoCommaNumber(StringBuilder &builder, int64 number)
 
{
 
	return FormatNumber(buff, number, last, "");
 
	FormatNumber(builder, number, "");
 
}
 

	
 
static char *FormatZerofillNumber(char *buff, int64 number, int64 count, const char *last)
 
static void FormatZerofillNumber(StringBuilder &builder, int64 number, int64 count)
 
{
 
	return FormatNumber(buff, number, last, "", count);
 
	FormatNumber(builder, number, "", count);
 
}
 

	
 
static char *FormatHexNumber(char *buff, uint64 number, const char *last)
 
static void FormatHexNumber(StringBuilder &builder, uint64 number)
 
{
 
	return strecpy(buff, fmt::format("0x{:X}", number).c_str(), last);
 
	fmt::format_to(builder, "0x{:X}", number);
 
}
 

	
 
/**
 
 * Format a given number as a number of bytes with the SI prefix.
 
 * @param buff   the buffer to write to
 
 * @param number the number of bytes to write down
 
 * @param last   the last element in the buffer
 
 * @return till where we wrote
 
 * @param builder the string builder to write to
 
 * @param number  the number of bytes to write down
 
 */
 
static char *FormatBytes(char *buff, int64 number, const char *last)
 
static void FormatBytes(StringBuilder &builder, int64 number)
 
{
 
	assert(number >= 0);
 

	
 
@@ -409,20 +404,18 @@ static char *FormatBytes(char *buff, int
 

	
 
	if (number < 1024) {
 
		id = 0;
 
		buff += seprintf(buff, last, "%i", (int)number);
 
		fmt::format_to(builder, "{}", number);
 
	} else if (number < 1024 * 10) {
 
		buff += seprintf(buff, last, "%i%s%02i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 100 / 1024);
 
		fmt::format_to(builder, "{}{}{:02}", number / 1024, decimal_separator, (number % 1024) * 100 / 1024);
 
	} else if (number < 1024 * 100) {
 
		buff += seprintf(buff, last, "%i%s%01i", (int)number / 1024, decimal_separator, (int)(number % 1024) * 10 / 1024);
 
		fmt::format_to(builder, "{}{}{:01}", number / 1024, decimal_separator, (number % 1024) * 10 / 1024);
 
	} else {
 
		assert(number < 1024 * 1024);
 
		buff += seprintf(buff, last, "%i", (int)number / 1024);
 
		fmt::format_to(builder, "{}", number / 1024);
 
	}
 

	
 
	assert(id < lengthof(iec_prefixes));
 
	buff += seprintf(buff, last, NBSP "%sB", iec_prefixes[id]);
 

	
 
	return buff;
 
	fmt::format_to(builder, NBSP "{}B", iec_prefixes[id]);
 
}
 

	
 
static char *FormatYmdString(char *buff, TimerGameCalendar::Date date, const char *last, uint case_index)
 
@@ -456,7 +449,7 @@ static char *FormatTinyOrISODate(char *b
 
	return FormatString(buff, GetStringPtr(str), &tmp_params, last);
 
}
 

	
 
static char *FormatGenericCurrency(char *buff, const CurrencySpec *spec, Money number, bool compact, const char *last)
 
static void FormatGenericCurrency(StringBuilder &builder, const CurrencySpec *spec, Money number, bool compact)
 
{
 
	/* We are going to make number absolute for printing, so
 
	 * keep this piece of data as we need it later on */
 
@@ -467,18 +460,16 @@ static char *FormatGenericCurrency(char 
 

	
 
	/* convert from negative */
 
	if (number < 0) {
 
		if (buff + Utf8CharLen(SCC_PUSH_COLOUR) > last) return buff;
 
		buff += Utf8Encode(buff, SCC_PUSH_COLOUR);
 
		if (buff + Utf8CharLen(SCC_RED) > last) return buff;
 
		buff += Utf8Encode(buff, SCC_RED);
 
		buff = strecpy(buff, "-", last);
 
		if (!builder.Utf8Encode(SCC_PUSH_COLOUR)) return;
 
		if (!builder.Utf8Encode(SCC_RED)) return;
 
		builder += '-';
 
		number = -number;
 
	}
 

	
 
	/* Add prefix part, following symbol_pos specification.
 
	 * Here, it can can be either 0 (prefix) or 2 (both prefix and suffix).
 
	 * The only remaining value is 1 (suffix), so everything that is not 1 */
 
	if (spec->symbol_pos != 1) buff = strecpy(buff, spec->prefix.c_str(), last);
 
	if (spec->symbol_pos != 1) builder += spec->prefix;
 

	
 
	/* for huge numbers, compact the number into k or M */
 
	if (compact) {
 
@@ -496,21 +487,17 @@ static char *FormatGenericCurrency(char 
 
	const char *separator = _settings_game.locale.digit_group_separator_currency.c_str();
 
	if (StrEmpty(separator)) separator = _currency->separator.c_str();
 
	if (StrEmpty(separator)) separator = _langpack.langpack->digit_group_separator_currency;
 
	buff = FormatNumber(buff, number, last, separator);
 
	buff = strecpy(buff, multiplier, last);
 
	FormatNumber(builder, number, separator);
 
	builder += multiplier;
 

	
 
	/* Add suffix part, following symbol_pos specification.
 
	 * Here, it can can be either 1 (suffix) or 2 (both prefix and suffix).
 
	 * The only remaining value is 1 (prefix), so everything that is not 0 */
 
	if (spec->symbol_pos != 0) buff = strecpy(buff, spec->suffix.c_str(), last);
 
	if (spec->symbol_pos != 0) builder += spec->suffix;
 

	
 
	if (negative) {
 
		if (buff + Utf8CharLen(SCC_POP_COLOUR) > last) return buff;
 
		buff += Utf8Encode(buff, SCC_POP_COLOUR);
 
		*buff = '\0';
 
		builder.Utf8Encode(SCC_POP_COLOUR);
 
	}
 

	
 
	return buff;
 
}
 

	
 
/**
 
@@ -1109,34 +1096,50 @@ static char *FormatString(char *buff, co
 
				break;
 
			}
 

	
 
			case SCC_COMMA: // {COMMA}
 
				buff = FormatCommaNumber(buff, args->GetInt64(SCC_COMMA), last);
 
			case SCC_COMMA: { // {COMMA}
 
				StringBuilder builder(buff, last);
 
				FormatCommaNumber(builder, args->GetInt64(SCC_COMMA));
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_DECIMAL: {// {DECIMAL}
 
			case SCC_DECIMAL: { // {DECIMAL}
 
				int64 number = args->GetInt64(SCC_DECIMAL);
 
				int digits = args->GetInt32(SCC_DECIMAL);
 
				buff = FormatCommaNumber(buff, number, last, digits);
 
				StringBuilder builder(buff, last);
 
				FormatCommaNumber(builder, number, digits);
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_NUM: // {NUM}
 
				buff = FormatNoCommaNumber(buff, args->GetInt64(SCC_NUM), last);
 
			case SCC_NUM: { // {NUM}
 
				StringBuilder builder(buff, last);
 
				FormatNoCommaNumber(builder, args->GetInt64(SCC_NUM));
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_ZEROFILL_NUM: { // {ZEROFILL_NUM}
 
				int64 num = args->GetInt64();
 
				buff = FormatZerofillNumber(buff, num, args->GetInt64(), last);
 
				StringBuilder builder(buff, last);
 
				FormatZerofillNumber(builder, num, args->GetInt64());
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_HEX: // {HEX}
 
				buff = FormatHexNumber(buff, (uint64)args->GetInt64(SCC_HEX), last);
 
			case SCC_HEX: { // {HEX}
 
				StringBuilder builder(buff, last);
 
				FormatHexNumber(builder, (uint64)args->GetInt64(SCC_HEX));
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_BYTES: // {BYTES}
 
				buff = FormatBytes(buff, args->GetInt64(), last);
 
			case SCC_BYTES: { // {BYTES}
 
				StringBuilder builder(buff, last);
 
				FormatBytes(builder, args->GetInt64());
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_CARGO_TINY: { // {CARGO_TINY}
 
				/* Tiny description of cargotypes. Layout:
 
@@ -1162,7 +1165,9 @@ static char *FormatString(char *buff, co
 
					}
 
				}
 

	
 
				buff = FormatCommaNumber(buff, amount, last);
 
				StringBuilder builder(buff, last);
 
				FormatCommaNumber(builder, amount);
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
@@ -1242,13 +1247,19 @@ static char *FormatString(char *buff, co
 
				break;
 
			}
 

	
 
			case SCC_CURRENCY_SHORT: // {CURRENCY_SHORT}
 
				buff = FormatGenericCurrency(buff, _currency, args->GetInt64(), true, last);
 
			case SCC_CURRENCY_SHORT: { // {CURRENCY_SHORT}
 
				StringBuilder builder(buff, last);
 
				FormatGenericCurrency(builder, _currency, args->GetInt64(), true);
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_CURRENCY_LONG: // {CURRENCY_LONG}
 
				buff = FormatGenericCurrency(buff, _currency, args->GetInt64(SCC_CURRENCY_LONG), false, last);
 
			case SCC_CURRENCY_LONG: { // {CURRENCY_LONG}
 
				StringBuilder builder(buff, last);
 
				FormatGenericCurrency(builder, _currency, args->GetInt64(SCC_CURRENCY_LONG), false);
 
				buff = builder.GetEnd();
 
				break;
 
			}
 

	
 
			case SCC_DATE_TINY: // {DATE_TINY}
 
				buff = FormatTinyOrISODate(buff, args->GetInt32(SCC_DATE_TINY), STR_FORMAT_DATE_TINY, last);
0 comments (0 inline, 0 general)