///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // File: StringBuilderExtNumeric.cs // Date: 9th March 2010 // Author: Gavin Pugh // Details: Extension methods for the 'StringBuilder' standard .NET class, to allow garbage-free concatenation of // a selection of simple numeric types. // // Copyright (c) Gavin Pugh 2010 - Released under the zlib license: http://www.opensource.org/licenses/zlib-license.php ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace StringLeakTest { public static partial class StringBuilderExtensions { // These digits are here in a static array to support hex with simple, easily-understandable code. // Since A-Z don't sit next to 0-9 in the ascii table. private static readonly char[] ms_digits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; private static readonly uint ms_default_decimal_places = 5; //< Matches standard .NET formatting dp's private static readonly char ms_default_pad_char = '0'; //! Convert a given unsigned integer value to a string and concatenate onto the stringbuilder. Any base value allowed. public static StringBuilder Concat( this StringBuilder string_builder, uint uint_val, uint pad_amount, char pad_char, uint base_val ) { Debug.Assert( pad_amount >= 0 ); Debug.Assert( base_val > 0 && base_val <= 16 ); // Calculate length of integer when written out uint length = 0; uint length_calc = uint_val; do { length_calc /= base_val; length++; } while ( length_calc > 0 ); // Pad out space for writing. string_builder.Append( pad_char, (int)Math.Max( pad_amount, length )); int strpos = string_builder.Length; // We're writing backwards, one character at a time. while ( length > 0 ) { strpos--; // Lookup from static char array, to cover hex values too string_builder[strpos] = ms_digits[uint_val % base_val]; uint_val /= base_val; length--; } return string_builder; } //! Convert a given unsigned integer value to a string and concatenate onto the stringbuilder. Assume no padding and base ten. public static StringBuilder Concat( this StringBuilder string_builder, uint uint_val ) { string_builder.Concat( uint_val, 0, ms_default_pad_char, 10 ); return string_builder; } //! Convert a given unsigned integer value to a string and concatenate onto the stringbuilder. Assume base ten. public static StringBuilder Concat( this StringBuilder string_builder, uint uint_val, uint pad_amount ) { string_builder.Concat( uint_val, pad_amount, ms_default_pad_char, 10 ); return string_builder; } //! Convert a given unsigned integer value to a string and concatenate onto the stringbuilder. Assume base ten. public static StringBuilder Concat( this StringBuilder string_builder, uint uint_val, uint pad_amount, char pad_char ) { string_builder.Concat( uint_val, pad_amount, pad_char, 10 ); return string_builder; } //! Convert a given signed integer value to a string and concatenate onto the stringbuilder. Any base value allowed. public static StringBuilder Concat( this StringBuilder string_builder, int int_val, uint pad_amount, char pad_char, uint base_val ) { Debug.Assert( pad_amount >= 0 ); Debug.Assert( base_val > 0 && base_val <= 16 ); // Deal with negative numbers if (int_val < 0) { string_builder.Append( '-' ); uint uint_val = uint.MaxValue - ((uint) int_val ) + 1; //< This is to deal with Int32.MinValue string_builder.Concat( uint_val, pad_amount, pad_char, base_val ); } else { string_builder.Concat((uint)int_val, pad_amount, pad_char, base_val ); } return string_builder; } //! Convert a given signed integer value to a string and concatenate onto the stringbuilder. Assume no padding and base ten. public static StringBuilder Concat( this StringBuilder string_builder, int int_val ) { string_builder.Concat( int_val, 0, ms_default_pad_char, 10 ); return string_builder; } //! Convert a given signed integer value to a string and concatenate onto the stringbuilder. Assume base ten. public static StringBuilder Concat( this StringBuilder string_builder, int int_val, uint pad_amount ) { string_builder.Concat( int_val, pad_amount, ms_default_pad_char, 10 ); return string_builder; } //! Convert a given signed integer value to a string and concatenate onto the stringbuilder. Assume base ten. public static StringBuilder Concat( this StringBuilder string_builder, int int_val, uint pad_amount, char pad_char ) { string_builder.Concat( int_val, pad_amount, pad_char, 10 ); return string_builder; } //! Convert a given float value to a string and concatenate onto the stringbuilder public static StringBuilder Concat( this StringBuilder string_builder, float float_val, uint decimal_places, uint pad_amount, char pad_char ) { Debug.Assert( pad_amount >= 0 ); if ( decimal_places == 0 ) { // No decimal places, just round up and print it as an int // Agh, Math.Floor() just works on doubles/decimals. Don't want to cast! Let's do this the old-fashioned way. int int_val; if ( float_val >= 0.0f ) { // Round up int_val = (int)( float_val + 0.5f ); } else { // Round down for negative numbers int_val = (int)( float_val - 0.5f ); } string_builder.Concat( int_val, pad_amount, pad_char, 10 ); } else { int int_part = (int)float_val; // First part is easy, just cast to an integer string_builder.Concat( int_part, pad_amount, pad_char, 10 ); // Decimal point string_builder.Append( '.' ); // Work out remainder we need to print after the d.p. float remainder = Math.Abs( float_val - int_part ); // Multiply up to become an int that we can print do { remainder *= 10; decimal_places--; } while ( decimal_places > 0 ); // Round up. It's guaranteed to be a positive number, so no extra work required here. remainder += 0.5f; // All done, print that as an int! string_builder.Concat( (uint)remainder, 0, '0', 10 ); } return string_builder; } //! Convert a given float value to a string and concatenate onto the stringbuilder. Assumes five decimal places, and no padding. public static StringBuilder Concat(this StringBuilder string_builder, float float_val) { string_builder.Concat(float_val, ms_default_decimal_places, 0, ms_default_pad_char); return string_builder; } //! Convert a given float value to a string and concatenate onto the stringbuilder. Assumes no padding. public static StringBuilder Concat( this StringBuilder string_builder, float float_val, uint decimal_places ) { string_builder.Concat (float_val, decimal_places, 0, ms_default_pad_char ); return string_builder; } //! Convert a given float value to a string and concatenate onto the stringbuilder. public static StringBuilder Concat(this StringBuilder string_builder, float float_val, uint decimal_places, uint pad_amount) { string_builder.Concat(float_val, decimal_places, pad_amount, ms_default_pad_char); return string_builder; } } }