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,4 +1,14 @@
|
||||
#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>
|
||||
/// <param name="iterationCount">
|
||||
@ -23,6 +33,3 @@
|
||||
|
||||
return shuffleTable;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user