diff --git a/Documents/DoubleConverter.txt b/Documents/DoubleConverter.txt
new file mode 100644
index 0000000..370bed8
--- /dev/null
+++ b/Documents/DoubleConverter.txt
@@ -0,0 +1,218 @@
+From http://www.yoda.arachsys.com/csharp/DoubleConverter.cs
+
+
+using System;
+using System.Globalization;
+
+///
+/// A class to allow the conversion of doubles to string representations of
+/// their exact decimal values. The implementation aims for readability over
+/// efficiency.
+///
+public class DoubleConverter
+{
+ ///
+ /// Converts the given double to a string representation of its
+ /// exact decimal value.
+ ///
+ /// The double to convert.
+ /// A string representation of the double's exact decimal value.
+ public static string ToExactString (double d)
+ {
+ if (double.IsPositiveInfinity(d))
+ return "+Infinity";
+ if (double.IsNegativeInfinity(d))
+ return "-Infinity";
+ if (double.IsNaN(d))
+ return "NaN";
+
+ // Translate the double into sign, exponent and mantissa.
+ long bits = BitConverter.DoubleToInt64Bits(d);
+ // Note that the shift is sign-extended, hence the test against -1 not 1
+ bool negative = (bits < 0);
+ int exponent = (int) ((bits >> 52) & 0x7ffL);
+ long mantissa = bits & 0xfffffffffffffL;
+
+ // Subnormal numbers; exponent is effectively one higher,
+ // but there's no extra normalisation bit in the mantissa
+ if (exponent==0)
+ {
+ exponent++;
+ }
+ // Normal numbers; leave exponent as it is but add extra
+ // bit to the front of the mantissa
+ else
+ {
+ mantissa = mantissa | (1L<<52);
+ }
+
+ // Bias the exponent. It's actually biased by 1023, but we're
+ // treating the mantissa as m.0 rather than 0.m, so we need
+ // to subtract another 52 from it.
+ exponent -= 1075;
+
+ if (mantissa == 0)
+ {
+ return "0";
+ }
+
+ /* Normalize */
+ while((mantissa & 1) == 0)
+ { /* i.e., Mantissa is even */
+ mantissa >>= 1;
+ exponent++;
+ }
+
+ /// Construct a new decimal expansion with the mantissa
+ ArbitraryDecimal ad = new ArbitraryDecimal (mantissa);
+
+ // If the exponent is less than 0, we need to repeatedly
+ // divide by 2 - which is the equivalent of multiplying
+ // by 5 and dividing by 10.
+ if (exponent < 0)
+ {
+ for (int i=0; i < -exponent; i++)
+ ad.MultiplyBy(5);
+ ad.Shift(-exponent);
+ }
+ // Otherwise, we need to repeatedly multiply by 2
+ else
+ {
+ for (int i=0; i < exponent; i++)
+ ad.MultiplyBy(2);
+ }
+
+ // Finally, return the string with an appropriate sign
+ if (negative)
+ return "-"+ad.ToString();
+ else
+ return ad.ToString();
+ }
+
+ /// Private class used for manipulating
+ class ArbitraryDecimal
+ {
+ /// Digits in the decimal expansion, one byte per digit
+ byte[] digits;
+ ///
+ /// How many digits are *after* the decimal point
+ ///
+ int decimalPoint=0;
+
+ ///
+ /// Constructs an arbitrary decimal expansion from the given long.
+ /// The long must not be negative.
+ ///
+ internal ArbitraryDecimal (long x)
+ {
+ string tmp = x.ToString(CultureInfo.InvariantCulture);
+ digits = new byte[tmp.Length];
+ for (int i=0; i < tmp.Length; i++)
+ digits[i] = (byte) (tmp[i]-'0');
+ Normalize();
+ }
+
+ ///
+ /// Multiplies the current expansion by the given amount, which should
+ /// only be 2 or 5.
+ ///
+ internal void MultiplyBy(int amount)
+ {
+ byte[] result = new byte[digits.Length+1];
+ for (int i=digits.Length-1; i >= 0; i--)
+ {
+ int resultDigit = digits[i]*amount+result[i+1];
+ result[i]=(byte)(resultDigit/10);
+ result[i+1]=(byte)(resultDigit%10);
+ }
+ if (result[0] != 0)
+ {
+ digits=result;
+ }
+ else
+ {
+ Array.Copy (result, 1, digits, 0, digits.Length);
+ }
+ Normalize();
+ }
+
+ ///
+ /// Shifts the decimal point; a negative value makes
+ /// the decimal expansion bigger (as fewer digits come after the
+ /// decimal place) and a positive value makes the decimal
+ /// expansion smaller.
+ ///
+ internal void Shift (int amount)
+ {
+ decimalPoint += amount;
+ }
+
+ ///
+ /// Removes leading/trailing zeroes from the expansion.
+ ///
+ internal void Normalize()
+ {
+ int first;
+ for (first=0; first < digits.Length; first++)
+ if (digits[first]!=0)
+ break;
+ int last;
+ for (last=digits.Length-1; last >= 0; last--)
+ if (digits[last]!=0)
+ break;
+
+ if (first==0 && last==digits.Length-1)
+ return;
+
+ byte[] tmp = new byte[last-first+1];
+ for (int i=0; i < tmp.Length; i++)
+ tmp[i]=digits[i+first];
+
+ decimalPoint -= digits.Length-(last+1);
+ digits=tmp;
+ }
+
+ ///
+ /// Converts the value to a proper decimal string representation.
+ ///
+ public override String ToString()
+ {
+ char[] digitString = new char[digits.Length];
+ for (int i=0; i < digits.Length; i++)
+ digitString[i] = (char)(digits[i]+'0');
+
+ // Simplest case - nothing after the decimal point,
+ // and last real digit is non-zero, eg value=35
+ if (decimalPoint==0)
+ {
+ return new string (digitString);
+ }
+
+ // Fairly simple case - nothing after the decimal
+ // point, but some 0s to add, eg value=350
+ if (decimalPoint < 0)
+ {
+ return new string (digitString)+
+ new string ('0', -decimalPoint);
+ }
+
+ // Nothing before the decimal point, eg 0.035
+ if (decimalPoint >= digitString.Length)
+ {
+ return "0."+
+ new string ('0',(decimalPoint-digitString.Length))+
+ new string (digitString);
+ }
+
+ // Most complicated case - part of the string comes
+ // before the decimal point, part comes after it,
+ // eg 3.5
+ return new string (digitString, 0,
+ digitString.Length-decimalPoint)+
+ "."+
+ new string (digitString,
+ digitString.Length-decimalPoint,
+ decimalPoint);
+ }
+ }
+}
diff --git a/Nuclex.Support (net-4.0).csproj b/Nuclex.Support (net-4.0).csproj
index 14f0286..6981346 100644
--- a/Nuclex.Support (net-4.0).csproj
+++ b/Nuclex.Support (net-4.0).csproj
@@ -197,6 +197,7 @@
WeakCollection.cs
+
PartialStream.cs
@@ -335,6 +336,7 @@
+
diff --git a/Nuclex.Support (xna-4.0-phone7).csproj b/Nuclex.Support (xna-4.0-phone7).csproj
index 923c4f4..2c3c325 100644
--- a/Nuclex.Support (xna-4.0-phone7).csproj
+++ b/Nuclex.Support (xna-4.0-phone7).csproj
@@ -228,6 +228,7 @@
WeakCollection.cs
+
PartialStream.cs
@@ -366,6 +367,7 @@
+
diff --git a/Nuclex.Support (xna-4.0-xbox360).csproj b/Nuclex.Support (xna-4.0-xbox360).csproj
index 719a295..bd0dbe3 100644
--- a/Nuclex.Support (xna-4.0-xbox360).csproj
+++ b/Nuclex.Support (xna-4.0-xbox360).csproj
@@ -239,6 +239,7 @@
WeakCollection.cs
+
PartialStream.cs
@@ -377,6 +378,7 @@
+
diff --git a/Source/Collections/ObservableSet.Test.cs b/Source/Collections/ObservableSet.Test.cs
index 7a28f76..ea9d26c 100644
--- a/Source/Collections/ObservableSet.Test.cs
+++ b/Source/Collections/ObservableSet.Test.cs
@@ -194,6 +194,35 @@ namespace Nuclex.Support.Collections {
Assert.IsFalse(set2.IsSubsetOf(set1));
}
+ ///
+ /// Verifies that a set can determine if another set overlaps with it
+ ///
+ [Test]
+ public void CanDetermineOverlap() {
+ var set1 = new ObservableSet() { 1, 3, 5 };
+ var set2 = new ObservableSet() { 3 };
+
+ Assert.IsTrue(set1.Overlaps(set2));
+ Assert.IsTrue(set2.Overlaps(set1));
+ }
+
+ ///
+ /// Verifies that a set can determine if another set contains the same elements
+ ///
+ [Test]
+ public void CanDetermineSetEquality() {
+ var set1 = new ObservableSet() { 1, 3, 5 };
+ var set2 = new ObservableSet() { 3, 1, 5 };
+
+ Assert.IsTrue(set1.SetEquals(set2));
+ Assert.IsTrue(set2.SetEquals(set1));
+
+ set1.Add(7);
+
+ Assert.IsFalse(set1.SetEquals(set2));
+ Assert.IsFalse(set2.SetEquals(set1));
+ }
+
/// Creates mock object for the test
private MockFactory mockFactory;
/// Observable set being tested
diff --git a/Source/Collections/ObservableSet.cs b/Source/Collections/ObservableSet.cs
index a680f15..bdb6172 100644
--- a/Source/Collections/ObservableSet.cs
+++ b/Source/Collections/ObservableSet.cs
@@ -37,9 +37,9 @@ namespace Nuclex.Support.Collections {
ISet,
ICollection,
#if !NO_SPECIALIZED_COLLECTIONS
- INotifyCollectionChanged,
+ INotifyCollectionChanged,
#endif
- IObservableCollection {
+ IObservableCollection {
/// Raised when an item has been added to the collection
public event EventHandler> ItemAdded;
diff --git a/Source/EnumHelper.cs b/Source/EnumHelper.cs
index 0ab9123..60d62c5 100644
--- a/Source/EnumHelper.cs
+++ b/Source/EnumHelper.cs
@@ -29,22 +29,22 @@ namespace Nuclex.Support {
public static class EnumHelper {
/// Returns the highest value encountered in an enumeration
- ///
+ ///
/// Enumeration of which the highest value will be returned
///
/// The highest value in the enumeration
- public static EnumType GetHighestValue() where EnumType : IComparable {
- EnumType[] values = GetValues();
+ public static TEnum GetHighestValue() where TEnum : IComparable {
+ TEnum[] values = GetValues();
// If the enumeration is empty, return nothing
if(values.Length == 0) {
- return default(EnumType);
+ return default(TEnum);
}
// Look for the highest value in the enumeration. We initialize the highest value
// to the first enumeration value so we don't have to use some arbitrary starting
// value which might actually appear in the enumeration.
- EnumType highestValue = values[0];
+ TEnum highestValue = values[0];
for(int index = 1; index < values.Length; ++index) {
if(values[index].CompareTo(highestValue) > 0) {
highestValue = values[index];
@@ -81,7 +81,7 @@ namespace Nuclex.Support {
}
/// Retrieves a list of all values contained in an enumeration
- ///
+ ///
/// Type of the enumeration whose values will be returned
///
/// All values contained in the specified enumeration
@@ -89,21 +89,21 @@ namespace Nuclex.Support {
/// This method produces collectable garbage so it's best to only call it once
/// and cache the result.
///
- public static EnumType[] GetValues() {
+ public static TEnum[] GetValues() {
#if XBOX360 || WINDOWS_PHONE
- return GetValuesXbox360();
+ return GetValuesXbox360();
#else
- return (EnumType[])Enum.GetValues(typeof(EnumType));
+ return (TEnum[])Enum.GetValues(typeof(TEnum));
#endif
}
/// Retrieves a list of all values contained in an enumeration
- ///
+ ///
/// Type of the enumeration whose values will be returned
///
/// All values contained in the specified enumeration
- internal static EnumType[] GetValuesXbox360() {
- Type enumType = typeof(EnumType);
+ internal static TEnum[] GetValuesXbox360() {
+ Type enumType = typeof(TEnum);
if(!enumType.IsEnum) {
throw new ArgumentException(
"The provided type needs to be an enumeration", "EnumType"
@@ -117,9 +117,9 @@ namespace Nuclex.Support {
// Create an array to hold the enumeration values and copy them over from
// the fields we just retrieved
- EnumType[] values = new EnumType[fieldInfos.Length];
+ TEnum[] values = new TEnum[fieldInfos.Length];
for(int index = 0; index < fieldInfos.Length; ++index) {
- values[index] = (EnumType)fieldInfos[index].GetValue(null);
+ values[index] = (TEnum)fieldInfos[index].GetValue(null);
}
return values;
diff --git a/Source/GarbagePolicy.cs b/Source/GarbagePolicy.cs
new file mode 100644
index 0000000..1d82f0e
--- /dev/null
+++ b/Source/GarbagePolicy.cs
@@ -0,0 +1,33 @@
+#region CPL License
+/*
+Nuclex Framework
+Copyright (C) 2002-2012 Nuclex Development Labs
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the IBM Common Public License as
+published by the IBM Corporation; either version 1.0 of the
+License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+IBM Common Public License for more details.
+
+You should have received a copy of the IBM Common Public
+License along with this library
+*/
+#endregion
+
+using System;
+
+namespace Nuclex.Support {
+
+ /// How to behave in in respect to the garbage collector
+ public enum GarbagePolicy {
+ /// Avoid feeding the garbage collector whenever possible
+ Avoid,
+ /// Accept garbage production
+ Accept
+ }
+
+} // namespace Nuclex.Support
diff --git a/Source/PropertyChangedEventArgsHelper.cs b/Source/PropertyChangedEventArgsHelper.cs
index e9105e2..bf7d3ec 100644
--- a/Source/PropertyChangedEventArgsHelper.cs
+++ b/Source/PropertyChangedEventArgsHelper.cs
@@ -175,9 +175,6 @@ namespace Nuclex.Support {
///
/// Determines whether the property change affects the specified property
///
- ///
- /// Type of the property that will be tested for being affected
- ///
///
/// Property change that has been reported by the observed object
///
diff --git a/Source/Semaphore.cs b/Source/Semaphore.cs
index 1ea591a..49c85b4 100644
--- a/Source/Semaphore.cs
+++ b/Source/Semaphore.cs
@@ -56,10 +56,12 @@ namespace Nuclex.Support {
/// become eligible for execution.
///
///
-#if !(XBOX360 || WINDOWS_PHONE)
+#if WINDOWS
[Obsolete("Prefer the normal semaphore on Windows builds.")]
-#endif
+ internal class Semaphore : WaitHandle {
+#else
public class Semaphore : WaitHandle {
+#endif
/// Initializes a new semaphore
public Semaphore() {
diff --git a/Source/Shared.cs b/Source/Shared.cs
index 253cb9c..ac6726b 100644
--- a/Source/Shared.cs
+++ b/Source/Shared.cs
@@ -24,13 +24,13 @@ using System.Diagnostics;
namespace Nuclex.Support {
/// Manages a globally shared instance of the given Type
- ///
+ ///
/// Type of which a globally shared instance will be provided
///
- public static class Shared where SharedType : new() {
+ public static class Shared where TShared : new() {
/// Returns the global instance of the class
- public static SharedType Instance {
+ public static TShared Instance {
[DebuggerStepThrough]
get {
return instance;
@@ -38,7 +38,7 @@ namespace Nuclex.Support {
}
/// Stored the globally shared instance
- private static readonly SharedType instance = new SharedType();
+ private static readonly TShared instance = new TShared();
}
diff --git a/Source/StringBuilderHelper.Test.cs b/Source/StringBuilderHelper.Test.cs
index e3a669a..19dae80 100644
--- a/Source/StringBuilderHelper.Test.cs
+++ b/Source/StringBuilderHelper.Test.cs
@@ -40,8 +40,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendByte() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, (byte)255);
+ builder.Append((byte)255, GarbagePolicy.Avoid);
+ Assert.AreEqual("255", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append((byte)255, GarbagePolicy.Accept);
Assert.AreEqual("255", builder.ToString());
}
@@ -51,8 +56,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendNullByte() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, (byte)0);
+ builder.Append((byte)0, GarbagePolicy.Avoid);
+ Assert.AreEqual("0", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append((byte)0, GarbagePolicy.Accept);
Assert.AreEqual("0", builder.ToString());
}
@@ -62,8 +72,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendPositiveInteger() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 12345);
+ builder.Append(12345, GarbagePolicy.Avoid);
+ Assert.AreEqual("12345", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(12345, GarbagePolicy.Accept);
Assert.AreEqual("12345", builder.ToString());
}
@@ -73,8 +88,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendNullInteger() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 0);
+ builder.Append(0, GarbagePolicy.Avoid);
+ Assert.AreEqual("0", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(0, GarbagePolicy.Accept);
Assert.AreEqual("0", builder.ToString());
}
@@ -84,8 +104,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendNegativeInteger() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, -12345);
+ builder.Append(-12345, GarbagePolicy.Avoid);
+ Assert.AreEqual("-12345", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(-12345, GarbagePolicy.Accept);
Assert.AreEqual("-12345", builder.ToString());
}
@@ -95,8 +120,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendPositiveLong() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 12345L);
+ builder.Append(12345L, GarbagePolicy.Avoid);
+ Assert.AreEqual("12345", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(12345L, GarbagePolicy.Accept);
Assert.AreEqual("12345", builder.ToString());
}
@@ -106,8 +136,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendNullLong() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 0L);
+ builder.Append(0L, GarbagePolicy.Avoid);
+ Assert.AreEqual("0", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(0L, GarbagePolicy.Accept);
Assert.AreEqual("0", builder.ToString());
}
@@ -117,8 +152,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendNegativeLong() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, -12345L);
+ builder.Append(-12345L, GarbagePolicy.Avoid);
+ Assert.AreEqual("-12345", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(-12345L, GarbagePolicy.Accept);
Assert.AreEqual("-12345", builder.ToString());
}
@@ -128,9 +168,14 @@ namespace Nuclex.Support {
[Test]
public void TestAppendNegativeFloat() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, -32.015625f);
- Assert.AreEqual("-32.015625", builder.ToString());
+ builder.Append(-0.125f, GarbagePolicy.Avoid);
+ Assert.AreEqual("-0.125", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(-0.125f, GarbagePolicy.Accept);
+ Assert.AreEqual("-0.125", builder.ToString());
}
///
@@ -139,8 +184,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendPositiveFloat() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 10.0625f);
+ builder.Append(10.0625f, GarbagePolicy.Avoid);
+ Assert.AreEqual("10.0625", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(10.0625f, GarbagePolicy.Accept);
Assert.AreEqual("10.0625", builder.ToString());
}
@@ -150,8 +200,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendSmallFloat() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 0.00390625f);
+ builder.Append(0.00390625f, GarbagePolicy.Avoid);
+ Assert.AreEqual("0.00390625", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(0.00390625f, GarbagePolicy.Accept);
Assert.AreEqual("0.00390625", builder.ToString());
}
@@ -161,17 +216,21 @@ namespace Nuclex.Support {
[Test]
public void TestAppendHugeFloat() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 1000000000.0f);
+ builder.Append(1000000000.0f, GarbagePolicy.Avoid);
Assert.AreEqual("1000000000.0", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(1000000000.0f, GarbagePolicy.Accept);
+ Assert.AreEqual("1E+09", builder.ToString());
}
/// Tests whether the number of decimal places can be restricted
[Test]
public void TestAppendFloatLimitDecimalPlaces() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 0.00390625f, 3);
-
+ builder.Append(0.00390625f, 3);
Assert.AreEqual("0.003", builder.ToString());
}
@@ -181,8 +240,7 @@ namespace Nuclex.Support {
[Test]
public void TestAppendFloatWithoutDecimalPlaces() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 0.00390625f, 0);
-
+ builder.Append(0.00390625f, 0);
Assert.AreEqual("0", builder.ToString()); // Note: no rounding!
}
@@ -192,10 +250,10 @@ namespace Nuclex.Support {
[Test]
public void TestAppendOutOfRangeFloat() {
StringBuilder builder = new StringBuilder();
- Assert.IsFalse(StringBuilderHelper.Append(builder, float.PositiveInfinity));
- Assert.IsFalse(StringBuilderHelper.Append(builder, float.NegativeInfinity));
- Assert.IsFalse(StringBuilderHelper.Append(builder, float.NaN));
- Assert.IsFalse(StringBuilderHelper.Append(builder, 0.000000059604644775390625f));
+ Assert.IsFalse(builder.Append(float.PositiveInfinity, GarbagePolicy.Avoid));
+ Assert.IsFalse(builder.Append(float.NegativeInfinity, GarbagePolicy.Avoid));
+ Assert.IsFalse(builder.Append(float.NaN, GarbagePolicy.Avoid));
+ Assert.IsFalse(builder.Append(0.000000059604644775390625f, GarbagePolicy.Avoid));
}
///
@@ -205,8 +263,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendNegativeDouble() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, -32.015625);
+ builder.Append(-32.015625, GarbagePolicy.Avoid);
+ Assert.AreEqual("-32.015625", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(-32.015625, GarbagePolicy.Accept);
Assert.AreEqual("-32.015625", builder.ToString());
}
@@ -217,8 +280,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendPositiveDouble() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 10.0625);
+ builder.Append(10.0625, GarbagePolicy.Avoid);
+ Assert.AreEqual("10.0625", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(10.0625, GarbagePolicy.Accept);
Assert.AreEqual("10.0625", builder.ToString());
}
@@ -229,8 +297,13 @@ namespace Nuclex.Support {
[Test]
public void TestAppendSmallDouble() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 0.00390625);
+ builder.Append(0.00390625, GarbagePolicy.Avoid);
+ Assert.AreEqual("0.00390625", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(0.00390625, GarbagePolicy.Accept);
Assert.AreEqual("0.00390625", builder.ToString());
}
@@ -241,9 +314,14 @@ namespace Nuclex.Support {
[Test]
public void TestAppendHugeDouble() {
StringBuilder builder = new StringBuilder();
- StringBuilderHelper.Append(builder, 1000000000000000000.0);
+ builder.Append(1000000000000000000.0, GarbagePolicy.Avoid);
Assert.AreEqual("1000000000000000000.0", builder.ToString());
+
+ builder.Clear();
+
+ builder.Append(1000000000000000000.0, GarbagePolicy.Accept);
+ Assert.AreEqual("1E+18", builder.ToString());
}
/// Tests whether the number of decimal places can be restricted
@@ -273,12 +351,10 @@ namespace Nuclex.Support {
[Test]
public void TestAppendOutOfRangeDouble() {
StringBuilder builder = new StringBuilder();
- Assert.IsFalse(StringBuilderHelper.Append(builder, double.PositiveInfinity));
- Assert.IsFalse(StringBuilderHelper.Append(builder, double.NegativeInfinity));
- Assert.IsFalse(StringBuilderHelper.Append(builder, double.NaN));
- Assert.IsFalse(
- StringBuilderHelper.Append(builder, 1.1102230246251565404236316680908e-16)
- );
+ Assert.IsFalse(builder.Append(double.PositiveInfinity, GarbagePolicy.Avoid));
+ Assert.IsFalse(builder.Append(double.NegativeInfinity, GarbagePolicy.Avoid));
+ Assert.IsFalse(builder.Append(double.NaN, GarbagePolicy.Avoid));
+ Assert.IsFalse(builder.Append(1.1102230246251565404236316680908e-16, GarbagePolicy.Avoid));
}
///
@@ -291,7 +367,7 @@ namespace Nuclex.Support {
Assert.AreEqual(string.Empty, builder.ToString());
}
-
+
}
} // namespace Nuclex.Support
diff --git a/Source/StringBuilderHelper.cs b/Source/StringBuilderHelper.cs
index f1468a4..fc81632 100644
--- a/Source/StringBuilderHelper.cs
+++ b/Source/StringBuilderHelper.cs
@@ -25,12 +25,6 @@ using System.Text;
namespace Nuclex.Support {
- /*
- public enum Garbage {
- Avoid,
- Accept
- }
- */
/// Contains helper methods for the string builder class
public static class StringBuilderHelper {
@@ -50,13 +44,20 @@ namespace Nuclex.Support {
///
/// String builder to which an integer will be appended
/// Byte that will be appended to the string builder
+ /// How to behave regarding the garbage collector
///
/// The normal StringBuilder.Append() method generates garbage when converting
/// integer arguments whereas this method will avoid any garbage, albeit probably
/// with a small performance impact compared to the built-in method.
///
- public static void Append(StringBuilder builder, byte value) {
- recursiveAppend(builder, value);
+ public static void Append(
+ this StringBuilder builder, byte value, GarbagePolicy garbagePolicy
+ ) {
+ if(garbagePolicy == GarbagePolicy.Avoid) {
+ recursiveAppend(builder, value);
+ } else {
+ builder.Append((int)value);
+ }
}
///
@@ -64,17 +65,24 @@ namespace Nuclex.Support {
///
/// String builder to which an integer will be appended
/// Integer that will be appended to the string builder
+ /// How to behave regarding the garbage collector
///
/// The normal StringBuilder.Append() method generates garbage when converting
/// integer arguments whereas this method will avoid any garbage, albeit probably
/// with a small performance impact compared to the built-in method.
///
- public static void Append(StringBuilder builder, int value) {
- if (value < 0) {
- builder.Append('-');
- recursiveAppend(builder, -value);
+ public static void Append(
+ this StringBuilder builder, int value, GarbagePolicy garbagePolicy
+ ) {
+ if(garbagePolicy == GarbagePolicy.Avoid) {
+ if(value < 0) {
+ builder.Append('-');
+ recursiveAppend(builder, -value);
+ } else {
+ recursiveAppend(builder, value);
+ }
} else {
- recursiveAppend(builder, value);
+ builder.Append(value);
}
}
@@ -83,17 +91,24 @@ namespace Nuclex.Support {
///
/// String builder to which an integer will be appended
/// Long integer that will be appended to the string builder
+ /// How to behave regarding the garbage collector
///
/// The normal StringBuilder.Append() method generates garbage when converting
/// integer arguments whereas this method will avoid any garbage, albeit probably
/// with a small performance impact compared to the built-in method.
///
- public static void Append(StringBuilder builder, long value) {
- if (value < 0) {
- builder.Append('-');
- recursiveAppend(builder, -value);
+ public static void Append(
+ this StringBuilder builder, long value, GarbagePolicy garbagePolicy
+ ) {
+ if(garbagePolicy == GarbagePolicy.Avoid) {
+ if(value < 0) {
+ builder.Append('-');
+ recursiveAppend(builder, -value);
+ } else {
+ recursiveAppend(builder, value);
+ }
} else {
- recursiveAppend(builder, value);
+ builder.Append(value);
}
}
@@ -102,14 +117,22 @@ namespace Nuclex.Support {
///
/// String builder the value will be appended to
/// Value that will be appended to the string builder
+ /// How to behave regarding the garbage collector
/// Whether the value was inside the algorithm's supported range
///
/// Uses an algorithm that covers the sane range of possible values but will
/// fail to render extreme values, NaNs and infinity. In these cases, false
/// is returned and the traditional double.ToString() method can be used.
///
- public static bool Append(StringBuilder builder, float value) {
- return Append(builder, value, int.MaxValue);
+ public static bool Append(
+ this StringBuilder builder, float value, GarbagePolicy garbagePolicy
+ ) {
+ if(garbagePolicy == GarbagePolicy.Avoid) {
+ return Append(builder, value, int.MaxValue);
+ } else {
+ builder.Append(value);
+ return true;
+ }
}
///
@@ -124,7 +147,7 @@ namespace Nuclex.Support {
/// fail to render extreme values, NaNs and infinity. In these cases, false
/// is returned and the traditional double.ToString() method can be used.
///
- public static bool Append(StringBuilder builder, float value, int decimalPlaces) {
+ public static bool Append(this StringBuilder builder, float value, int decimalPlaces) {
const int ExponentBits = 0xFF; // Bit mask for the exponent bits
const int FractionalBitCount = 23; // Number of bits for fractional part
const int ExponentBias = 127; // Bias subtraced from exponent
@@ -142,9 +165,9 @@ namespace Nuclex.Support {
int integral;
int fractional;
- if (exponent >= 0) {
- if (exponent >= FractionalBitCount) {
- if (exponent >= NumericBitCount) {
+ if(exponent >= 0) {
+ if(exponent >= FractionalBitCount) {
+ if(exponent >= NumericBitCount) {
return false;
}
integral = mantissa << (exponent - FractionalBitCount);
@@ -154,7 +177,7 @@ namespace Nuclex.Support {
fractional = (mantissa << (exponent + 1)) & FractionalBits;
}
} else {
- if (exponent < -FractionalBitCount) {
+ if(exponent < -FractionalBitCount) {
return false;
}
integral = 0;
@@ -162,30 +185,30 @@ namespace Nuclex.Support {
}
// Build the integral part
- if (intValue < 0) {
+ if(intValue < 0) {
builder.Append('-');
}
- if (integral == 0) {
+ if(integral == 0) {
builder.Append('0');
} else {
recursiveAppend(builder, integral);
}
- if (decimalPlaces > 0) {
+ if(decimalPlaces > 0) {
builder.Append('.');
// Build the fractional part
- if (fractional == 0) {
+ if(fractional == 0) {
builder.Append('0');
} else {
- while (fractional != 0) {
+ while(fractional != 0) {
fractional *= 10;
int digit = (fractional >> FractionalBitCountPlusOne);
builder.Append(numbers[digit]);
fractional &= FractionalBits;
--decimalPlaces;
- if (decimalPlaces == 0) {
+ if(decimalPlaces == 0) {
break;
}
}
@@ -201,14 +224,22 @@ namespace Nuclex.Support {
///
/// String builder the value will be appended to
/// Value that will be appended to the string builder
+ /// How to behave regarding the garbage collector
/// Whether the value was inside the algorithm's supported range
///
/// Uses an algorithm that covers the sane range of possible values but will
/// fail to render extreme values, NaNs and infinity. In these cases, false
/// is returned and the traditional double.ToString() method can be used.
///
- public static bool Append(StringBuilder builder, double value) {
- return Append(builder, value, int.MaxValue);
+ public static bool Append(
+ this StringBuilder builder, double value, GarbagePolicy garbagePolicy
+ ) {
+ if(garbagePolicy == GarbagePolicy.Avoid) {
+ return Append(builder, value, int.MaxValue);
+ } else {
+ builder.Append(value);
+ return true;
+ }
}
///
@@ -224,7 +255,7 @@ namespace Nuclex.Support {
/// fail to render extreme values, NaNs and infinity. In these cases, false
/// is returned and the traditional double.ToString() method can be used.
///
- public static bool Append(StringBuilder builder, double value, int decimalPlaces) {
+ public static bool Append(this StringBuilder builder, double value, int decimalPlaces) {
const long ExponentBits = 0x7FF; // Bit mask for the exponent bits
const int FractionalBitCount = 52; // Number of bits for fractional part
const int ExponentBias = 1023; // Bias subtraced from exponent
@@ -242,9 +273,9 @@ namespace Nuclex.Support {
long integral;
long fractional;
- if (exponent >= 0) {
- if (exponent >= FractionalBitCount) {
- if (exponent >= NumericBitCount) {
+ if(exponent >= 0) {
+ if(exponent >= FractionalBitCount) {
+ if(exponent >= NumericBitCount) {
return false;
}
integral = mantissa << (int)(exponent - FractionalBitCount);
@@ -254,7 +285,7 @@ namespace Nuclex.Support {
fractional = (mantissa << (int)(exponent + 1)) & FractionalBits;
}
} else {
- if (exponent < -FractionalBitCount) {
+ if(exponent < -FractionalBitCount) {
return false;
}
integral = 0;
@@ -262,30 +293,30 @@ namespace Nuclex.Support {
}
// Build the integral part
- if (longValue < 0) {
+ if(longValue < 0) {
builder.Append('-');
}
- if (integral == 0) {
+ if(integral == 0) {
builder.Append('0');
} else {
recursiveAppend(builder, integral);
}
- if (decimalPlaces > 0) {
+ if(decimalPlaces > 0) {
builder.Append('.');
// Build the fractional part
- if (fractional == 0) {
+ if(fractional == 0) {
builder.Append('0');
} else {
- while (fractional != 0) {
+ while(fractional != 0) {
fractional *= 10;
long digit = (fractional >> FractionalBitCountPlusOne);
builder.Append(numbers[digit]);
fractional &= FractionalBits;
--decimalPlaces;
- if (decimalPlaces == 0) {
+ if(decimalPlaces == 0) {
break;
}
}
@@ -307,7 +338,7 @@ namespace Nuclex.Support {
int tenth = remaining / 10;
#endif
- if (tenth > 0) {
+ if(tenth > 0) {
recursiveAppend(builder, tenth);
}
@@ -326,7 +357,7 @@ namespace Nuclex.Support {
long tenth = remaining / 10;
#endif
- if (tenth > 0) {
+ if(tenth > 0) {
recursiveAppend(builder, tenth);
}