add StringBuilder extension methods to allow garbage-free number appends
This commit is contained in:
parent
8831788f14
commit
a7dba64e18
|
@ -134,6 +134,7 @@
|
|||
<Compile Include="Graphics\BillboardSpriteBatch.cs" />
|
||||
<Compile Include="Graphics\Helpers\FlatWireframeGrid.cs" />
|
||||
<Compile Include="Support\FreeMovementCamera.cs" />
|
||||
<Compile Include="Support\StringBuilderExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
|
198
Blarg.GameFramework/Support/StringBuilderExtensions.cs
Normal file
198
Blarg.GameFramework/Support/StringBuilderExtensions.cs
Normal file
|
@ -0,0 +1,198 @@
|
|||
#region File Description
|
||||
//-----------------------------------------------------------------------------
|
||||
// StringBuilderExtensions.cs
|
||||
//
|
||||
// Microsoft XNA Community Game Platform
|
||||
// Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
//-----------------------------------------------------------------------------
|
||||
#endregion
|
||||
|
||||
#region Using Statements
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Blarg.GameFramework.Support
|
||||
{
|
||||
/// <summary>
|
||||
/// Options for StringBuilder extension methods.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum AppendNumberOptions
|
||||
{
|
||||
// Normal format.
|
||||
None = 0,
|
||||
|
||||
// Added "+" sign for positive value.
|
||||
PositiveSign = 1,
|
||||
|
||||
// Insert Number group separation characters.
|
||||
// In Use, added "," for every 3 digits.
|
||||
NumberGroup = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Static class for string builder extension methods.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You can specified StringBuilder for SpriteFont.DrawString from XNA GS 3.0.
|
||||
/// And you can save unwanted memory allocations.
|
||||
///
|
||||
/// But there are still problems for adding numerical value to StringBuilder.
|
||||
/// One of them is boxing occurred when you use StringBuilder.AppendFormat method.
|
||||
/// Another issue is memory allocation occurred when you specify int or float for
|
||||
/// StringBuild.Append method.
|
||||
///
|
||||
/// This class provides solution for those issue.
|
||||
///
|
||||
/// All methods are defined as extension methods as StringBuilder. So, you can use
|
||||
/// those method like below.
|
||||
///
|
||||
/// stringBuilder.AppendNumber(12345);
|
||||
///
|
||||
/// </remarks>
|
||||
public static class StringBuilderExtensions
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Cache for NumberGroupSizes of NumberFormat class.
|
||||
/// </summary>
|
||||
static int[] numberGroupSizes = CultureInfo.CurrentCulture.NumberFormat.NumberGroupSizes;
|
||||
|
||||
/// <summary>
|
||||
/// string conversion buffer.
|
||||
/// </summary>
|
||||
static char[] numberString = new char[32];
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Convert integer to string and add to string builder.
|
||||
/// </summary>
|
||||
public static StringBuilder AppendNumber(this StringBuilder builder, int number)
|
||||
{
|
||||
return AppendNumberInternal(builder, number, 0, AppendNumberOptions.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert integer to string and add to string builder.
|
||||
/// </summary>
|
||||
/// <param name="number"></param>
|
||||
/// <param name="options">Format options</param>
|
||||
public static StringBuilder AppendNumber(this StringBuilder builder, int number, AppendNumberOptions options)
|
||||
{
|
||||
return AppendNumberInternal(builder, number, 0, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert float to string and add to string builder.
|
||||
/// </summary>
|
||||
/// <remarks>It shows 2 decimal digits.</remarks>
|
||||
public static StringBuilder AppendNumber(this StringBuilder builder, float number)
|
||||
{
|
||||
return AppendNumber(builder, number, 2, AppendNumberOptions.None);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert float to string and add to string builder.
|
||||
/// </summary>
|
||||
/// <remarks>It shows 2 decimal digits.</remarks>
|
||||
public static StringBuilder AppendNumber(this StringBuilder builder, float number, AppendNumberOptions options)
|
||||
{
|
||||
return AppendNumber(builder, number, 2, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert float to string and add to string builder.
|
||||
/// </summary>
|
||||
public static StringBuilder AppendNumber(this StringBuilder builder, float number, int decimalCount, AppendNumberOptions options = AppendNumberOptions.None)
|
||||
{
|
||||
// Handle NaN, Infinity cases.
|
||||
if (float.IsNaN(number))
|
||||
{
|
||||
return builder.Append("NaN");
|
||||
}
|
||||
else if (float.IsNegativeInfinity(number))
|
||||
{
|
||||
return builder.Append("-Infinity");
|
||||
}
|
||||
else if (float.IsPositiveInfinity(number))
|
||||
{
|
||||
return builder.Append("+Infinity");
|
||||
}
|
||||
else
|
||||
{
|
||||
int intNumber =
|
||||
(int)(number * (float)Math.Pow(10, decimalCount) + 0.5f);
|
||||
|
||||
return AppendNumberInternal(builder, intNumber, decimalCount, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static StringBuilder AppendNumberInternal(StringBuilder builder, int number, int decimalCount, AppendNumberOptions options)
|
||||
{
|
||||
// Initialize variables for conversion.
|
||||
NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
|
||||
|
||||
int idx = numberString.Length;
|
||||
int decimalPos = idx - decimalCount;
|
||||
|
||||
if (decimalPos == idx)
|
||||
decimalPos = idx + 1;
|
||||
|
||||
int numberGroupIdx = 0;
|
||||
int numberGroupCount = numberGroupSizes[numberGroupIdx] + decimalCount;
|
||||
|
||||
bool showNumberGroup = (options & AppendNumberOptions.NumberGroup) != 0;
|
||||
bool showPositiveSign = (options & AppendNumberOptions.PositiveSign) != 0;
|
||||
|
||||
bool isNegative = number < 0;
|
||||
number = Math.Abs(number);
|
||||
|
||||
// Converting from smallest digit.
|
||||
do
|
||||
{
|
||||
// Add decimal separator ("." in US).
|
||||
if (idx == decimalPos)
|
||||
{
|
||||
numberString[--idx] = nfi.NumberDecimalSeparator[0];
|
||||
}
|
||||
|
||||
// Added number group separator ("," in US).
|
||||
if (--numberGroupCount < 0 && showNumberGroup)
|
||||
{
|
||||
numberString[--idx] = nfi.NumberGroupSeparator[0];
|
||||
|
||||
if (numberGroupIdx < numberGroupSizes.Length - 1)
|
||||
numberGroupIdx++;
|
||||
|
||||
numberGroupCount = numberGroupSizes[numberGroupIdx] - 1;
|
||||
}
|
||||
|
||||
// Convert current digit to character and add to buffer.
|
||||
numberString[--idx] = (char)('0' + (number % 10));
|
||||
number /= 10;
|
||||
|
||||
} while (number > 0 || decimalPos <= idx);
|
||||
|
||||
|
||||
// Added sign character if needed.
|
||||
if (isNegative)
|
||||
{
|
||||
numberString[--idx] = nfi.NegativeSign[0];
|
||||
}
|
||||
else if (showPositiveSign)
|
||||
{
|
||||
numberString[--idx] = nfi.PositiveSign[0];
|
||||
}
|
||||
|
||||
// Added converted string to StringBuilder.
|
||||
return builder.Append(numberString, idx, numberString.Length - idx);
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue