Converted all documents to Markdown
This commit is contained in:
parent
e146057071
commit
5fb433ad98
229
Documents/DoubleConverter.md
Normal file
229
Documents/DoubleConverter.md
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
Double Converter
|
||||||
|
================
|
||||||
|
|
||||||
|
Originally from http://www.yoda.arachsys.com/csharp/DoubleConverter.cs
|
||||||
|
|
||||||
|
This class converts doubles to strings without relying on the current locale.
|
||||||
|
The problem itself is quite deep and in the C++ side (including the supporting
|
||||||
|
libraries used to implement the .NET BCL), various techniques such as Dragon4,
|
||||||
|
Ryu and DragonBox are used.
|
||||||
|
|
||||||
|
The main problems are a) speed and b) full round-trip support such that a float
|
||||||
|
converted to a string (even if the decimal places are an infinite series or
|
||||||
|
have been rounded) will parse back to the exact same floating point number.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A class to allow the conversion of doubles to string representations of
|
||||||
|
/// their exact decimal values. The implementation aims for readability over
|
||||||
|
/// efficiency.
|
||||||
|
/// </summary>
|
||||||
|
public class DoubleConverter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the given double to a string representation of its
|
||||||
|
/// exact decimal value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="d">The double to convert.</param>
|
||||||
|
/// <returns>A string representation of the double's exact decimal value.</return>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Private class used for manipulating
|
||||||
|
class ArbitraryDecimal
|
||||||
|
{
|
||||||
|
/// <summary>Digits in the decimal expansion, one byte per digit
|
||||||
|
byte[] digits;
|
||||||
|
/// <summary>
|
||||||
|
/// How many digits are *after* the decimal point
|
||||||
|
/// </summary>
|
||||||
|
int decimalPoint=0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs an arbitrary decimal expansion from the given long.
|
||||||
|
/// The long must not be negative.
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Multiplies the current expansion by the given amount, which should
|
||||||
|
/// only be 2 or 5.
|
||||||
|
/// </summary>
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
internal void Shift (int amount)
|
||||||
|
{
|
||||||
|
decimalPoint += amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes leading/trailing zeroes from the expansion.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the value to a proper decimal string representation.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,218 +0,0 @@
|
|||||||
From http://www.yoda.arachsys.com/csharp/DoubleConverter.cs
|
|
||||||
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A class to allow the conversion of doubles to string representations of
|
|
||||||
/// their exact decimal values. The implementation aims for readability over
|
|
||||||
/// efficiency.
|
|
||||||
/// </summary>
|
|
||||||
public class DoubleConverter
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the given double to a string representation of its
|
|
||||||
/// exact decimal value.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="d">The double to convert.</param>
|
|
||||||
/// <returns>A string representation of the double's exact decimal value.</return>
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Private class used for manipulating
|
|
||||||
class ArbitraryDecimal
|
|
||||||
{
|
|
||||||
/// <summary>Digits in the decimal expansion, one byte per digit
|
|
||||||
byte[] digits;
|
|
||||||
/// <summary>
|
|
||||||
/// How many digits are *after* the decimal point
|
|
||||||
/// </summary>
|
|
||||||
int decimalPoint=0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs an arbitrary decimal expansion from the given long.
|
|
||||||
/// The long must not be negative.
|
|
||||||
/// </summary>
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Multiplies the current expansion by the given amount, which should
|
|
||||||
/// only be 2 or 5.
|
|
||||||
/// </summary>
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </summary>
|
|
||||||
internal void Shift (int amount)
|
|
||||||
{
|
|
||||||
decimalPoint += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes leading/trailing zeroes from the expansion.
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the value to a proper decimal string representation.
|
|
||||||
/// </summary>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
184
Documents/Request Framework.md
Normal file
184
Documents/Request Framework.md
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
Request Framework
|
||||||
|
=================
|
||||||
|
|
||||||
|
These are just some ponderings on the design. Most of what I was trying to accomplish
|
||||||
|
via my request framework has long since been covered by System.Threading.Tasks and
|
||||||
|
most of what I designed is not in the separate and forgotten Nuclex.Support.Transactions
|
||||||
|
library.
|
||||||
|
|
||||||
|
|
||||||
|
Ponderings
|
||||||
|
----------
|
||||||
|
|
||||||
|
The request framework should not require that .NET multithreading is used for
|
||||||
|
asynchronous requests.
|
||||||
|
|
||||||
|
Otherwise, it would prvent overlapped operations, 3rd party APIs (eg. used
|
||||||
|
via P/Invoke) from being able to use the request framework and possibly even
|
||||||
|
spawn duplicate implementations.
|
||||||
|
|
||||||
|
|
||||||
|
Design using interfaces
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
interface IWaitable {
|
||||||
|
|
||||||
|
/// <summary>Fired when the background process has finished</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the process is already finished when a client registers to this event,
|
||||||
|
/// the registered callback will be invoked synchronously right when the
|
||||||
|
/// registration takes place.
|
||||||
|
/// </remarks>
|
||||||
|
event EventHandler Finished;
|
||||||
|
|
||||||
|
/// <summary>Waits until the background process finishes</summary>
|
||||||
|
void Wait();
|
||||||
|
|
||||||
|
/// <summary>Waits until the background process finishes or a timeout occurs</summary>
|
||||||
|
/// <param name="timeoutMilliseconds">
|
||||||
|
/// Number of milliseconds after which to stop waiting and return immediately
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if the background process completed, false if the timeout was reached
|
||||||
|
/// </returns>
|
||||||
|
bool Wait(int timeoutMilliseconds);
|
||||||
|
|
||||||
|
/// <summary>Whether the background process has finished</summary>
|
||||||
|
bool Finished { get; }
|
||||||
|
|
||||||
|
// ?
|
||||||
|
/// <summary>Wait handle that can be used to wait for multiple waitables</summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Only use the WaitHandle to wait if you're running in a different thread than
|
||||||
|
/// the request, or you may deadlock. Libraries based on single-threaded
|
||||||
|
/// multitasking may employ concepts such as window message processing to achieve
|
||||||
|
/// parallelism which could be stalled by improper waiting using the WaitHandle
|
||||||
|
/// whereas the Wait() method typically has a suitable implementation compatible
|
||||||
|
/// with the request's requirements.
|
||||||
|
/// </remarks>
|
||||||
|
WaitHandle WaitHandle { get; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IThreadedWaitable : IWaitable {
|
||||||
|
|
||||||
|
WaitHandle WaitHandle { get; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRequest : IWaitable {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the background process to complete and re-throws the exception to
|
||||||
|
/// the caller when an error has occured
|
||||||
|
/// </summary>
|
||||||
|
void Join();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IRequest<ResultType> : IRequest {
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Waits for the background process to complete and re-throws the exception to
|
||||||
|
/// the caller when an error has occured
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The result of the background processing</returns>
|
||||||
|
new ResultType Join();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IThreadedRequest : IRequest, IThreadedWaitable { }
|
||||||
|
|
||||||
|
interface IThreadedRequest<ResultType> : IRequest<ResultType>, IThreadedRequest { }
|
||||||
|
|
||||||
|
|
||||||
|
Impossible implementation
|
||||||
|
-------------------------
|
||||||
|
|
||||||
|
class Request : IRequest {
|
||||||
|
|
||||||
|
event EventHandler Finished;
|
||||||
|
void Wait();
|
||||||
|
bool Wait(int timeoutMilliseconds);
|
||||||
|
bool Finished { get; }
|
||||||
|
void Join();
|
||||||
|
protected virtual void ReraiseExceptions() { }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Request<ResultType> : Request, IRequest<ResultType> {
|
||||||
|
|
||||||
|
new ResultType Join();
|
||||||
|
protected abstract ResultType GatherResults();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Do not provide: (see conflict in second version)
|
||||||
|
|
||||||
|
class ThreadedRequest : Request, IThreadedRequest {
|
||||||
|
|
||||||
|
WaitHandle WaitHandle { get; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThreadedRequest<ResultType> : ThreadedRequest, Request<ResultType> { }
|
||||||
|
|
||||||
|
// However, not providing these, the user would have to rewrite
|
||||||
|
// the complex threading routines everywhere he uses then. Bad.
|
||||||
|
|
||||||
|
|
||||||
|
Inelegant implementation
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
class Void {}
|
||||||
|
|
||||||
|
class Request<ResultType> : IRequest<ResultType> {
|
||||||
|
|
||||||
|
new ResultType Join();
|
||||||
|
protected abstract ResultType GatherResults();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class ThreadedRequest<ResultType> : Request<ResultType> { }
|
||||||
|
|
||||||
|
// However, not providing these, the user would have to rewrite
|
||||||
|
// the complex threading routines everywhere he uses then. Bad.
|
||||||
|
|
||||||
|
Maybe keeping threaded and non-threaded requests apart is a good thing?
|
||||||
|
|
||||||
|
IWaitable (without Finished event)
|
||||||
|
Waitable (Finished event)
|
||||||
|
Request
|
||||||
|
Request<Bla>
|
||||||
|
|
||||||
|
IWaitable (without Finished event)
|
||||||
|
ThreadedWaitable (AsyncFinished event)
|
||||||
|
ThreadedRequest
|
||||||
|
ThreadedRequest<Bla>
|
||||||
|
|
||||||
|
Or just dump the WaitHandle schmonder
|
||||||
|
|
||||||
|
Waitable (with virtual protected SyncRoot { get { return this; } })
|
||||||
|
Request
|
||||||
|
Request<Bla>
|
||||||
|
|
||||||
|
LazyWaitHandle
|
||||||
|
WaitHandle Get(Waitable waitable)
|
||||||
|
|
||||||
|
Or use policy classes (waithandle trouble)
|
||||||
|
|
||||||
|
Waitable
|
||||||
|
Request
|
||||||
|
Request<Bla>
|
||||||
|
|
||||||
|
RequestImpl<ThreadPolicy>
|
||||||
|
RequestImpl<Bla, ThreadPolicy>
|
||||||
|
|
||||||
|
LazyWaitHandle
|
||||||
|
WaitHandle Get(Waitable waitable)
|
||||||
|
WaitHandle Get(Waitable waitable, object syncRoot)
|
||||||
|
|
||||||
|
ThreadPolicy {
|
||||||
|
virtual void lock() {}
|
||||||
|
virtual void unlock() {}
|
||||||
|
}
|
@ -1,172 +0,0 @@
|
|||||||
The request framework should not require that .NET multithreading is used for
|
|
||||||
asynchronous requests.
|
|
||||||
|
|
||||||
Otherwise, it would prvent overlapped operations, 3rd party APIs (eg. used
|
|
||||||
via P/Invoke) from being able to use the request framework and possibly even
|
|
||||||
spawn duplicate implementations.
|
|
||||||
|
|
||||||
|
|
||||||
Design using interfaces:
|
|
||||||
|
|
||||||
interface IWaitable {
|
|
||||||
|
|
||||||
/// <summary>Fired when the background process has finished</summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the process is already finished when a client registers to this event,
|
|
||||||
/// the registered callback will be invoked synchronously right when the
|
|
||||||
/// registration takes place.
|
|
||||||
/// </remarks>
|
|
||||||
event EventHandler Finished;
|
|
||||||
|
|
||||||
/// <summary>Waits until the background process finishes</summary>
|
|
||||||
void Wait();
|
|
||||||
|
|
||||||
/// <summary>Waits until the background process finishes or a timeout occurs</summary>
|
|
||||||
/// <param name="timeoutMilliseconds">
|
|
||||||
/// Number of milliseconds after which to stop waiting and return immediately
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// True if the background process completed, false if the timeout was reached
|
|
||||||
/// </returns>
|
|
||||||
bool Wait(int timeoutMilliseconds);
|
|
||||||
|
|
||||||
/// <summary>Whether the background process has finished</summary>
|
|
||||||
bool Finished { get; }
|
|
||||||
|
|
||||||
// ?
|
|
||||||
/// <summary>Wait handle that can be used to wait for multiple waitables</summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Only use the WaitHandle to wait if you're running in a different thread than
|
|
||||||
/// the request, or you may deadlock. Libraries based on single-threaded
|
|
||||||
/// multitasking may employ concepts such as window message processing to achieve
|
|
||||||
/// parallelism which could be stalled by improper waiting using the WaitHandle
|
|
||||||
/// whereas the Wait() method typically has a suitable implementation compatible
|
|
||||||
/// with the request's requirements.
|
|
||||||
/// </remarks>
|
|
||||||
WaitHandle WaitHandle { get; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IThreadedWaitable : IWaitable {
|
|
||||||
|
|
||||||
WaitHandle WaitHandle { get; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRequest : IWaitable {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Waits for the background process to complete and re-throws the exception to
|
|
||||||
/// the caller when an error has occured
|
|
||||||
/// </summary>
|
|
||||||
void Join();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRequest<ResultType> : IRequest {
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Waits for the background process to complete and re-throws the exception to
|
|
||||||
/// the caller when an error has occured
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The result of the background processing</returns>
|
|
||||||
new ResultType Join();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IThreadedRequest : IRequest, IThreadedWaitable { }
|
|
||||||
|
|
||||||
interface IThreadedRequest<ResultType> : IRequest<ResultType>, IThreadedRequest { }
|
|
||||||
|
|
||||||
|
|
||||||
Impossible implementation:
|
|
||||||
|
|
||||||
class Request : IRequest {
|
|
||||||
|
|
||||||
event EventHandler Finished;
|
|
||||||
void Wait();
|
|
||||||
bool Wait(int timeoutMilliseconds);
|
|
||||||
bool Finished { get; }
|
|
||||||
void Join();
|
|
||||||
protected virtual void ReraiseExceptions() { }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class Request<ResultType> : Request, IRequest<ResultType> {
|
|
||||||
|
|
||||||
new ResultType Join();
|
|
||||||
protected abstract ResultType GatherResults();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Do not provide: (see conflict in second version)
|
|
||||||
|
|
||||||
class ThreadedRequest : Request, IThreadedRequest {
|
|
||||||
|
|
||||||
WaitHandle WaitHandle { get; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ThreadedRequest<ResultType> : ThreadedRequest, Request<ResultType> { }
|
|
||||||
|
|
||||||
// However, not providing these, the user would have to rewrite
|
|
||||||
// the complex threading routines everywhere he uses then. Bad.
|
|
||||||
|
|
||||||
Inelegant implementation:
|
|
||||||
|
|
||||||
class Void {}
|
|
||||||
|
|
||||||
class Request<ResultType> : IRequest<ResultType> {
|
|
||||||
|
|
||||||
new ResultType Join();
|
|
||||||
protected abstract ResultType GatherResults();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class ThreadedRequest<ResultType> : Request<ResultType> { }
|
|
||||||
|
|
||||||
// However, not providing these, the user would have to rewrite
|
|
||||||
// the complex threading routines everywhere he uses then. Bad.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Maybe keeping threaded and non-threaded requests apart is a good thing?
|
|
||||||
|
|
||||||
IWaitable (without Finished event)
|
|
||||||
Waitable (Finished event)
|
|
||||||
Request
|
|
||||||
Request<Bla>
|
|
||||||
|
|
||||||
IWaitable (without Finished event)
|
|
||||||
ThreadedWaitable (AsyncFinished event)
|
|
||||||
ThreadedRequest
|
|
||||||
ThreadedRequest<Bla>
|
|
||||||
|
|
||||||
|
|
||||||
Or just dump the WaitHandle schmonder
|
|
||||||
|
|
||||||
Waitable (with virtual protected SyncRoot { get { return this; } })
|
|
||||||
Request
|
|
||||||
Request<Bla>
|
|
||||||
|
|
||||||
LazyWaitHandle
|
|
||||||
WaitHandle Get(Waitable waitable)
|
|
||||||
|
|
||||||
|
|
||||||
Or use policy classes (waithandle trouble)
|
|
||||||
|
|
||||||
Waitable
|
|
||||||
Request
|
|
||||||
Request<Bla>
|
|
||||||
|
|
||||||
RequestImpl<ThreadPolicy>
|
|
||||||
RequestImpl<Bla, ThreadPolicy>
|
|
||||||
|
|
||||||
LazyWaitHandle
|
|
||||||
WaitHandle Get(Waitable waitable)
|
|
||||||
WaitHandle Get(Waitable waitable, object syncRoot)
|
|
||||||
|
|
||||||
ThreadPolicy {
|
|
||||||
virtual void lock() {}
|
|
||||||
virtual void unlock() {}
|
|
||||||
}
|
|
@ -1,5 +1,15 @@
|
|||||||
#if DEBUG
|
Shuffle Table Generator
|
||||||
|
=======================
|
||||||
|
|
||||||
|
The `LicenseKey` class converts a GUID (or any 128 bits of data) into a license key similar
|
||||||
|
to the ones used by Microsoft products (5 groups of 5 alphanumeric characters).
|
||||||
|
|
||||||
|
For additional obfuscation, bits can be mapped in a random order. That will offer some
|
||||||
|
protection when you have, for example, a running serial number. With shuffling, each
|
||||||
|
increment will result in likely several alphanumeric characters changing in seemingly
|
||||||
|
unpredictable ways (base-2 to base-36 conversion pretty much guarantees that any small
|
||||||
|
change affects many following alphanumeric "digits").
|
||||||
|
|
||||||
/// <summary>Generates a new random shuffle table</summary>
|
/// <summary>Generates a new random shuffle table</summary>
|
||||||
/// <param name="iterationCount">
|
/// <param name="iterationCount">
|
||||||
/// Number of iterations in which to randomize the shuffle table
|
/// Number of iterations in which to randomize the shuffle table
|
||||||
@ -23,6 +33,3 @@
|
|||||||
|
|
||||||
return shuffleTable;
|
return shuffleTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user