Added an interface for the scheduler service (this might be once candidate for replacement by different implementations); added a new helper class for the StringBuilder that allows garbage-free appending of integers and floats; added unit tests for most of the code
git-svn-id: file:///srv/devel/repo-conversion/nusu@188 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
66f0ae9b34
commit
237fb57fc8
|
@ -145,6 +145,7 @@
|
|||
<Compile Include="Source\Scheduling\GenericTimeSource.Test.cs">
|
||||
<DependentUpon>GenericTimeSource.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Scheduling\ISchedulerService.cs" />
|
||||
<Compile Include="Source\Scheduling\ITimeSource.cs" />
|
||||
<Compile Include="Source\Scheduling\Scheduler.cs" />
|
||||
<Compile Include="Source\Scheduling\GenericTimeSource.cs" />
|
||||
|
@ -305,6 +306,10 @@
|
|||
<Compile Include="Source\IO\ChainStream.Test.cs">
|
||||
<DependentUpon>ChainStream.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\StringBuilderHelper.cs" />
|
||||
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
||||
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\StringHelper.cs" />
|
||||
<Compile Include="Source\StringHelper.Test.cs">
|
||||
<DependentUpon>StringHelper.cs</DependentUpon>
|
||||
|
|
|
@ -131,6 +131,7 @@
|
|||
<Compile Include="Source\Scheduling\GenericTimeSource.Test.cs">
|
||||
<DependentUpon>GenericTimeSource.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Scheduling\ISchedulerService.cs" />
|
||||
<Compile Include="Source\Scheduling\ITimeSource.cs" />
|
||||
<Compile Include="Source\Scheduling\Scheduler.cs" />
|
||||
<Compile Include="Source\Scheduling\GenericTimeSource.cs" />
|
||||
|
@ -291,6 +292,10 @@
|
|||
<Compile Include="Source\IO\ChainStream.Test.cs">
|
||||
<DependentUpon>ChainStream.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\StringBuilderHelper.cs" />
|
||||
<Compile Include="Source\StringBuilderHelper.Test.cs">
|
||||
<DependentUpon>StringBuilderHelper.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\StringHelper.cs" />
|
||||
<Compile Include="Source\StringHelper.Test.cs">
|
||||
<DependentUpon>StringHelper.cs</DependentUpon>
|
||||
|
|
|
@ -18,11 +18,11 @@ License along with this library
|
|||
*/
|
||||
#endregion
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
|
101
Source/Scheduling/ISchedulerService.cs
Normal file
101
Source/Scheduling/ISchedulerService.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2009 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Nuclex.Support.Scheduling {
|
||||
|
||||
/// <summary>Service that allows the scheduled invocation of tasks</summary>
|
||||
public interface ISchedulerService {
|
||||
|
||||
/// <summary>Schedules a notification at the specified absolute time</summary>
|
||||
/// <param name="notificationTime">
|
||||
/// Absolute time at which the notification will occur
|
||||
/// </param>
|
||||
/// <param name="callback">
|
||||
/// Callback that will be invoked when the notification is due
|
||||
/// </param>
|
||||
/// <returns>A handle that can be used to cancel the notification</returns>
|
||||
/// <remarks>
|
||||
/// The notification is scheduled for the indicated absolute time. If the system
|
||||
/// enters/leaves daylight saving time or the date/time is changed (for example
|
||||
/// when the system synchronizes with an NTP server), this will affect
|
||||
/// the notification. So if you need to be notified after a fixed time, use
|
||||
/// the NotifyIn() method instead.
|
||||
/// </remarks>
|
||||
object NotifyAt(DateTime notificationTime, WaitCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Schedules a recurring notification after the specified amount of milliseconds
|
||||
/// </summary>
|
||||
/// <param name="delayMilliseconds">
|
||||
/// Milliseconds after which the first notification will occur
|
||||
/// </param>
|
||||
/// <param name="intervalMilliseconds">
|
||||
/// Interval in milliseconds at which the notification will be repeated
|
||||
/// </param>
|
||||
/// <param name="callback">
|
||||
/// Callback that will be invoked when the notification is due
|
||||
/// </param>
|
||||
/// <returns>A handle that can be used to cancel the notification</returns>
|
||||
object NotifyEach(int delayMilliseconds, int intervalMilliseconds, WaitCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Schedules a recurring notification after the specified time span
|
||||
/// </summary>
|
||||
/// <param name="delay">Delay after which the first notification will occur</param>
|
||||
/// <param name="interval">Interval at which the notification will be repeated</param>
|
||||
/// <param name="callback">
|
||||
/// Callback that will be invoked when the notification is due
|
||||
/// </param>
|
||||
/// <returns>A handle that can be used to cancel the notification</returns>
|
||||
object NotifyEach(TimeSpan delay, TimeSpan interval, WaitCallback callback);
|
||||
|
||||
/// <summary>
|
||||
/// Schedules a notification after the specified amount of milliseconds
|
||||
/// </summary>
|
||||
/// <param name="delayMilliseconds">
|
||||
/// Number of milliseconds after which the notification will occur
|
||||
/// </param>
|
||||
/// <param name="callback">
|
||||
/// Callback that will be invoked when the notification is due
|
||||
/// </param>
|
||||
/// <returns>A handle that can be used to cancel the notification</returns>
|
||||
object NotifyIn(int delayMilliseconds, WaitCallback callback);
|
||||
|
||||
/// <summary>Schedules a notification after the specified time span</summary>
|
||||
/// <param name="delay">Delay after which the notification will occur</param>
|
||||
/// <param name="callback">
|
||||
/// Callback that will be invoked when the notification is due
|
||||
/// </param>
|
||||
/// <returns>A handle that can be used to cancel the notification</returns>
|
||||
object NotifyIn(TimeSpan delay, WaitCallback callback);
|
||||
|
||||
/// <summary>Cancels a scheduled notification</summary>
|
||||
/// <param name="notificationHandle">
|
||||
/// Handle of the notification that will be cancelled
|
||||
/// </param>
|
||||
void Cancel(object notificationHandle);
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Scheduling
|
|
@ -28,7 +28,7 @@ using Nuclex.Support.Collections;
|
|||
namespace Nuclex.Support.Scheduling {
|
||||
|
||||
/// <summary>Schedules actions for execution at a future point in time</summary>
|
||||
public class Scheduler : IDisposable {
|
||||
public class Scheduler : ISchedulerService, IDisposable {
|
||||
|
||||
/// <summary>One tick is 100 ns, meaning 10000 ticks equal 1 ms</summary>
|
||||
private const long TicksPerMillisecond = 10000;
|
||||
|
|
151
Source/StringBuilderHelper.Test.cs
Normal file
151
Source/StringBuilderHelper.Test.cs
Normal file
|
@ -0,0 +1,151 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2009 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
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>
|
||||
/// Unit test for the helper class to .NET's string builder
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public class StringBuilderHelperTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that bytes are correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendByte() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, (byte)255);
|
||||
|
||||
Assert.AreEqual("255", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a byte with value 0 is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNullByte() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, (byte)0);
|
||||
|
||||
Assert.AreEqual("0", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a positive integer is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendPositiveInteger() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, 12345);
|
||||
|
||||
Assert.AreEqual("12345", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an integer with value 0 is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNullInteger() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, 0);
|
||||
|
||||
Assert.AreEqual("0", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a negative integer is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNegativeInteger() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, -12345);
|
||||
|
||||
Assert.AreEqual("-12345", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that negative floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNegativeFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, -32.015625f);
|
||||
|
||||
Assert.AreEqual("-32.015625", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that positive floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendPositiveFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, 10.0625f);
|
||||
|
||||
Assert.AreEqual("10.0625", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that very small floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendSmallFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, 0.00390625f);
|
||||
|
||||
Assert.AreEqual("0.00390625", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that very large floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendHugeFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, 1000000000.0f);
|
||||
|
||||
Assert.AreEqual("1000000000.0", builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the contents of a string builder can be cleared
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestClear() {
|
||||
StringBuilder builder = new StringBuilder("Hello World");
|
||||
StringBuilderHelper.Clear(builder);
|
||||
|
||||
Assert.AreEqual(string.Empty, builder.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
202
Source/StringBuilderHelper.cs
Normal file
202
Source/StringBuilderHelper.cs
Normal file
|
@ -0,0 +1,202 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2009 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Contains helper methods for the string builder class</summary>
|
||||
public class StringBuilderHelper {
|
||||
|
||||
/// <summary>Predefined unicode characters for the numbers 0 to 9</summary>
|
||||
private static readonly char[] numbers = new char[] {
|
||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
|
||||
};
|
||||
|
||||
/// <summary>Clears the contents of a string builder</summary>
|
||||
/// <param name="builder">String builder that will be cleared</param>
|
||||
public static void Clear(StringBuilder builder) {
|
||||
builder.Remove(0, builder.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends an integer to a string builder without generating garbage
|
||||
/// </summary>
|
||||
/// <param name="builder">String builder to which an integer will be appended</param>
|
||||
/// <param name="value">Byte that will be appended to the string builder</param>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static void Append(StringBuilder builder, byte value) {
|
||||
recursiveAppend(builder, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends an integer to a string builder without generating garbage
|
||||
/// </summary>
|
||||
/// <param name="builder">String builder to which an integer will be appended</param>
|
||||
/// <param name="value">Integer that will be appended to the string builder</param>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static void Append(StringBuilder builder, int value) {
|
||||
if(value < 0) {
|
||||
builder.Append('-');
|
||||
recursiveAppend(builder, -value);
|
||||
} else {
|
||||
recursiveAppend(builder, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends an long integer to a string builder without generating garbage
|
||||
/// </summary>
|
||||
/// <param name="builder">String builder to which an integer will be appended</param>
|
||||
/// <param name="value">Long integer that will be appended to the string builder</param>
|
||||
/// <remarks>
|
||||
/// 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.
|
||||
/// </remarks>
|
||||
public static void Append(StringBuilder builder, long value) {
|
||||
if(value < 0) {
|
||||
builder.Append('-');
|
||||
recursiveAppend(builder, -value);
|
||||
} else {
|
||||
recursiveAppend(builder, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a floating point value to a string builder without generating garbage
|
||||
/// </summary>
|
||||
/// <param name="builder">String builder the value will be appended to</param>
|
||||
/// <param name="value">Value that will be appended to the string builder</param>
|
||||
public static void Append(StringBuilder builder, float value) {
|
||||
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
|
||||
const int NumericBitCount = 31; // Bits without sign
|
||||
|
||||
// You do not modify these as they're calculated based on the
|
||||
const int FractionalBits = (2 << FractionalBitCount) - 1;
|
||||
const int HighestFractionalBit = (1 << FractionalBitCount);
|
||||
const int FractionalBitCountPlusOne = FractionalBitCount + 1;
|
||||
|
||||
int intValue = FloatHelper.ReinterpretAsInt(value);
|
||||
|
||||
int exponent = ((intValue >> FractionalBitCount) & ExponentBits) - ExponentBias;
|
||||
int mantissa = (intValue & FractionalBits) | HighestFractionalBit;
|
||||
|
||||
int integral;
|
||||
int fractional;
|
||||
if(exponent >= 0) {
|
||||
if(exponent >= FractionalBitCount) {
|
||||
Debug.Assert(exponent < NumericBitCount);
|
||||
integral = mantissa << (exponent - FractionalBitCount);
|
||||
fractional = 0;
|
||||
} else {
|
||||
integral = mantissa >> (FractionalBitCount - exponent);
|
||||
fractional = (mantissa << (exponent + 1)) & FractionalBits;
|
||||
}
|
||||
} else {
|
||||
Debug.Assert(exponent >= -FractionalBitCount);
|
||||
integral = 0;
|
||||
fractional = (mantissa & FractionalBits) >> -(exponent + 1);
|
||||
}
|
||||
/*
|
||||
if(exponent >= NumericBitCount) {
|
||||
throw new ArgumentException("Value too large", "value");
|
||||
} else if(exponent < -FractionalBitCount) {
|
||||
throw new ArgumentException("Value too small", "value");
|
||||
} else if(exponent >= FractionalBitCount) {
|
||||
integral = mantissa << (exponent - FractionalBitCount);
|
||||
fractional = 0;
|
||||
} else if(exponent >= 0) {
|
||||
integral = mantissa >> (FractionalBitCount - exponent);
|
||||
fractional = (mantissa << (exponent + 1)) & FractionalBits;
|
||||
} else { // exp2 < 0
|
||||
integral = 0;
|
||||
fractional = (mantissa & FractionalBits) >> -(exponent + 1);
|
||||
}
|
||||
*/
|
||||
// Build the integral part
|
||||
if(intValue < 0) {
|
||||
builder.Append('-');
|
||||
}
|
||||
if(integral == 0) {
|
||||
builder.Append('0');
|
||||
} else {
|
||||
recursiveAppend(builder, integral);
|
||||
}
|
||||
|
||||
builder.Append('.');
|
||||
|
||||
// Build the fractional part
|
||||
if(fractional == 0) {
|
||||
builder.Append('0');
|
||||
} else {
|
||||
while(fractional != 0) {
|
||||
fractional *= 10;
|
||||
int digit = (fractional >> FractionalBitCountPlusOne);
|
||||
builder.Append(numbers[digit]);
|
||||
fractional &= FractionalBits;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Recursively appends a number's characters to a string builder</summary>
|
||||
/// <param name="builder">String builder the number will be appended to</param>
|
||||
/// <param name="remaining">Remaining digits that will be recursively processed</param>
|
||||
private static void recursiveAppend(StringBuilder builder, int remaining) {
|
||||
int digit;
|
||||
int tenth = Math.DivRem(remaining, 10, out digit);
|
||||
|
||||
if(tenth > 0) {
|
||||
recursiveAppend(builder, tenth);
|
||||
}
|
||||
|
||||
builder.Append(numbers[digit]);
|
||||
}
|
||||
|
||||
/// <summary>Recursively appends a number's characters to a string builder</summary>
|
||||
/// <param name="builder">String builder the number will be appended to</param>
|
||||
/// <param name="remaining">Remaining digits that will be recursively processed</param>
|
||||
private static void recursiveAppend(StringBuilder builder, long remaining) {
|
||||
long digit;
|
||||
long tenth = Math.DivRem(remaining, 10, out digit);
|
||||
|
||||
if(tenth > 0) {
|
||||
recursiveAppend(builder, tenth);
|
||||
}
|
||||
|
||||
builder.Append(numbers[digit]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
|
@ -165,3 +165,4 @@ namespace Nuclex.Support {
|
|||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user