├── README.md ├── Compression └── Vcdiff │ ├── InstructionType.cs │ ├── VcdiffFormatException.cs │ ├── Instruction.cs │ ├── IOHelper.cs │ ├── AddressCache.cs │ └── CodeTable.cs ├── Linq ├── Extensions │ ├── DataProducerExt.cs │ ├── ListExt.cs │ ├── TypeExt.cs │ └── DataProducerExt.Conversion.cs ├── IFuture.cs ├── IProducerGrouping.cs ├── IOrderedDataProducer.cs ├── IDataProducer.cs ├── FutureProxy.cs ├── EditableLookup.LookupGrouping.cs ├── Future.cs ├── ProducerGrouping.cs ├── OrderedDataProducer.cs ├── ExpressionUtil.cs ├── DataProducer.cs ├── KeyValueTuple.cs └── EditableLookup.cs ├── Conversion ├── Endianness.cs ├── LittleEndianBitConverter.cs ├── BigEndianBitConverter.cs └── DoubleConverter.cs ├── DotNet20 ├── ExtensionAttribute.cs ├── IGrouping.cs ├── ILookup.cs └── Delegates.cs ├── .gitattributes ├── BufferAcquisitionException.cs ├── .gitignore ├── EventArgs.cs ├── IBufferManager.cs ├── Collections ├── Extensions │ ├── SmartEnumerableExt.cs │ ├── RangeBasedExt.cs │ ├── ComparerExt.cs │ └── DictionaryExt.cs ├── LinkedComparer.cs ├── ReverseComparer.cs ├── ComparisonComparer.cs ├── DictionaryByType.cs ├── RangeIterator.cs ├── SmartEnumerable.cs ├── ProjectionComparer.cs └── ProjectionEqualityComparer.cs ├── Threading ├── LockOrderException.cs ├── LockTimeoutException.cs ├── LockToken.cs ├── Delegates.cs ├── ThreadPoolWorkItem.cs ├── OrderedLock.cs ├── SyncLock.cs └── ThreadController.cs ├── CachedBuffer.cs ├── IBuffer.cs ├── Extensions ├── ReferenceExt.cs └── TimeRelated │ ├── TimeSpanBasedExt.cs │ └── DateTimeBasedExt.cs ├── Xml └── Linq │ └── Extensions │ └── ObjectExt.cs ├── NullOp.cs ├── GenericMath.cs ├── Properties └── AssemblyInfo.cs ├── StaticRandom.cs ├── IO ├── StringWriterWithEncoding.cs └── LineReader.cs ├── Checksum └── Adler32.cs ├── PartialComparer.cs ├── Reflection └── PropertyCopy.cs ├── NonNullable.cs ├── ApplicationChooser.cs └── MiscUtil.csproj /README.md: -------------------------------------------------------------------------------- 1 | # MiscUtil 2 | From Jon Skeet http://www.yoda.arachsys.com/csharp/miscutil/ 3 | -------------------------------------------------------------------------------- /Compression/Vcdiff/InstructionType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Compression.Vcdiff 3 | { 4 | /// 5 | /// Enumeration of the different instruction types. 6 | /// 7 | internal enum InstructionType : byte 8 | { 9 | NoOp=0, 10 | Add=1, 11 | Run=2, 12 | Copy=3 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Linq/Extensions/DataProducerExt.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Linq.Extensions 3 | { 4 | /// 5 | /// Extensions on IDataProducer 6 | /// 7 | public static partial class DataProducerExt 8 | { 9 | // note: contents are in partial classes broken 10 | // down by function - i.e. DataProducerExt.Grouping.cs 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Conversion/Endianness.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Conversion 3 | { 4 | /// 5 | /// Endianness of a converter 6 | /// 7 | public enum Endianness 8 | { 9 | /// 10 | /// Little endian - least significant byte first 11 | /// 12 | LittleEndian, 13 | /// 14 | /// Big endian - most significant byte first 15 | /// 16 | BigEndian 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Linq/IFuture.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Linq 3 | { 4 | /// 5 | /// Class representing a value which will be available some time in the future. 6 | /// 7 | public interface IFuture 8 | { 9 | /// 10 | /// Retrieves the value, if available, and throws InvalidOperationException 11 | /// otherwise. 12 | /// 13 | T Value { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DotNet20/ExtensionAttribute.cs: -------------------------------------------------------------------------------- 1 | #if !DOTNET35 2 | using System; 3 | 4 | namespace System.Runtime.CompilerServices 5 | { 6 | /// 7 | /// Attribute used by the compiler to create extension methods under .NET 2.0. 8 | /// 9 | [AttributeUsageAttribute(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)] 10 | public class ExtensionAttribute : Attribute 11 | { 12 | } 13 | } 14 | #endif -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /Compression/Vcdiff/VcdiffFormatException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Compression.Vcdiff 4 | { 5 | /// 6 | /// Summary description for VcdiffFormatException. 7 | /// 8 | [Serializable()] 9 | public class VcdiffFormatException : Exception 10 | { 11 | internal VcdiffFormatException(string message) : base (message) 12 | { 13 | } 14 | 15 | internal VcdiffFormatException(string message, Exception inner) : base(message, inner) 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Linq/IProducerGrouping.cs: -------------------------------------------------------------------------------- 1 | namespace MiscUtil.Linq 2 | { 3 | /// 4 | /// IProducerGrouping is to IDataProducer as IGrouping is to IEnumerable: 5 | /// it's basically a data producer with a key. It's used by the GroupBy 6 | /// operator. 7 | /// 8 | public interface IProducerGrouping : IDataProducer 9 | { 10 | /// 11 | /// The key for this grouping. 12 | /// 13 | TKey Key { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DotNet20/IGrouping.cs: -------------------------------------------------------------------------------- 1 | #if !DOTNET35 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace System.Linq 6 | { 7 | /// 8 | /// LINQ interface used to represent groupings - each grouping has a key, 9 | /// and represents a sequence of elements. 10 | /// 11 | public interface IGrouping : IEnumerable, IEnumerable 12 | { 13 | /// 14 | /// Key for this group 15 | /// 16 | TKey Key { get; } 17 | } 18 | } 19 | #endif -------------------------------------------------------------------------------- /BufferAcquisitionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil 4 | { 5 | /// 6 | /// Exception thrown to indicate that a buffer of the 7 | /// desired size cannot be acquired. 8 | /// 9 | public class BufferAcquisitionException : Exception 10 | { 11 | /// 12 | /// Creates an instance of this class with the given message. 13 | /// 14 | public BufferAcquisitionException(string message) 15 | : base(message) 16 | { 17 | // No-op 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Compression/Vcdiff/Instruction.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Compression.Vcdiff 3 | { 4 | /// 5 | /// Contains the information for a single instruction 6 | /// 7 | internal struct Instruction 8 | { 9 | readonly InstructionType type; 10 | internal InstructionType Type 11 | { 12 | get { return type; } 13 | } 14 | 15 | readonly byte size; 16 | internal byte Size 17 | { 18 | get { return size; } 19 | } 20 | 21 | readonly byte mode; 22 | internal byte Mode 23 | { 24 | get { return mode; } 25 | } 26 | 27 | internal Instruction(InstructionType type, byte size, byte mode) 28 | { 29 | this.type = type; 30 | this.size = size; 31 | this.mode = mode; 32 | } 33 | 34 | 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /EventArgs.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil 3 | { 4 | /// 5 | /// Generic event argument taking a single value 6 | /// 7 | public class EventArgs : System.EventArgs 8 | { 9 | readonly T value; 10 | 11 | /// 12 | /// The typed value of the EventArgs<T> 13 | /// 14 | public T Value 15 | { 16 | get { return value; } 17 | } 18 | /// 19 | /// Creates a new EventArgs<T> with the specified value. 20 | /// 21 | /// The Value of the EventArgs<T> instance. 22 | public EventArgs(T value) 23 | { 24 | this.value = value; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /IBufferManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil 4 | { 5 | /// 6 | /// Interface for classes which manage instances of 7 | /// IBuffer. 8 | /// 9 | public interface IBufferManager 10 | { 11 | /// 12 | /// Returns a buffer of the given size or greater. 13 | /// 14 | /// The minimum size of buffer to return 15 | /// This manager is unable 16 | /// to return a buffer of the appropriate size 17 | /// minimumSize is less than 18 | /// or equal to 0 19 | IBuffer GetBuffer(int minimumSize); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Linq/IOrderedDataProducer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiscUtil.Linq 4 | { 5 | /// 6 | /// Ordered variant of IDataProducer; note that generally 7 | /// this will force data to be buffered until the sequence 8 | /// is complete. 9 | /// 10 | /// 11 | public interface IOrderedDataProducer : IDataProducer 12 | { 13 | /// 14 | /// The unlerlying producer that can push data 15 | /// 16 | IDataProducer BaseProducer { get; } 17 | /// 18 | /// The comparer used to order the sequence (once complete) 19 | /// 20 | IComparer Comparer { get; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Collections/Extensions/SmartEnumerableExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace MiscUtil.Collections.Extensions 7 | { 8 | /// 9 | /// Wrapper methods for SmartEnumerable[T]. 10 | /// 11 | public static class SmartEnumerableExt 12 | { 13 | /// 14 | /// Extension method to make life easier. 15 | /// 16 | /// Type of enumerable 17 | /// Source enumerable 18 | /// A new SmartEnumerable of the appropriate type 19 | public static SmartEnumerable AsSmartEnumerable(this IEnumerable source) 20 | { 21 | return new SmartEnumerable(source); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /DotNet20/ILookup.cs: -------------------------------------------------------------------------------- 1 | #if !DOTNET35 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace System.Linq 6 | { 7 | /// 8 | /// LINQ interface representing a lookup. This is like a dictionary, but 9 | /// each key maps to a sequence of values. 10 | /// 11 | public interface ILookup : IEnumerable>, 12 | IEnumerable 13 | { 14 | /// 15 | /// Returns whether or not the lookup contains the specified key 16 | /// 17 | bool Contains(TKey key); 18 | /// 19 | /// Returns the number of keys in this lookup 20 | /// 21 | int Count { get; } 22 | /// 23 | /// Returns the sequence of elements associated with the given key 24 | /// 25 | IEnumerable this[TKey key] { get; } 26 | } 27 | } 28 | #endif -------------------------------------------------------------------------------- /Threading/LockOrderException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Threading 4 | { 5 | /// 6 | /// Exception thrown when a Lock method on the SyncLock class times out. 7 | /// 8 | public class LockOrderException : Exception 9 | { 10 | /// 11 | /// Constructs an instance with the specified message. 12 | /// 13 | /// The message for the exception 14 | internal LockOrderException (string message) : base (message) 15 | { 16 | } 17 | 18 | /// 19 | /// Constructs an instance by formatting the specified message with 20 | /// the given parameters. 21 | /// 22 | /// The message, which will be formatted with the parameters. 23 | /// The parameters to use for formatting. 24 | internal LockOrderException (string format, params object[] args) 25 | : this (string.Format(format, args)) 26 | { 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Threading/LockTimeoutException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Threading 4 | { 5 | /// 6 | /// Exception thrown when a Lock method on the SyncLock class times out. 7 | /// 8 | public class LockTimeoutException : Exception 9 | { 10 | /// 11 | /// Constructs an instance with the specified message. 12 | /// 13 | /// The message for the exception 14 | internal LockTimeoutException (string message) : base (message) 15 | { 16 | } 17 | 18 | /// 19 | /// Constructs an instance by formatting the specified message with 20 | /// the given parameters. 21 | /// 22 | /// The message, which will be formatted with the parameters. 23 | /// The parameters to use for formatting. 24 | internal LockTimeoutException (string format, params object[] args) 25 | : this (string.Format(format, args)) 26 | { 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /CachedBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil 4 | { 5 | /// 6 | /// Type of buffer returned by CachingBufferManager. 7 | /// 8 | class CachedBuffer : IBuffer 9 | { 10 | readonly byte[] data; 11 | volatile bool available; 12 | readonly bool clearOnDispose; 13 | 14 | internal CachedBuffer(int size, bool clearOnDispose) 15 | { 16 | data = new byte[size]; 17 | this.clearOnDispose = clearOnDispose; 18 | } 19 | 20 | internal bool Available 21 | { 22 | get { return available; } 23 | set { available = value; } 24 | } 25 | 26 | public byte[] Bytes 27 | { 28 | get { return data; } 29 | } 30 | 31 | public void Dispose() 32 | { 33 | if (clearOnDispose) 34 | { 35 | Array.Clear(data, 0, data.Length); 36 | } 37 | available = true; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /IBuffer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil 4 | { 5 | /// 6 | /// Interface encapsulating a byte array which may be managed 7 | /// by an IBufferManager implementation. When the buffer is 8 | /// disposed, some implementations of this interface may 9 | /// return the buffer to the IBufferManager which created 10 | /// it. Note that an IBuffer *must* always be disposed after use, 11 | /// or some implementations may leak memory. The buffer must 12 | /// not be used after being disposed, likewise the byte array must 13 | /// not be used after the buffer is disposed. 14 | /// 15 | public interface IBuffer : IDisposable 16 | { 17 | /// 18 | /// Returns the byte array encapsulated by this buffer. 19 | /// Note that depending on the buffer manager providing 20 | /// the buffer, the array may or may not be cleared (i.e. 21 | /// with every byte 0) to start with. 22 | /// 23 | byte[] Bytes { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Threading/LockToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Threading 4 | { 5 | /// 6 | /// A lock token returned by a Lock method call on a SyncLock. 7 | /// This effectively holds the lock until it is disposed - a 8 | /// slight violation of the IDisposable contract, but it makes 9 | /// for easy use of the SyncLock system. This type itself 10 | /// is not thread-safe - LockTokens should not be shared between 11 | /// threads. 12 | /// 13 | public struct LockToken : IDisposable 14 | { 15 | /// 16 | /// The lock this token has been created by. 17 | /// 18 | SyncLock parent; 19 | 20 | /// 21 | /// Constructs a new lock token for the specified lock. 22 | /// 23 | /// The internal monitor used for locking. 24 | internal LockToken (SyncLock parent) 25 | { 26 | this.parent = parent; 27 | } 28 | 29 | /// 30 | /// Releases the lock. Subsequent calls to this method do nothing. 31 | /// 32 | public void Dispose() 33 | { 34 | if (parent==null) 35 | { 36 | return; 37 | } 38 | parent.Unlock(); 39 | parent = null; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Extensions/ReferenceExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Extensions 4 | { 5 | /// 6 | /// Extension methods on all reference types. 7 | /// 8 | public static class ObjectExt 9 | { 10 | /// 11 | /// Throws an ArgumentNullException if the given data item is null. 12 | /// 13 | /// The item to check for nullity. 14 | /// The name to use when throwing an exception, if necessary 15 | public static void ThrowIfNull(this T data, string name) where T : class 16 | { 17 | if (data == null) 18 | { 19 | throw new ArgumentNullException(name); 20 | } 21 | } 22 | 23 | /// 24 | /// Throws an ArgumentNullException if the given data item is null. 25 | /// No parameter name is specified. 26 | /// 27 | /// The item to check for nullity. 28 | public static void ThrowIfNull(this T data) where T : class 29 | { 30 | if (data == null) 31 | { 32 | throw new ArgumentNullException(); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Collections/LinkedComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using MiscUtil.Extensions; 3 | namespace MiscUtil.Collections 4 | { 5 | /// 6 | /// Comparer to daisy-chain two existing comparers and 7 | /// apply in sequence (i.e. sort by x then y) 8 | /// 9 | /// 10 | internal class LinkedComparer : IComparer 11 | { 12 | readonly IComparer primary, secondary; 13 | /// 14 | /// Create a new LinkedComparer 15 | /// 16 | /// The first comparison to use 17 | /// The next level of comparison if the primary returns 0 (equivalent) 18 | public LinkedComparer( 19 | IComparer primary, 20 | IComparer secondary) 21 | { 22 | primary.ThrowIfNull("primary"); 23 | secondary.ThrowIfNull("secondary"); 24 | 25 | this.primary = primary; 26 | this.secondary = secondary; 27 | } 28 | 29 | int IComparer.Compare(T x, T y) 30 | { 31 | int result = primary.Compare(x, y); 32 | return result == 0 ? secondary.Compare(x, y) : result; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Linq/IDataProducer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Linq 4 | { 5 | /// 6 | /// Interface to be implemented by sequences of data which have a "push" 7 | /// nature rather than "pull" - instead of the IEnumerable model of 8 | /// the client pulling data from the sequence, here the client registers 9 | /// an interest in the data being produced, and in the sequence reaching 10 | /// an end. The data producer than produces data whenever it wishes, and the 11 | /// clients can react. This allows other actions to occur between items being 12 | /// pulled, as well as multiple clients for the same sequence of data. 13 | /// 14 | public interface IDataProducer 15 | { 16 | /// 17 | /// Event which is raised when an item of data is produced. 18 | /// This will not be raised after EndOfData has been raised. 19 | /// The parameter for the event is the 20 | /// 21 | event Action DataProduced; 22 | /// 23 | /// Event which is raised when the sequence has finished being 24 | /// produced. This will be raised exactly once, and after all 25 | /// DataProduced events (if any) have been raised. 26 | /// 27 | event Action EndOfData; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Linq/FutureProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Linq 4 | { 5 | /// 6 | /// Implementation of IFuture which retrieves it value from a delegate. 7 | /// This is primarily used for FromFuture, which will transform another 8 | /// Future's value on demand. 9 | /// 10 | public class FutureProxy : IFuture 11 | { 12 | readonly Func fetcher; 13 | 14 | /// 15 | /// Creates a new FutureProxy using the given method 16 | /// to obtain the value when needed 17 | /// 18 | public FutureProxy(Func fetcher) 19 | { 20 | this.fetcher = fetcher; 21 | } 22 | 23 | /// 24 | /// Creates a new FutureProxy from an existing future using 25 | /// the supplied transformation to obtain the value as needed 26 | /// 27 | public static FutureProxy FromFuture(IFuture future, Func projection) 28 | { 29 | return new FutureProxy(() => projection(future.Value)); 30 | } 31 | /// 32 | /// Returns the value of the Future 33 | /// 34 | public T Value 35 | { 36 | get 37 | { 38 | return fetcher(); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Collections/Extensions/RangeBasedExt.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Collections.Extensions 3 | { 4 | /// 5 | /// Extension methods to do with ranges. 6 | /// 7 | public static class RangeBasedExt 8 | { 9 | /// 10 | /// Creates an inclusive range between two values. The default comparer is used 11 | /// to compare values. 12 | /// 13 | /// Type of the values 14 | /// Start of range. 15 | /// End of range. 16 | /// An inclusive range between the start point and the end point. 17 | public static Range To(this T start, T end) 18 | { 19 | return new Range(start, end); 20 | } 21 | 22 | /// 23 | /// Returns a RangeIterator over the given range, where the stepping function 24 | /// is to step by the given number of characters. 25 | /// 26 | /// The range to create an iterator for 27 | /// How many characters to step each time 28 | /// A RangeIterator with a suitable stepping function 29 | public static RangeIterator StepChar(this Range range, int step) 30 | { 31 | return range.Step(c => (char)(c + step)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Collections/ReverseComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | using MiscUtil.Extensions; 4 | 5 | namespace MiscUtil.Collections 6 | { 7 | /// 8 | /// Implementation of IComparer{T} based on another one; 9 | /// this simply reverses the original comparison. 10 | /// 11 | /// 12 | public sealed class ReverseComparer : IComparer 13 | { 14 | readonly IComparer originalComparer; 15 | 16 | /// 17 | /// Returns the original comparer; this can be useful to avoid multiple 18 | /// reversals. 19 | /// 20 | public IComparer OriginalComparer 21 | { 22 | get { return originalComparer; } 23 | } 24 | 25 | /// 26 | /// Creates a new reversing comparer. 27 | /// 28 | /// The original comparer to use for comparisons. 29 | public ReverseComparer(IComparer original) 30 | { 31 | original.ThrowIfNull("original"); 32 | this.originalComparer = original; 33 | } 34 | 35 | /// 36 | /// Returns the result of comparing the specified values using the original 37 | /// comparer, but reversing the order of comparison. 38 | /// 39 | public int Compare(T x, T y) 40 | { 41 | return originalComparer.Compare(y, x); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Linq/EditableLookup.LookupGrouping.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace MiscUtil.Linq 5 | { 6 | partial class EditableLookup 7 | { 8 | internal sealed class LookupGrouping : IGrouping 9 | { 10 | private readonly TKey key; 11 | private List items = new List(); 12 | public TKey Key { get { return key; } } 13 | public LookupGrouping(TKey key) 14 | { 15 | this.key = key; 16 | } 17 | public int Count 18 | { 19 | get { return items.Count; } 20 | } 21 | public void Add(TElement item) 22 | { 23 | items.Add(item); 24 | } 25 | public bool Contains(TElement item) 26 | { 27 | return items.Contains(item); 28 | } 29 | public bool Remove(TElement item) 30 | { 31 | return items.Remove(item); 32 | } 33 | public void TrimExcess() 34 | { 35 | items.TrimExcess(); 36 | } 37 | 38 | public IEnumerator GetEnumerator() 39 | { 40 | return items.GetEnumerator(); 41 | } 42 | 43 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 44 | { 45 | return GetEnumerator(); 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Xml/Linq/Extensions/ObjectExt.cs: -------------------------------------------------------------------------------- 1 | #if DOTNET35 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Xml.Linq; 5 | 6 | namespace MiscUtil.Xml.Linq.Extensions 7 | { 8 | /// 9 | /// Extensions to System.Object for LINQ to XML purposes. 10 | /// 11 | public static class ObjectExt 12 | { 13 | /// 14 | /// Returns the properties of the given object as XElements. 15 | /// Properties with null values are still returned, but as empty 16 | /// elements. Underscores in property names are replaces with hyphens. 17 | /// 18 | public static IEnumerable AsXElements(this object source) 19 | { 20 | foreach (PropertyInfo prop in source.GetType().GetProperties()) 21 | { 22 | object value = prop.GetValue(source, null); 23 | yield return new XElement(prop.Name.Replace("_", "-"), value); 24 | } 25 | } 26 | 27 | /// 28 | /// Returns the properties of the given object as XElements. 29 | /// Properties with null values are returned as empty attributes. 30 | /// Underscores in property names are replaces with hyphens. 31 | /// 32 | public static IEnumerable AsXAttributes(this object source) 33 | { 34 | foreach (PropertyInfo prop in source.GetType().GetProperties()) 35 | { 36 | object value = prop.GetValue(source, null); 37 | yield return new XAttribute(prop.Name.Replace("_", "-"), value ?? ""); 38 | } 39 | } 40 | } 41 | } 42 | #endif -------------------------------------------------------------------------------- /Linq/Future.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Linq 4 | { 5 | /// 6 | /// Poor-man's version of a Future. This wraps a result which *will* be 7 | /// available in the future. It's up to the caller/provider to make sure 8 | /// that the value has been specified by the time it's requested. 9 | /// 10 | public class Future : IFuture 11 | { 12 | T value; 13 | bool valueSet = false; 14 | /// 15 | /// Returns the value of the future, once it has been set 16 | /// 17 | /// If the value is not yet available 18 | public T Value 19 | { 20 | get 21 | { 22 | if (!valueSet) 23 | { 24 | throw new InvalidOperationException("No value has been set yet"); 25 | } 26 | return value; 27 | } 28 | set 29 | { 30 | if (valueSet) 31 | { 32 | throw new InvalidOperationException("Value has already been set"); 33 | } 34 | valueSet = true; 35 | this.value = value; 36 | } 37 | } 38 | /// 39 | /// Returns a string representation of the value if available, null otherwise 40 | /// 41 | /// A string representation of the value if available, null otherwise 42 | public override string ToString() 43 | { 44 | return valueSet ? Convert.ToString(value) : null; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Collections/Extensions/ComparerExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MiscUtil.Collections.Extensions 5 | { 6 | /// 7 | /// Extensions to IComparer 8 | /// 9 | public static class ComparerExt 10 | { 11 | /// 12 | /// Reverses the original comparer; if it was already a reverse comparer, 13 | /// the previous version was reversed (rather than reversing twice). 14 | /// In other words, for any comparer X, X==X.Reverse().Reverse(). 15 | /// 16 | public static IComparer Reverse(this IComparer original) 17 | { 18 | ReverseComparer originalAsReverse = original as ReverseComparer; 19 | if (originalAsReverse != null) 20 | { 21 | return originalAsReverse.OriginalComparer; 22 | } 23 | return new ReverseComparer(original); 24 | } 25 | 26 | /// 27 | /// Combines a comparer with a second comparer to implement composite sort 28 | /// behaviour. 29 | /// 30 | public static IComparer ThenBy(this IComparer firstComparer, IComparer secondComparer) 31 | { 32 | return new LinkedComparer(firstComparer, secondComparer); 33 | } 34 | 35 | /// 36 | /// Combines a comparer with a projection to implement composite sort behaviour. 37 | /// 38 | public static IComparer ThenBy(this IComparer firstComparer, Func projection) 39 | { 40 | return new LinkedComparer(firstComparer, new ProjectionComparer(projection)); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Compression/Vcdiff/IOHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace MiscUtil.Compression.Vcdiff 5 | { 6 | /// 7 | /// A few IO routines to make life easier. Most are basically available 8 | /// in EndianBinaryReader, but having them separately here makes VcdiffDecoder 9 | /// more easily movable to other places - and no endianness issues are involved in 10 | /// the first place. 11 | /// 12 | internal static class IOHelper 13 | { 14 | internal static byte[] CheckedReadBytes(Stream stream, int size) 15 | { 16 | byte[] ret = new byte[size]; 17 | int index=0; 18 | while (index < size) 19 | { 20 | int read = stream.Read(ret, index, size-index); 21 | if (read==0) 22 | { 23 | throw new EndOfStreamException 24 | (String.Format("End of stream reached with {0} byte{1} left to read.", size-index, 25 | size-index==1 ? "s" : "")); 26 | } 27 | index += read; 28 | } 29 | return ret; 30 | } 31 | 32 | internal static byte CheckedReadByte (Stream stream) 33 | { 34 | int b = stream.ReadByte(); 35 | if (b==-1) 36 | { 37 | throw new IOException ("Expected to be able to read a byte."); 38 | } 39 | return (byte)b; 40 | } 41 | 42 | internal static int ReadBigEndian7BitEncodedInt(Stream stream) 43 | { 44 | int ret=0; 45 | for (int i=0; i < 5; i++) 46 | { 47 | int b = stream.ReadByte(); 48 | if (b==-1) 49 | { 50 | throw new EndOfStreamException(); 51 | } 52 | ret = (ret << 7) | (b&0x7f); 53 | if ((b & 0x80) == 0) 54 | { 55 | return ret; 56 | } 57 | } 58 | // Still haven't seen a byte with the high bit unset? Dodgy data. 59 | throw new IOException("Invalid 7-bit encoded integer in stream."); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /NullOp.cs: -------------------------------------------------------------------------------- 1 | #if DOTNET35 2 | namespace MiscUtil 3 | { 4 | interface INullOp 5 | { 6 | bool HasValue(T value); 7 | bool AddIfNotNull(ref T accumulator, T value); 8 | } 9 | sealed class StructNullOp 10 | : INullOp, INullOp 11 | where T : struct 12 | { 13 | public bool HasValue(T value) 14 | { 15 | return true; 16 | } 17 | public bool AddIfNotNull(ref T accumulator, T value) 18 | { 19 | accumulator = Operator.Add(accumulator, value); 20 | return true; 21 | } 22 | public bool HasValue(T? value) 23 | { 24 | return value.HasValue; 25 | } 26 | public bool AddIfNotNull(ref T? accumulator, T? value) 27 | { 28 | if (value.HasValue) 29 | { 30 | accumulator = accumulator.HasValue ? 31 | Operator.Add( 32 | accumulator.GetValueOrDefault(), 33 | value.GetValueOrDefault()) 34 | : value; 35 | return true; 36 | } 37 | return false; 38 | } 39 | } 40 | sealed class ClassNullOp 41 | : INullOp 42 | where T : class 43 | { 44 | public bool HasValue(T value) 45 | { 46 | return value != null; 47 | } 48 | public bool AddIfNotNull(ref T accumulator, T value) 49 | { 50 | if (value != null) 51 | { 52 | accumulator = accumulator == null ? 53 | value : Operator.Add(accumulator, value); 54 | return true; 55 | } 56 | return false; 57 | } 58 | } 59 | } 60 | #endif -------------------------------------------------------------------------------- /DotNet20/Delegates.cs: -------------------------------------------------------------------------------- 1 | #if !DOTNET35 2 | namespace System 3 | { 4 | /// 5 | /// Delegate taking no parameters and returning no value. 6 | /// 7 | public delegate void Action(); 8 | /// 9 | /// Generic delegate taking two parameters and returning no value. 10 | /// 11 | public delegate void Action(T1 arg1, T2 arg2); 12 | /// 13 | /// Generic delegate taking three parameters and returning no value. 14 | /// 15 | public delegate void Action(T1 arg1, T2 arg2, T3 arg3); 16 | /// 17 | /// Generic delegate taking four parameters and returning no value. 18 | /// 19 | public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); 20 | 21 | /// 22 | /// Generic delegate taking no parameters and returning a value of the specified type. 23 | /// 24 | public delegate TResult Func(); 25 | /// 26 | /// Generic delegate taking one parameter and returning a value of the specified type. 27 | /// 28 | public delegate TResult Func(T arg); 29 | /// 30 | /// Generic delegate taking two parameters and returning a value of the specified type. 31 | /// 32 | public delegate TResult Func(T1 arg1, T2 arg2); 33 | /// 34 | /// Generic delegate taking three parameters and returning a value of the specified type. 35 | /// 36 | public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); 37 | /// 38 | /// Generic delegate taking four parameters and returning a value of the specified type. 39 | /// 40 | public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); 41 | } 42 | #endif -------------------------------------------------------------------------------- /Compression/Vcdiff/AddressCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace MiscUtil.Compression.Vcdiff 5 | { 6 | /// 7 | /// Cache used for encoding/decoding addresses. 8 | /// 9 | internal sealed class AddressCache 10 | { 11 | const byte SelfMode = 0; 12 | const byte HereMode = 1; 13 | 14 | int nearSize; 15 | int sameSize; 16 | int[] near; 17 | int nextNearSlot; 18 | int[] same; 19 | 20 | Stream addressStream; 21 | 22 | internal AddressCache(int nearSize, int sameSize) 23 | { 24 | this.nearSize = nearSize; 25 | this.sameSize = sameSize; 26 | near = new int[nearSize]; 27 | same = new int[sameSize*256]; 28 | } 29 | 30 | internal void Reset(byte[] addresses) 31 | { 32 | nextNearSlot = 0; 33 | Array.Clear(near, 0, near.Length); 34 | Array.Clear(same, 0, same.Length); 35 | 36 | addressStream = new MemoryStream(addresses, false); 37 | } 38 | 39 | internal int DecodeAddress (int here, byte mode) 40 | { 41 | int ret; 42 | if (mode==SelfMode) 43 | { 44 | ret = IOHelper.ReadBigEndian7BitEncodedInt(addressStream); 45 | } 46 | else if (mode==HereMode) 47 | { 48 | ret = here - IOHelper.ReadBigEndian7BitEncodedInt(addressStream); 49 | } 50 | else if (mode-2 < nearSize) // Near cache 51 | { 52 | ret = near[mode-2] + IOHelper.ReadBigEndian7BitEncodedInt(addressStream); 53 | } 54 | else // Same cache 55 | { 56 | int m = mode-(2+nearSize); 57 | ret = same[(m*256)+IOHelper.CheckedReadByte(addressStream)]; 58 | } 59 | 60 | Update (ret); 61 | return ret; 62 | } 63 | 64 | void Update (int address) 65 | { 66 | if (nearSize > 0) 67 | { 68 | near[nextNearSlot] = address; 69 | nextNearSlot=(nextNearSlot+1)%nearSize; 70 | } 71 | if (sameSize > 0) 72 | { 73 | same[address%(sameSize*256)] = address; 74 | } 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /GenericMath.cs: -------------------------------------------------------------------------------- 1 | #if DOTNET35 2 | namespace MiscUtil 3 | { 4 | /// 5 | /// Generic math equivalents of System.Math. 6 | /// (Calling this just Math makes far too much mess.) 7 | /// 8 | public static class GenericMath 9 | { 10 | /// 11 | /// Returns the absolute value of a specified number. 12 | /// 13 | /// Type to calculate with 14 | /// Input to return the absolute value of. 15 | /// The input value if it is greater than or equal to the default value of T, 16 | /// or the negated input value otherwise 17 | public static T Abs(T input) 18 | { 19 | return Operator.GreaterThanOrEqual(input, default(T)) 20 | ? input 21 | : Operator.Negate(input); 22 | } 23 | 24 | /// 25 | /// Returns whether or not two inputs are "close" to each other with respect to a given delta. 26 | /// 27 | /// 28 | /// This implementation currently does no overflow checking - if (input1-input2) overflows, it 29 | /// could yield the wrong result. 30 | /// 31 | /// Type to calculate with 32 | /// First input value 33 | /// Second input value 34 | /// Permitted range (exclusive) 35 | /// True if Abs(input1-input2) is less than or equal to delta; false otherwise. 36 | public static bool WithinDelta(T input1, T input2, T delta) 37 | { 38 | return Operator.LessThanOrEqual(Abs(Operator.Subtract(input1, input2)), delta); 39 | } 40 | } 41 | } 42 | #endif 43 | -------------------------------------------------------------------------------- /Linq/Extensions/ListExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MiscUtil.Collections; 4 | using MiscUtil.Collections.Extensions; 5 | namespace MiscUtil.Linq.Extensions 6 | { 7 | /// 8 | /// Provides extension methods to List<T> 9 | /// 10 | public static class ListExt 11 | { 12 | /// 13 | /// Sorts the elements in the entire System.Collections.Generic.List{T} using 14 | /// a projection. 15 | /// 16 | /// Data source 17 | /// The projection to use to obtain values for comparison 18 | /// The comparer to use to compare projected values (on null to use the default comparer) 19 | /// Should the list be sorted ascending or descending? 20 | public static void Sort(this List source, Func selector, IComparer comparer, bool descending) 21 | { 22 | if (source == null) throw new ArgumentNullException("source"); 23 | if (comparer == null) comparer = Comparer.Default; 24 | IComparer itemComparer = new ProjectionComparer(selector, comparer); 25 | if(descending) itemComparer = itemComparer.Reverse(); 26 | source.Sort(itemComparer); 27 | } 28 | 29 | /// 30 | /// Sorts the elements in the entire System.Collections.Generic.List{T} using 31 | /// a projection. 32 | /// 33 | /// Data source 34 | /// The projection to use to obtain values for comparison 35 | public static void Sort(this List source, Func selector) 36 | { 37 | Sort(source, selector, null, false); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Linq/ProducerGrouping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Linq 4 | { 5 | /// 6 | /// Simple implementation of IProducerGrouping which proxies to an existing 7 | /// IDataProducer. 8 | /// 9 | public class ProducerGrouping : IProducerGrouping 10 | { 11 | readonly IDataProducer source; 12 | readonly TKey key; 13 | /// 14 | /// Event which is raised when an item of data is produced. 15 | /// This will not be raised after EndOfData has been raised. 16 | /// The parameter for the event is the 17 | /// 18 | /// 19 | public event Action DataProduced 20 | { 21 | add { source.DataProduced += value; } 22 | remove { source.DataProduced -= value; } 23 | } 24 | 25 | /// 26 | /// Event which is raised when the sequence has finished being 27 | /// produced. This will be raised exactly once, and after all 28 | /// DataProduced events (if any) have been raised. 29 | /// 30 | /// 31 | public event Action EndOfData 32 | { 33 | add { source.EndOfData += value; } 34 | remove { source.EndOfData -= value; } 35 | } 36 | 37 | /// 38 | /// The key for this grouping. 39 | /// 40 | public TKey Key 41 | { 42 | get { return key; } 43 | } 44 | 45 | /// 46 | /// Constructs a new grouping with the given key 47 | /// 48 | public ProducerGrouping(TKey key, IDataProducer source) 49 | { 50 | this.key = key; 51 | this.source = source; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Collections/ComparisonComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MiscUtil.Collections 5 | { 6 | /// 7 | /// Utility to build an IComparer implementation from a Comparison delegate, 8 | /// and a static method to do the reverse. 9 | /// 10 | public sealed class ComparisonComparer : IComparer 11 | { 12 | readonly Comparison comparison; 13 | 14 | /// 15 | /// Creates a new instance which will proxy to the given Comparison 16 | /// delegate when called. 17 | /// 18 | /// Comparison delegate to proxy to. Must not be null. 19 | public ComparisonComparer(Comparison comparison) 20 | { 21 | if (comparison == null) 22 | { 23 | throw new ArgumentNullException("comparison"); 24 | } 25 | this.comparison = comparison; 26 | } 27 | 28 | /// 29 | /// Implementation of IComparer.Compare which simply proxies 30 | /// to the originally specified Comparison delegate. 31 | /// 32 | public int Compare(T x, T y) 33 | { 34 | return comparison(x, y); 35 | } 36 | 37 | /// 38 | /// Creates a Comparison delegate from the given Comparer. 39 | /// 40 | /// Comparer to use when the returned delegate is called. Must not be null. 41 | /// A Comparison delegate which proxies to the given Comparer. 42 | public static Comparison CreateComparison(IComparer comparer) 43 | { 44 | if (comparer == null) 45 | { 46 | throw new ArgumentNullException("comparer"); 47 | } 48 | return delegate(T x, T y) { return comparer.Compare(x, y); }; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | 9 | [assembly: InternalsVisibleTo("MiscUtil.UnitTests, PublicKey=" 10 | + "0024000004800000940000000602000000240000525341310004000001000100bb16d9f0c36900" 11 | + "bbe4124ff25cbb883abce8e5bc49e9d6185918d978ddc62d9dd2fb31f12c4bca5b5b3ca02a9f85" 12 | + "b094449d96d0a91bcc13f321ecc9446966e35a3b709f5f5b22c66ccea05ee564a91e6ea5959df8" 13 | + "333a40847ed8938194612ce84f7b3e6056ada43918444a883c7ef82583185a8f379281b62d6d2b" 14 | + "2236679c")] 15 | [assembly: AssemblyTitle("MiscUtil")] 16 | [assembly: AssemblyDescription("Miscellaneous Utility Library")] 17 | [assembly: AssemblyConfiguration("")] 18 | [assembly: AssemblyCompany("")] 19 | [assembly: AssemblyProduct("MiscUtil")] 20 | [assembly: AssemblyCopyright("Copyright © 2006")] 21 | [assembly: AssemblyTrademark("")] 22 | [assembly: AssemblyCulture("")] 23 | 24 | // Setting ComVisible to false makes the types in this assembly not visible 25 | // to COM components. If you need to access a type in this assembly from 26 | // COM, set the ComVisible attribute to true on that type. 27 | [assembly: ComVisible(false)] 28 | 29 | // The following GUID is for the ID of the typelib if this project is exposed to COM 30 | [assembly: Guid("90fc96ca-4f38-453f-a805-720f3dbf2f08")] 31 | 32 | // Version information for an assembly consists of the following four values: 33 | // 34 | // Major Version 35 | // Minor Version 36 | // Build Number 37 | // Revision 38 | // 39 | // You can specify all the values or you can default the Revision and Build Numbers 40 | // by using the '*' as shown below: 41 | [assembly: AssemblyVersion("1.0.0.0")] 42 | // This is filled in automatically with the revision number from buildkit.build 43 | [assembly: AssemblyFileVersion("1.0.0.285")] 44 | -------------------------------------------------------------------------------- /Extensions/TimeRelated/TimeSpanBasedExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Extensions.TimeRelated 4 | { 5 | /// 6 | /// Extension methods producing TimeSpan values. Note: Ticks should really 7 | /// take a long, and the rest should all take doubles. It looks like extension 8 | /// methods don't quite work properly with implicit numeric conversions :( 9 | /// 10 | public static class TimeSpanBasedExt 11 | { 12 | /// 13 | /// Returns a TimeSpan representing the specified number of ticks. 14 | /// 15 | public static TimeSpan Ticks(this int ticks) 16 | { 17 | return TimeSpan.FromTicks(ticks); 18 | } 19 | 20 | /// 21 | /// Returns a TimeSpan representing the specified number of milliseconds. 22 | /// 23 | public static TimeSpan Milliseconds(this int milliseconds) 24 | { 25 | return TimeSpan.FromMilliseconds(milliseconds); 26 | } 27 | 28 | /// 29 | /// Returns a TimeSpan representing the specified number of seconds. 30 | /// 31 | public static TimeSpan Seconds(this int seconds) 32 | { 33 | return TimeSpan.FromSeconds(seconds); 34 | } 35 | 36 | /// 37 | /// Returns a TimeSpan representing the specified number of minutes. 38 | /// 39 | public static TimeSpan Minutes(this int minutes) 40 | { 41 | return TimeSpan.FromMinutes(minutes); 42 | } 43 | 44 | /// 45 | /// Returns a TimeSpan representing the specified number of hours. 46 | /// 47 | public static TimeSpan Hours(this int hours) 48 | { 49 | return TimeSpan.FromHours(hours); 50 | } 51 | 52 | /// 53 | /// Returns a TimeSpan representing the specified number of days. 54 | /// 55 | public static TimeSpan Days(this int days) 56 | { 57 | return TimeSpan.FromDays(days); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Collections/DictionaryByType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | namespace MiscUtil.Collections 4 | { 5 | /// 6 | /// Map from types to instances of those types, e.g. int to 10 and 7 | /// string to "hi" within the same dictionary. This cannot be done 8 | /// without casting (and boxing for value types) as .NET cannot 9 | /// represent this relationship with generics in their current form. 10 | /// This class encapsulates the nastiness in a single place. 11 | /// 12 | public class DictionaryByType 13 | { 14 | private readonly IDictionary dictionary = new Dictionary(); 15 | 16 | /// 17 | /// Maps the specified type argument to the given value. If 18 | /// the type argument already has a value within the dictionary, 19 | /// ArgumentException is thrown. 20 | /// 21 | public void Add(T value) 22 | { 23 | dictionary.Add(typeof(T), value); 24 | } 25 | 26 | /// 27 | /// Maps the specified type argument to the given value. If 28 | /// the type argument already has a value within the dictionary, it 29 | /// is overwritten. 30 | /// 31 | public void Put(T value) 32 | { 33 | dictionary[typeof(T)] = value; 34 | } 35 | 36 | /// 37 | /// Attempts to fetch a value from the dictionary, throwing a 38 | /// KeyNotFoundException if the specified type argument has no 39 | /// entry in the dictionary. 40 | /// 41 | public T Get() 42 | { 43 | return (T) dictionary[typeof(T)]; 44 | } 45 | 46 | /// 47 | /// Attempts to fetch a value from the dictionary, returning false and 48 | /// setting the output parameter to the default value for T if it 49 | /// fails, or returning true and setting the output parameter to the 50 | /// fetched value if it succeeds. 51 | /// 52 | public bool TryGet(out T value) 53 | { 54 | object tmp; 55 | if (dictionary.TryGetValue(typeof(T), out tmp)) 56 | { 57 | value = (T) tmp; 58 | return true; 59 | } 60 | value = default(T); 61 | return false; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Conversion/LittleEndianBitConverter.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Conversion 3 | { 4 | /// 5 | /// Implementation of EndianBitConverter which converts to/from little-endian 6 | /// byte arrays. 7 | /// 8 | public sealed class LittleEndianBitConverter : EndianBitConverter 9 | { 10 | /// 11 | /// Indicates the byte order ("endianess") in which data is converted using this class. 12 | /// 13 | /// 14 | /// Different computer architectures store data using different byte orders. "Big-endian" 15 | /// means the most significant byte is on the left end of a word. "Little-endian" means the 16 | /// most significant byte is on the right end of a word. 17 | /// 18 | /// true if this converter is little-endian, false otherwise. 19 | public sealed override bool IsLittleEndian() 20 | { 21 | return true; 22 | } 23 | 24 | /// 25 | /// Indicates the byte order ("endianess") in which data is converted using this class. 26 | /// 27 | public sealed override Endianness Endianness 28 | { 29 | get { return Endianness.LittleEndian; } 30 | } 31 | 32 | /// 33 | /// Copies the specified number of bytes from value to buffer, starting at index. 34 | /// 35 | /// The value to copy 36 | /// The number of bytes to copy 37 | /// The buffer to copy the bytes into 38 | /// The index to start at 39 | protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) 40 | { 41 | for (int i=0; i < bytes; i++) 42 | { 43 | buffer[i+index] = unchecked((byte)(value&0xff)); 44 | value = value >> 8; 45 | } 46 | } 47 | 48 | /// 49 | /// Returns a value built from the specified number of bytes from the given buffer, 50 | /// starting at index. 51 | /// 52 | /// The data in byte array format 53 | /// The first index to use 54 | /// The number of bytes to use 55 | /// The value built from the given bytes 56 | protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) 57 | { 58 | long ret = 0; 59 | for (int i=0; i < bytesToConvert; i++) 60 | { 61 | ret = unchecked((ret << 8) | buffer[startIndex+bytesToConvert-1-i]); 62 | } 63 | return ret; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Conversion/BigEndianBitConverter.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace MiscUtil.Conversion 3 | { 4 | /// 5 | /// Implementation of EndianBitConverter which converts to/from big-endian 6 | /// byte arrays. 7 | /// 8 | public sealed class BigEndianBitConverter : EndianBitConverter 9 | { 10 | /// 11 | /// Indicates the byte order ("endianess") in which data is converted using this class. 12 | /// 13 | /// 14 | /// Different computer architectures store data using different byte orders. "Big-endian" 15 | /// means the most significant byte is on the left end of a word. "Little-endian" means the 16 | /// most significant byte is on the right end of a word. 17 | /// 18 | /// true if this converter is little-endian, false otherwise. 19 | public sealed override bool IsLittleEndian() 20 | { 21 | return false; 22 | } 23 | 24 | /// 25 | /// Indicates the byte order ("endianess") in which data is converted using this class. 26 | /// 27 | public sealed override Endianness Endianness 28 | { 29 | get { return Endianness.BigEndian; } 30 | } 31 | 32 | /// 33 | /// Copies the specified number of bytes from value to buffer, starting at index. 34 | /// 35 | /// The value to copy 36 | /// The number of bytes to copy 37 | /// The buffer to copy the bytes into 38 | /// The index to start at 39 | protected override void CopyBytesImpl(long value, int bytes, byte[] buffer, int index) 40 | { 41 | int endOffset = index+bytes-1; 42 | for (int i=0; i < bytes; i++) 43 | { 44 | buffer[endOffset-i] = unchecked((byte)(value&0xff)); 45 | value = value >> 8; 46 | } 47 | } 48 | 49 | /// 50 | /// Returns a value built from the specified number of bytes from the given buffer, 51 | /// starting at index. 52 | /// 53 | /// The data in byte array format 54 | /// The first index to use 55 | /// The number of bytes to use 56 | /// The value built from the given bytes 57 | protected override long FromBytes(byte[] buffer, int startIndex, int bytesToConvert) 58 | { 59 | long ret = 0; 60 | for (int i=0; i < bytesToConvert; i++) 61 | { 62 | ret = unchecked((ret << 8) | buffer[startIndex+i]); 63 | } 64 | return ret; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Threading/Delegates.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Threading 4 | { 5 | /// 6 | /// Delegate for handling exceptions. 7 | /// 8 | public delegate void ExceptionHandler(object sender, Exception e); 9 | 10 | #region Delegates from CustomThreadPool 11 | /// 12 | /// Delegate for handling exceptions thrown by work items executing 13 | /// in a custom thread pool. 14 | /// 15 | /// The pool which created the worker thread 16 | /// The work item which threw the exception 17 | /// The exception thrown 18 | /// 19 | /// Whether or not the exception has been handled by this delegate. The value 20 | /// of this parameter will be false on entry, and changing it to true will 21 | /// prevent any further delegates in the event from being executed. 22 | /// 23 | public delegate void ThreadPoolExceptionHandler (CustomThreadPool pool, 24 | ThreadPoolWorkItem workItem, 25 | Exception e, 26 | ref bool handled); 27 | 28 | /// 29 | /// Delegate for handling the event that a thread is about to execute 30 | /// a work item. 31 | /// 32 | /// The pool which created the worker thread 33 | /// The work item which is about to execute 34 | /// 35 | /// Whether or not the work item should be cancelled. The value 36 | /// of this parameter will be false on entry, and changing it to true will 37 | /// prevent any further delegates in the event from being executed, and 38 | /// prevent the work item itself from being executed. 39 | /// 40 | public delegate void BeforeWorkItemHandler (CustomThreadPool pool, 41 | ThreadPoolWorkItem workItem, 42 | ref bool cancel); 43 | 44 | /// 45 | /// Delegate for handling the event that a thread has executed a work item. 46 | /// 47 | /// The pool which created the worker thread 48 | /// The work item which has executed 49 | public delegate void AfterWorkItemHandler (CustomThreadPool pool, 50 | ThreadPoolWorkItem workItem); 51 | #endregion 52 | 53 | /// 54 | /// Delegate for handling the event that a thread has changed state 55 | /// (e.g. it's about to execute a work item, it's just executed one, etc). 56 | /// Also used for requests for a thread to change state (e.g. if a stop 57 | /// request has been received). 58 | /// 59 | public delegate void ThreadProgress(object sender); 60 | 61 | /// 62 | /// Represents the method that is executed by a ThreadController. 63 | /// 64 | public delegate void ControlledThreadStart (ThreadController controller, object state); 65 | } 66 | -------------------------------------------------------------------------------- /StaticRandom.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil 4 | { 5 | /// 6 | /// Thread-safe equivalent of System.Random, using just static methods. 7 | /// If all you want is a source of random numbers, this is an easy class to 8 | /// use. If you need to specify your own seeds (eg for reproducible sequences 9 | /// of numbers), use System.Random. 10 | /// 11 | public static class StaticRandom 12 | { 13 | static Random random = new Random(); 14 | static object myLock = new object(); 15 | 16 | /// 17 | /// Returns a nonnegative random number. 18 | /// 19 | /// A 32-bit signed integer greater than or equal to zero and less than Int32.MaxValue. 20 | public static int Next() 21 | { 22 | lock (myLock) 23 | { 24 | return random.Next(); 25 | } 26 | } 27 | 28 | /// 29 | /// Returns a nonnegative random number less than the specified maximum. 30 | /// 31 | /// 32 | /// A 32-bit signed integer greater than or equal to zero, and less than maxValue; 33 | /// that is, the range of return values includes zero but not maxValue. 34 | /// 35 | /// maxValue is less than zero. 36 | public static int Next(int max) 37 | { 38 | lock (myLock) 39 | { 40 | return random.Next(max); 41 | } 42 | } 43 | 44 | /// 45 | /// Returns a random number within a specified range. 46 | /// 47 | /// The inclusive lower bound of the random number returned. 48 | /// 49 | /// The exclusive upper bound of the random number returned. 50 | /// maxValue must be greater than or equal to minValue. 51 | /// 52 | /// 53 | /// A 32-bit signed integer greater than or equal to minValue and less than maxValue; 54 | /// that is, the range of return values includes minValue but not maxValue. 55 | /// If minValue equals maxValue, minValue is returned. 56 | /// 57 | /// minValue is greater than maxValue. 58 | public static int Next(int min, int max) 59 | { 60 | lock (myLock) 61 | { 62 | return random.Next(min, max); 63 | } 64 | } 65 | 66 | /// 67 | /// Returns a random number between 0.0 and 1.0. 68 | /// 69 | /// A double-precision floating point number greater than or equal to 0.0, and less than 1.0. 70 | public static double NextDouble() 71 | { 72 | lock (myLock) 73 | { 74 | return random.NextDouble(); 75 | } 76 | } 77 | 78 | /// 79 | /// Fills the elements of a specified array of bytes with random numbers. 80 | /// 81 | /// An array of bytes to contain random numbers. 82 | /// buffer is a null reference (Nothing in Visual Basic). 83 | public static void NextBytes(byte[] buffer) 84 | { 85 | lock (myLock) 86 | { 87 | random.NextBytes(buffer); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Linq/OrderedDataProducer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using MiscUtil.Extensions; 5 | 6 | namespace MiscUtil.Linq 7 | { 8 | /// 9 | /// A DataProducer with ordering capabilities 10 | /// Note that this may cause data to be buffered 11 | /// 12 | internal class OrderedDataProducer : IOrderedDataProducer 13 | { 14 | private bool dataHasEnded; 15 | private readonly IDataProducer baseProducer; 16 | private readonly IComparer comparer; 17 | private List buffer; 18 | 19 | public IDataProducer BaseProducer 20 | { 21 | get { return baseProducer; } 22 | } 23 | 24 | public IComparer Comparer 25 | { 26 | get { return comparer; } 27 | } 28 | 29 | public event Action DataProduced; 30 | public event Action EndOfData; 31 | 32 | /// 33 | /// Create a new OrderedDataProducer 34 | /// 35 | /// The base source which will supply data 36 | /// The comparer to use when sorting the data (once complete) 37 | public OrderedDataProducer( 38 | IDataProducer baseProducer, 39 | IComparer comparer) 40 | { 41 | baseProducer.ThrowIfNull("baseProducer"); 42 | 43 | this.baseProducer = baseProducer; 44 | this.comparer = comparer ?? Comparer.Default; 45 | 46 | baseProducer.DataProduced += new Action(OriginalDataProduced); 47 | baseProducer.EndOfData += new Action(EndOfOriginalData); 48 | } 49 | 50 | 51 | void OriginalDataProduced(T item) 52 | { 53 | if (dataHasEnded) 54 | { 55 | throw new InvalidOperationException("EndOfData already occurred"); 56 | } 57 | if (DataProduced != null) 58 | { // only get excited if somebody is listening 59 | if (buffer == null) buffer = new List(); 60 | buffer.Add(item); 61 | } 62 | } 63 | 64 | void EndOfOriginalData() 65 | { 66 | if (dataHasEnded) 67 | { 68 | throw new InvalidOperationException("EndOfData already occurred"); 69 | } 70 | dataHasEnded = true; 71 | // only do the sort if somebody is still listening 72 | if (DataProduced != null && buffer != null) 73 | { 74 | buffer.Sort(Comparer); 75 | foreach (T item in buffer) 76 | { 77 | OnDataProduced(item); 78 | } 79 | } 80 | buffer = null; 81 | OnEndOfData(); 82 | } 83 | 84 | void OnEndOfData() 85 | { 86 | if (EndOfData != null) EndOfData(); 87 | } 88 | 89 | void OnDataProduced(T item) 90 | { 91 | if (DataProduced != null) DataProduced(item); 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /IO/StringWriterWithEncoding.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace MiscUtil.IO 6 | { 7 | /// 8 | /// A simple class derived from StringWriter, but which allows 9 | /// the user to select which Encoding is used. This is most 10 | /// likely to be used with XmlTextWriter, which uses the Encoding 11 | /// property to determine which encoding to specify in the XML. 12 | /// 13 | public class StringWriterWithEncoding : StringWriter 14 | { 15 | /// 16 | /// The encoding to return in the Encoding property. 17 | /// 18 | readonly Encoding encoding; 19 | 20 | /// 21 | /// Initializes a new instance of the StringWriterWithEncoding class 22 | /// with the specified encoding. 23 | /// 24 | /// The encoding to report. 25 | public StringWriterWithEncoding(Encoding encoding) 26 | { 27 | if (encoding == null) 28 | { 29 | throw new ArgumentNullException("encoding"); 30 | } 31 | this.encoding = encoding; 32 | } 33 | 34 | /// 35 | /// Initializes a new instance of the StringWriter class with the 36 | /// specified format control and encoding. 37 | /// 38 | /// An IFormatProvider object that controls formatting. 39 | /// The encoding to report. 40 | public StringWriterWithEncoding (IFormatProvider formatProvider, Encoding encoding) 41 | : base (formatProvider) 42 | { 43 | if (encoding == null) 44 | { 45 | throw new ArgumentNullException("encoding"); 46 | } 47 | this.encoding = encoding; 48 | } 49 | 50 | /// 51 | /// Initializes a new instance of the StringWriter class that writes to the 52 | /// specified StringBuilder, and reports the specified encoding. 53 | /// 54 | /// The StringBuilder to write to. 55 | /// The encoding to report. 56 | public StringWriterWithEncoding (StringBuilder sb, Encoding encoding) 57 | : base (sb) 58 | { 59 | if (encoding == null) 60 | { 61 | throw new ArgumentNullException("encoding"); 62 | } 63 | this.encoding = encoding; 64 | } 65 | 66 | /// 67 | /// Initializes a new instance of the StringWriter class that writes to the specified 68 | /// StringBuilder, has the specified format provider, and reports the specified encoding. 69 | /// 70 | /// The StringBuilder to write to. 71 | /// An IFormatProvider object that controls formatting. 72 | /// The encoding to report. 73 | public StringWriterWithEncoding (StringBuilder sb, IFormatProvider formatProvider, Encoding encoding) 74 | : base (sb, formatProvider) 75 | { 76 | if (encoding == null) 77 | { 78 | throw new ArgumentNullException("encoding"); 79 | } 80 | this.encoding = encoding; 81 | } 82 | 83 | /// 84 | /// Gets the Encoding in which the output is written. 85 | /// 86 | public override Encoding Encoding 87 | { 88 | get 89 | { 90 | return encoding; 91 | } 92 | } 93 | 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Checksum/Adler32.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace MiscUtil.Checksum 4 | { 5 | /// 6 | /// Implementation of the Adler32 checksum routine. 7 | /// TODO: Derive from HashAlgorithm. 8 | /// 9 | public class Adler32 10 | { 11 | /// 12 | /// Base for modulo arithmetic 13 | /// 14 | const int Base = 65521; 15 | 16 | /// 17 | /// Number of iterations we can safely do before applying the modulo. 18 | /// 19 | const int NMax = 5552; 20 | 21 | /// 22 | /// Computes the Adler32 checksum for the given data. 23 | /// 24 | /// 25 | /// Initial value or previous result. Use 1 for the 26 | /// first transformation. 27 | /// 28 | /// The data to compute the checksum of 29 | /// Index of first byte to compute checksum for 30 | /// Number of bytes to compute checksum for 31 | /// The checksum of the given data 32 | public static int ComputeChecksum (int initial, byte[] data, int start, int length) 33 | { 34 | if (data == null) 35 | { 36 | throw (new System.ArgumentNullException("data")); 37 | } 38 | uint s1 = (uint)(initial & 0xffff); 39 | uint s2 = (uint)((initial >> 16) & 0xffff); 40 | 41 | int index=start; 42 | int len = length; 43 | 44 | int k; 45 | while (len > 0) 46 | { 47 | k = (len < NMax) ? len : NMax; 48 | len -= k; 49 | 50 | for (int i=0; i < k; i++) 51 | { 52 | s1 += data[index++]; 53 | s2 += s1; 54 | } 55 | s1 %= Base; 56 | s2 %= Base; 57 | } 58 | 59 | return (int)((s2<<16) | s1); 60 | } 61 | 62 | /// 63 | /// Computes the Adler32 checksum for the given data. 64 | /// 65 | /// 66 | /// Initial value or previous result. Use 1 for the 67 | /// first transformation. 68 | /// 69 | /// The data to compute the checksum of 70 | /// The checksum of the given data 71 | public static int ComputeChecksum (int initial, byte[] data) 72 | { 73 | if (data == null) 74 | { 75 | throw (new System.ArgumentNullException("data")); 76 | } 77 | return ComputeChecksum(initial, data, 0, data.Length); 78 | } 79 | 80 | /// 81 | /// Computes the checksum for a stream, starting from the current 82 | /// position and reading until no more can be read 83 | /// 84 | /// The stream to compute the checksum for 85 | /// The checksum for the stream 86 | public static int ComputeChecksum (Stream stream) 87 | { 88 | if (stream == null) 89 | { 90 | throw (new System.ArgumentNullException("stream")); 91 | } 92 | byte[] buffer = new byte[8172]; 93 | int size; 94 | int checksum=1; 95 | while ((size = stream.Read(buffer, 0, buffer.Length)) > 0) 96 | { 97 | checksum = ComputeChecksum(checksum, buffer, 0, size); 98 | } 99 | return checksum; 100 | } 101 | 102 | /// 103 | /// Computes the checksum of a file 104 | /// 105 | /// The file to compute the checksum of 106 | /// The checksum for the file 107 | public static int ComputeChecksum (string path) 108 | { 109 | using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read)) 110 | { 111 | return ComputeChecksum(stream); 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /IO/LineReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | 7 | namespace MiscUtil.IO 8 | { 9 | /// 10 | /// Reads a data source line by line. The source can be a file, a stream, 11 | /// or a text reader. In any case, the source is only opened when the 12 | /// enumerator is fetched, and is closed when the iterator is disposed. 13 | /// 14 | public sealed class LineReader : IEnumerable 15 | { 16 | /// 17 | /// Means of creating a TextReader to read from. 18 | /// 19 | readonly Func dataSource; 20 | 21 | /// 22 | /// Creates a LineReader from a stream source. The delegate is only 23 | /// called when the enumerator is fetched. UTF-8 is used to decode 24 | /// the stream into text. 25 | /// 26 | /// Data source 27 | public LineReader(Func streamSource) 28 | : this(streamSource, Encoding.UTF8) 29 | { 30 | } 31 | 32 | /// 33 | /// Creates a LineReader from a stream source. The delegate is only 34 | /// called when the enumerator is fetched. 35 | /// 36 | /// Data source 37 | /// Encoding to use to decode the stream into text 38 | public LineReader(Func streamSource, Encoding encoding) 39 | : this(() => new StreamReader(streamSource(), encoding)) 40 | { 41 | } 42 | 43 | /// 44 | /// Creates a LineReader from a filename. The file is only opened 45 | /// (or even checked for existence) when the enumerator is fetched. 46 | /// UTF8 is used to decode the file into text. 47 | /// 48 | /// File to read from 49 | public LineReader(string filename) 50 | : this(filename, Encoding.UTF8) 51 | { 52 | } 53 | 54 | /// 55 | /// Creates a LineReader from a filename. The file is only opened 56 | /// (or even checked for existence) when the enumerator is fetched. 57 | /// 58 | /// File to read from 59 | /// Encoding to use to decode the file into text 60 | public LineReader(string filename, Encoding encoding) 61 | : this(() => new StreamReader(filename, encoding)) 62 | { 63 | } 64 | 65 | /// 66 | /// Creates a LineReader from a TextReader source. The delegate 67 | /// is only called when the enumerator is fetched 68 | /// 69 | /// Data source 70 | public LineReader(Func dataSource) 71 | { 72 | this.dataSource = dataSource; 73 | } 74 | 75 | /// 76 | /// Enumerates the data source line by line. 77 | /// 78 | public IEnumerator GetEnumerator() 79 | { 80 | using (TextReader reader = dataSource()) 81 | { 82 | string line; 83 | while ((line = reader.ReadLine()) != null) 84 | { 85 | yield return line; 86 | } 87 | } 88 | } 89 | 90 | /// 91 | /// Enumerates the data source line by line. 92 | /// 93 | IEnumerator IEnumerable.GetEnumerator() 94 | { 95 | return GetEnumerator(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Collections/Extensions/DictionaryExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MiscUtil.Collections.Extensions 5 | { 6 | /// 7 | /// Extensions to IDictionary 8 | /// 9 | public static class DictionaryExt 10 | { 11 | /// 12 | /// Returns the value associated with the specified key if there 13 | /// already is one, or inserts a new value for the specified key and 14 | /// returns that. 15 | /// 16 | /// Type of key 17 | /// Type of value, which must either have 18 | /// a public parameterless constructor or be a value type 19 | /// Dictionary to access 20 | /// Key to lookup 21 | /// Existing value in the dictionary, or new one inserted 22 | public static TValue GetOrCreate(this IDictionary dictionary, 23 | TKey key) 24 | where TValue : new() 25 | { 26 | TValue ret; 27 | if (!dictionary.TryGetValue(key, out ret)) 28 | { 29 | ret = new TValue(); 30 | dictionary[key] = ret; 31 | } 32 | return ret; 33 | } 34 | 35 | /// 36 | /// Returns the value associated with the specified key if there already 37 | /// is one, or calls the specified delegate to create a new value which is 38 | /// stored and returned. 39 | /// 40 | /// Type of key 41 | /// Type of value 42 | /// Dictionary to access 43 | /// Key to lookup 44 | /// Delegate to provide new value if required 45 | /// Existing value in the dictionary, or new one inserted 46 | public static TValue GetOrCreate(this IDictionary dictionary, 47 | TKey key, 48 | Func valueProvider) 49 | { 50 | TValue ret; 51 | if (!dictionary.TryGetValue(key, out ret)) 52 | { 53 | ret = valueProvider(); 54 | dictionary[key] = ret; 55 | } 56 | return ret; 57 | } 58 | 59 | /// 60 | /// Returns the value associated with the specified key if there 61 | /// already is one, or inserts the specified value and returns it. 62 | /// 63 | /// Type of key 64 | /// Type of value 65 | /// Dictionary to access 66 | /// Key to lookup 67 | /// Value to use when key is missing 68 | /// Existing value in the dictionary, or new one inserted 69 | public static TValue GetOrCreate(this IDictionary dictionary, 70 | TKey key, 71 | TValue missingValue) 72 | { 73 | TValue ret; 74 | if (!dictionary.TryGetValue(key, out ret)) 75 | { 76 | ret = missingValue; 77 | dictionary[key] = ret; 78 | } 79 | return ret; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Compression/Vcdiff/CodeTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Compression.Vcdiff 4 | { 5 | /// 6 | /// Table used to encode/decode instructions. 7 | /// 8 | internal sealed class CodeTable 9 | { 10 | /// 11 | /// Default code table specified in RFC 3284. 12 | /// 13 | static internal CodeTable Default = BuildDefaultCodeTable(); 14 | 15 | /// 16 | /// Array of entries in the code table 17 | /// 18 | Instruction[,] entries = new Instruction[256,2]; 19 | 20 | /// 21 | /// 22 | /// 23 | internal Instruction this[int i, int j] 24 | { 25 | get 26 | { 27 | return entries[i, j]; 28 | } 29 | } 30 | 31 | internal CodeTable(byte[] bytes) 32 | { 33 | for (int i=0; i < 256; i++) 34 | { 35 | entries[i,0] = new Instruction((InstructionType)bytes[i], bytes[i+512], bytes[i+1024]); 36 | entries[i,1] = new Instruction((InstructionType)bytes[i+256], bytes[i+768], bytes[i+1280]); 37 | } 38 | } 39 | 40 | internal CodeTable(Instruction[,] entries) 41 | { 42 | if (entries==null) 43 | { 44 | throw new ArgumentNullException("entries"); 45 | } 46 | if (entries.Rank != 2) 47 | { 48 | throw new ArgumentException ("Array must be rectangular.", "entries"); 49 | } 50 | if (entries.GetLength(0) != 256) 51 | { 52 | throw new ArgumentException ("Array must have outer length 256.", "entries"); 53 | } 54 | if (entries.GetLength(1) != 2) 55 | { 56 | throw new ArgumentException ("Array must have inner length 256.", "entries"); 57 | } 58 | Array.Copy (entries, 0, this.entries, 0, 512); 59 | } 60 | 61 | /// 62 | /// Builds the default code table specified in RFC 3284 63 | /// 64 | /// 65 | /// The default code table. 66 | /// 67 | static CodeTable BuildDefaultCodeTable() 68 | { 69 | // Defaults are NoOps with size and mode 0. 70 | Instruction[,] entries = new Instruction[256,2]; 71 | entries[0, 0] = new Instruction(InstructionType.Run, 0, 0); 72 | for (byte i=0; i < 18; i++) 73 | { 74 | entries[i+1, 0] = new Instruction(InstructionType.Add, i, 0); 75 | } 76 | 77 | int index = 19; 78 | 79 | // Entries 19-162 80 | for (byte mode = 0; mode < 9; mode++) 81 | { 82 | entries[index++, 0] = new Instruction (InstructionType.Copy, 0, mode); 83 | for (byte size = 4; size < 19; size++) 84 | { 85 | entries[index++, 0] = new Instruction (InstructionType.Copy, size, mode); 86 | } 87 | } 88 | 89 | // Entries 163-234 90 | for (byte mode = 0; mode < 6; mode++) 91 | { 92 | for (byte addSize = 1; addSize < 5; addSize++) 93 | { 94 | for (byte copySize = 4; copySize < 7; copySize++) 95 | { 96 | entries[index, 0] = new Instruction (InstructionType.Add, addSize, 0); 97 | entries[index++, 1] = new Instruction (InstructionType.Copy, copySize, mode); 98 | } 99 | } 100 | } 101 | 102 | // Entries 235-246 103 | for (byte mode = 6; mode < 9; mode++) 104 | { 105 | for (byte addSize = 1; addSize < 5; addSize++) 106 | { 107 | entries[index, 0] = new Instruction (InstructionType.Add, addSize, 0); 108 | entries[index++, 1] = new Instruction (InstructionType.Copy, 4, mode); 109 | } 110 | } 111 | 112 | // Entries 247-255 113 | for (byte mode = 0; mode < 9; mode++) 114 | { 115 | entries[index, 0] = new Instruction (InstructionType.Copy, 4, mode); 116 | entries[index++, 1] = new Instruction (InstructionType.Add, 1, 0); 117 | } 118 | 119 | return new CodeTable(entries); 120 | } 121 | 122 | internal byte[] GetBytes() 123 | { 124 | byte[] ret = new byte[1536]; 125 | for (int i=0; i < 256; i++) 126 | { 127 | ret[i]=(byte)entries[i,0].Type; 128 | ret[i+256]=(byte)entries[i,1].Type; 129 | ret[i+512]=entries[i,0].Size; 130 | ret[i+768]=entries[i,1].Size; 131 | ret[i+1024]=entries[i,0].Mode; 132 | ret[i+1280]=entries[i,1].Mode; 133 | } 134 | return ret; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /PartialComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MiscUtil 4 | { 5 | /// 6 | /// Class to provide partial comparisons, which can be useful when 7 | /// implementing full comparisons in other classes. 8 | /// 9 | public static class PartialComparer 10 | { 11 | /// 12 | /// Provides comparisons for just the references, returning 0 13 | /// if the arguments are the same reference, -1 if just the first is null, 14 | /// and 1 if just the second is null. Otherwise, this method returns null. 15 | /// It can be used to make life easier for an initial comparison 16 | /// before comparing individual components of an object. 17 | /// 18 | /// The type of objects to compare 19 | /// The first object to compare 20 | /// The second object to compare 21 | public static int? ReferenceCompare(T first, T second) 22 | where T : class 23 | { 24 | if (first==second) 25 | { 26 | return 0; 27 | } 28 | if (first==null) 29 | { 30 | return -1; 31 | } 32 | if (second==null) 33 | { 34 | return 1; 35 | } 36 | return null; 37 | } 38 | 39 | /// 40 | /// Compares two instances of T using the default comparer for T, 41 | /// returning a non-null value in the case of inequality, or null 42 | /// where the default comparer would return 0. This aids chained 43 | /// comparisons (where if the first values are equal, you move 44 | /// on to the next ones) if you use the null coalescing operator. 45 | /// 46 | /// The type of objects to compare 47 | /// The first object to compare 48 | /// The second object to compare 49 | public static int? Compare(T first, T second) 50 | { 51 | return Compare(Comparer.Default, first, second); 52 | } 53 | 54 | /// 55 | /// Compares two instances of T using the specified comparer for T, 56 | /// returning a non-null value in the case of inequality, or null 57 | /// where the comparer would return 0. This aids chained 58 | /// comparisons (where if the first values are equal, you move 59 | /// on to the next ones) if you use the null coalescing operator. 60 | /// 61 | /// The type of objects to compare 62 | /// The comparer to use 63 | /// The first object to compare 64 | /// The second object to compare 65 | public static int? Compare(IComparer comparer, T first, T second) 66 | { 67 | int ret = comparer.Compare(first, second); 68 | if (ret == 0) 69 | { 70 | return null; 71 | } 72 | return ret; 73 | } 74 | 75 | /// 76 | /// Compares two instances of T, returning true if they are definitely 77 | /// the same (i.e. the same references), false if exactly one reference is 78 | /// null, or null otherwise. This aids implementing equality operations. 79 | /// 80 | /// The type of objects to compare 81 | /// The first object to compare 82 | /// The second object to compare 83 | public static bool? Equals(T first, T second) 84 | where T : class 85 | { 86 | if (first==second) 87 | { 88 | return true; 89 | } 90 | if (first==null || second==null) 91 | { 92 | return false; 93 | } 94 | return null; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Reflection/PropertyCopy.cs: -------------------------------------------------------------------------------- 1 | #if DOTNET35 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | 7 | namespace MiscUtil.Reflection 8 | { 9 | /// 10 | /// Generic class which copies to its target type from a source 11 | /// type specified in the Copy method. The types are specified 12 | /// separately to take advantage of type inference on generic 13 | /// method arguments. 14 | /// 15 | public static class PropertyCopy where TTarget : class, new() 16 | { 17 | /// 18 | /// Copies all readable properties from the source to a new instance 19 | /// of TTarget. 20 | /// 21 | public static TTarget CopyFrom(TSource source) where TSource : class 22 | { 23 | return PropertyCopier.Copy(source); 24 | } 25 | 26 | /// 27 | /// Static class to efficiently store the compiled delegate which can 28 | /// do the copying. We need a bit of work to ensure that exceptions are 29 | /// appropriately propagated, as the exception is generated at type initialization 30 | /// time, but we wish it to be thrown as an ArgumentException. 31 | /// 32 | private static class PropertyCopier where TSource : class 33 | { 34 | private static readonly Func copier; 35 | private static readonly Exception initializationException; 36 | 37 | internal static TTarget Copy(TSource source) 38 | { 39 | if (initializationException != null) 40 | { 41 | throw initializationException; 42 | } 43 | if (source == null) 44 | { 45 | throw new ArgumentNullException("source"); 46 | } 47 | return copier(source); 48 | } 49 | 50 | static PropertyCopier() 51 | { 52 | try 53 | { 54 | copier = BuildCopier(); 55 | initializationException = null; 56 | } 57 | catch (Exception e) 58 | { 59 | copier = null; 60 | initializationException = e; 61 | } 62 | } 63 | 64 | private static Func BuildCopier() 65 | { 66 | ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source"); 67 | var bindings = new List(); 68 | foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties()) 69 | { 70 | if (!sourceProperty.CanRead) 71 | { 72 | continue; 73 | } 74 | PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name); 75 | if (targetProperty == null) 76 | { 77 | throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName); 78 | } 79 | if (!targetProperty.CanWrite) 80 | { 81 | throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName); 82 | } 83 | if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)) 84 | { 85 | throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName); 86 | } 87 | bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty))); 88 | } 89 | Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings); 90 | return Expression.Lambda>(initializer, sourceParameter).Compile(); 91 | } 92 | } 93 | } 94 | } 95 | #endif 96 | -------------------------------------------------------------------------------- /Extensions/TimeRelated/DateTimeBasedExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil.Extensions.TimeRelated 4 | { 5 | /// 6 | /// Extension methods producing DateTime values. These are useful 7 | /// when writing unit tests with fixed dates which should be easily understandable. 8 | /// 9 | public static class DateTimeBasedExt 10 | { 11 | /// 12 | /// Returns a DateTime representing the specified day in January 13 | /// in the specified year. 14 | /// 15 | public static DateTime January(this int day, int year) 16 | { 17 | return new DateTime(year, 1, day); 18 | } 19 | 20 | /// 21 | /// Returns a DateTime representing the specified day in February 22 | /// in the specified year. 23 | /// 24 | public static DateTime February(this int day, int year) 25 | { 26 | return new DateTime(year, 2, day); 27 | } 28 | 29 | /// 30 | /// Returns a DateTime representing the specified day in March 31 | /// in the specified year. 32 | /// 33 | public static DateTime March(this int day, int year) 34 | { 35 | return new DateTime(year, 3, day); 36 | } 37 | 38 | /// 39 | /// Returns a DateTime representing the specified day in April 40 | /// in the specified year. 41 | /// 42 | public static DateTime April(this int day, int year) 43 | { 44 | return new DateTime(year, 4, day); 45 | } 46 | 47 | /// 48 | /// Returns a DateTime representing the specified day in May 49 | /// in the specified year. 50 | /// 51 | public static DateTime May(this int day, int year) 52 | { 53 | return new DateTime(year, 5, day); 54 | } 55 | 56 | /// 57 | /// Returns a DateTime representing the specified day in June 58 | /// in the specified year. 59 | /// 60 | public static DateTime June(this int day, int year) 61 | { 62 | return new DateTime(year, 6, day); 63 | } 64 | 65 | /// 66 | /// Returns a DateTime representing the specified day in July 67 | /// in the specified year. 68 | /// 69 | public static DateTime July(this int day, int year) 70 | { 71 | return new DateTime(year, 7, day); 72 | } 73 | 74 | /// 75 | /// Returns a DateTime representing the specified day in August 76 | /// in the specified year. 77 | /// 78 | public static DateTime August(this int day, int year) 79 | { 80 | return new DateTime(year, 8, day); 81 | } 82 | 83 | /// 84 | /// Returns a DateTime representing the specified day in September 85 | /// in the specified year. 86 | /// 87 | public static DateTime September(this int day, int year) 88 | { 89 | return new DateTime(year, 9, day); 90 | } 91 | 92 | /// 93 | /// Returns a DateTime representing the specified day in October 94 | /// in the specified year. 95 | /// 96 | public static DateTime October(this int day, int year) 97 | { 98 | return new DateTime(year, 10, day); 99 | } 100 | 101 | /// 102 | /// Returns a DateTime representing the specified day in November 103 | /// in the specified year. 104 | /// 105 | public static DateTime November(this int day, int year) 106 | { 107 | return new DateTime(year, 11, day); 108 | } 109 | 110 | /// 111 | /// Returns a DateTime representing the specified day in December 112 | /// in the specified year. 113 | /// 114 | public static DateTime December(this int day, int year) 115 | { 116 | return new DateTime(year, 12, day); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Collections/RangeIterator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using MiscUtil.Collections.Extensions; 5 | using MiscUtil.Extensions; 6 | 7 | namespace MiscUtil.Collections 8 | { 9 | /// 10 | /// Iterates over a range. Despite its name, this implements IEnumerable{T} rather than 11 | /// IEnumerator{T} - it just sounds better, frankly. 12 | /// 13 | public class RangeIterator : IEnumerable 14 | { 15 | readonly Range range; 16 | /// 17 | /// Returns the range this object iterates over 18 | /// 19 | public Range Range 20 | { 21 | get { return range; } 22 | } 23 | 24 | readonly Func step; 25 | /// 26 | /// Returns the step function used for this range 27 | /// 28 | public Func Step 29 | { 30 | get { return step; } 31 | } 32 | 33 | readonly bool ascending; 34 | /// 35 | /// Returns whether or not this iterator works up from the start point (ascending) 36 | /// or down from the end point (descending) 37 | /// 38 | public bool Ascending 39 | { 40 | get { return ascending; } 41 | } 42 | 43 | /// 44 | /// Creates an ascending iterator over the given range with the given step function 45 | /// 46 | public RangeIterator(Range range, Func step) 47 | : this(range, step, true) 48 | { 49 | } 50 | 51 | /// 52 | /// Creates an iterator over the given range with the given step function, 53 | /// with the specified direction. 54 | /// 55 | public RangeIterator(Range range, Func step, bool ascending) 56 | { 57 | step.ThrowIfNull("step"); 58 | 59 | if ((ascending && range.Comparer.Compare(range.Start, step(range.Start)) >= 0) || 60 | (!ascending && range.Comparer.Compare(range.End, step(range.End)) <= 0)) 61 | { 62 | throw new ArgumentException("step does nothing, or progresses the wrong way"); 63 | } 64 | this.ascending = ascending; 65 | this.range = range; 66 | this.step = step; 67 | } 68 | 69 | /// 70 | /// Returns an IEnumerator{T} running over the range. 71 | /// 72 | public IEnumerator GetEnumerator() 73 | { 74 | // A descending range effectively has the start and end points (and inclusions) 75 | // reversed, and a reverse comparer. 76 | bool includesStart = ascending ? range.IncludesStart : range.IncludesEnd; 77 | bool includesEnd = ascending ? range.IncludesEnd : range.IncludesStart; 78 | T start = ascending ? range.Start : range.End; 79 | T end = ascending ? range.End : range.Start; 80 | IComparer comparer = ascending ? range.Comparer : range.Comparer.Reverse(); 81 | 82 | // Now we can use our local version of the range variables to iterate 83 | 84 | T value = start; 85 | 86 | if (includesStart) 87 | { 88 | // Deal with possibility that start point = end point 89 | if (includesEnd || comparer.Compare(value, end) < 0) 90 | { 91 | yield return value; 92 | } 93 | } 94 | value = step(value); 95 | 96 | while (comparer.Compare(value, end) < 0) 97 | { 98 | yield return value; 99 | value = step(value); 100 | } 101 | 102 | // We've already performed a step, therefore we can't 103 | // still be at the start point 104 | if (includesEnd && comparer.Compare(value, end) == 0) 105 | { 106 | yield return value; 107 | } 108 | } 109 | 110 | /// 111 | /// Returns an IEnumerator running over the range. 112 | /// 113 | IEnumerator IEnumerable.GetEnumerator() 114 | { 115 | return GetEnumerator(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Collections/SmartEnumerable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace MiscUtil.Collections 6 | { 7 | /// 8 | /// Static class to make creation easier. If possible though, use the extension 9 | /// method in SmartEnumerableExt. 10 | /// 11 | public static class SmartEnumerable 12 | { 13 | /// 14 | /// Extension method to make life easier. 15 | /// 16 | /// Type of enumerable 17 | /// Source enumerable 18 | /// A new SmartEnumerable of the appropriate type 19 | public static SmartEnumerable Create(IEnumerable source) 20 | { 21 | return new SmartEnumerable(source); 22 | } 23 | } 24 | 25 | /// 26 | /// Type chaining an IEnumerable<T> to allow the iterating code 27 | /// to detect the first and last entries simply. 28 | /// 29 | /// Type to iterate over 30 | public class SmartEnumerable : IEnumerable.Entry> 31 | { 32 | /// 33 | /// Enumerable we proxy to 34 | /// 35 | readonly IEnumerable enumerable; 36 | 37 | /// 38 | /// Constructor. 39 | /// 40 | /// Collection to enumerate. Must not be null. 41 | public SmartEnumerable(IEnumerable enumerable) 42 | { 43 | if (enumerable==null) 44 | { 45 | throw new ArgumentNullException ("enumerable"); 46 | } 47 | this.enumerable = enumerable; 48 | } 49 | 50 | /// 51 | /// Returns an enumeration of Entry objects, each of which knows 52 | /// whether it is the first/last of the enumeration, as well as the 53 | /// current value. 54 | /// 55 | public IEnumerator GetEnumerator() 56 | { 57 | using (IEnumerator enumerator = enumerable.GetEnumerator()) 58 | { 59 | if (!enumerator.MoveNext()) 60 | { 61 | yield break; 62 | } 63 | bool isFirst = true; 64 | bool isLast = false; 65 | int index=0; 66 | while (!isLast) 67 | { 68 | T current = enumerator.Current; 69 | isLast = !enumerator.MoveNext(); 70 | yield return new Entry(isFirst, isLast, current, index++); 71 | isFirst = false; 72 | } 73 | } 74 | } 75 | 76 | /// 77 | /// Non-generic form of GetEnumerator. 78 | /// 79 | IEnumerator IEnumerable.GetEnumerator() 80 | { 81 | return GetEnumerator(); 82 | } 83 | 84 | /// 85 | /// Represents each entry returned within a collection, 86 | /// containing the value and whether it is the first and/or 87 | /// the last entry in the collection's. enumeration 88 | /// 89 | public class Entry 90 | { 91 | readonly bool isFirst; 92 | readonly bool isLast; 93 | readonly T value; 94 | readonly int index; 95 | 96 | internal Entry(bool isFirst, bool isLast, T value, int index) 97 | { 98 | this.isFirst = isFirst; 99 | this.isLast = isLast; 100 | this.value = value; 101 | this.index = index; 102 | } 103 | 104 | /// 105 | /// The value of the entry. 106 | /// 107 | public T Value { get { return value; } } 108 | 109 | /// 110 | /// Whether or not this entry is first in the collection's enumeration. 111 | /// 112 | public bool IsFirst { get { return isFirst; } } 113 | 114 | /// 115 | /// Whether or not this entry is last in the collection's enumeration. 116 | /// 117 | public bool IsLast { get { return isLast; } } 118 | 119 | /// 120 | /// The 0-based index of this entry (i.e. how many entries have been returned before this one) 121 | /// 122 | public int Index { get { return index; } } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Linq/ExpressionUtil.cs: -------------------------------------------------------------------------------- 1 | #if DOTNET35 2 | using System; 3 | using System.Linq.Expressions; 4 | 5 | namespace MiscUtil.Linq 6 | { 7 | /// 8 | /// General purpose Expression utilities 9 | /// 10 | public static class ExpressionUtil 11 | { 12 | /// 13 | /// Create a function delegate representing a unary operation 14 | /// 15 | /// The parameter type 16 | /// The return type 17 | /// Body factory 18 | /// Compiled function delegate 19 | public static Func CreateExpression( 20 | Func body) 21 | { 22 | ParameterExpression inp = Expression.Parameter(typeof(TArg1), "inp"); 23 | try 24 | { 25 | return Expression.Lambda>(body(inp), inp).Compile(); 26 | } 27 | catch (Exception ex) 28 | { 29 | string msg = ex.Message; // avoid capture of ex itself 30 | return delegate { throw new InvalidOperationException(msg); }; 31 | } 32 | } 33 | 34 | /// 35 | /// Create a function delegate representing a binary operation 36 | /// 37 | /// The first parameter type 38 | /// The second parameter type 39 | /// The return type 40 | /// Body factory 41 | /// Compiled function delegate 42 | public static Func CreateExpression( 43 | Func body) 44 | { 45 | return CreateExpression(body, false); 46 | } 47 | 48 | /// 49 | /// Create a function delegate representing a binary operation 50 | /// 51 | /// 52 | /// If no matching operation is possible, attempt to convert 53 | /// TArg1 and TArg2 to TResult for a match? For example, there is no 54 | /// "decimal operator /(decimal, int)", but by converting TArg2 (int) to 55 | /// TResult (decimal) a match is found. 56 | /// 57 | /// The first parameter type 58 | /// The second parameter type 59 | /// The return type 60 | /// Body factory 61 | /// Compiled function delegate 62 | public static Func CreateExpression( 63 | Func body, bool castArgsToResultOnFailure) 64 | { 65 | ParameterExpression lhs = Expression.Parameter(typeof(TArg1), "lhs"); 66 | ParameterExpression rhs = Expression.Parameter(typeof(TArg2), "rhs"); 67 | try 68 | { 69 | try 70 | { 71 | return Expression.Lambda>(body(lhs, rhs), lhs, rhs).Compile(); 72 | } 73 | catch (InvalidOperationException) 74 | { 75 | if (castArgsToResultOnFailure && !( // if we show retry 76 | typeof(TArg1) == typeof(TResult) && // and the args aren't 77 | typeof(TArg2) == typeof(TResult))) 78 | { // already "TValue, TValue, TValue"... 79 | // convert both lhs and rhs to TResult (as appropriate) 80 | Expression castLhs = typeof(TArg1) == typeof(TResult) ? 81 | (Expression)lhs : 82 | (Expression)Expression.Convert(lhs, typeof(TResult)); 83 | Expression castRhs = typeof(TArg2) == typeof(TResult) ? 84 | (Expression)rhs : 85 | (Expression)Expression.Convert(rhs, typeof(TResult)); 86 | 87 | return Expression.Lambda>( 88 | body(castLhs, castRhs), lhs, rhs).Compile(); 89 | } 90 | else throw; 91 | } 92 | } 93 | catch (Exception ex) 94 | { 95 | string msg = ex.Message; // avoid capture of ex itself 96 | return delegate { throw new InvalidOperationException(msg); }; 97 | } 98 | } 99 | } 100 | } 101 | #endif -------------------------------------------------------------------------------- /Linq/DataProducer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MiscUtil.Linq 5 | { 6 | /// 7 | /// Very simple implementation of IDataProducer. 8 | /// 9 | /// 10 | public class DataProducer : IDataProducer 11 | { 12 | /// 13 | /// Event which is raised when an item of data is produced. 14 | /// This will not be raised after EndOfData has been raised. 15 | /// The parameter for the event is the 16 | /// 17 | /// 18 | public event Action DataProduced; 19 | /// 20 | /// Event which is raised when the sequence has finished being 21 | /// produced. This will be raised exactly once, and after all 22 | /// DataProduced events (if any) have been raised. 23 | /// 24 | /// 25 | public event Action EndOfData; 26 | bool endReached = false; 27 | 28 | /// 29 | /// Signals a single item of data. 30 | /// 31 | public void Produce(T item) 32 | { 33 | if (endReached) 34 | { 35 | throw new InvalidOperationException("Cannot produce after end of data"); 36 | } 37 | if (DataProduced != null) 38 | { 39 | DataProduced(item); 40 | } 41 | } 42 | 43 | /// 44 | /// Signals multiple items of data, one at a time, then ends. 45 | /// Note that this method only exists to support the params modifier. 46 | /// In every other way it's equivalent to the ProduceAndEnd(IEnumerable{T}). 47 | /// 48 | public void ProduceAndEnd(params T[] items) 49 | { 50 | ProduceAndEnd((IEnumerable)items); 51 | } 52 | 53 | /// 54 | /// Signals multiple items of data, one at a time, then ends. 55 | /// 56 | public void ProduceAndEnd(IEnumerable items) 57 | { 58 | foreach (T item in items) 59 | { 60 | Produce(item); 61 | } 62 | End(); 63 | } 64 | 65 | /// 66 | /// Pumps the specified items into this data producer, yielding results 67 | /// as they are received. Before an item is pumped, an internal queue is 68 | /// created. Pumping an item may yield results at the other end of the pipeline 69 | /// - any such results are buffered in the queue. When the pumping of a particular 70 | /// item has finished, all results in the queue are yielded. This means that 71 | /// naturally streaming operations (projection and filtering) require only a single item 72 | /// buffer. This producer "ends" when all the items have been produced. If the result 73 | /// pipeline ends before all items have been pumped, the buffered results are yielded 74 | /// but no more items are pumped. 75 | /// 76 | /// Type of element in the result pipeline 77 | /// Items to insert into the pipeline 78 | /// The pipeline to subscribe to for items to yield 79 | /// A sequence of yielded items. 80 | public IEnumerable PumpProduceAndEnd(IEnumerable items, IDataProducer pipeline) 81 | { 82 | bool stop = false; 83 | Queue resultBuffer = new Queue(); 84 | pipeline.DataProduced += result => resultBuffer.Enqueue(result); 85 | pipeline.EndOfData += () => stop = true; 86 | foreach (T item in items) 87 | { 88 | Produce(item); 89 | // Unbuffer as we go 90 | while (resultBuffer.Count > 0) 91 | { 92 | yield return resultBuffer.Dequeue(); 93 | } 94 | if (stop) 95 | { 96 | yield break; 97 | } 98 | } 99 | End(); 100 | // Yield any final items which may have been produced due to ending the pipeline 101 | while (resultBuffer.Count > 0) 102 | { 103 | yield return resultBuffer.Dequeue(); 104 | } 105 | } 106 | 107 | /// 108 | /// Signal the end of data. This can only be called once, and 109 | /// afterwards the Produce method must not be called. 110 | /// 111 | public void End() 112 | { 113 | if (endReached) 114 | { 115 | throw new InvalidOperationException("Cannot produce end twice"); 116 | } 117 | endReached = true; 118 | if (EndOfData != null) 119 | { 120 | EndOfData(); 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /NonNullable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MiscUtil 4 | { 5 | /// 6 | /// Encapsulates a reference compatible with the type parameter. The reference 7 | /// is guaranteed to be non-null unless the value has been created with the 8 | /// parameterless constructor (e.g. as the default value of a field or array). 9 | /// Implicit conversions are available to and from the type parameter. The 10 | /// conversion to the non-nullable type will throw ArgumentNullException 11 | /// when presented with a null reference. The conversion from the non-nullable 12 | /// type will throw NullReferenceException if it contains a null reference. 13 | /// This type is a value type (to avoid taking any extra space) and as the CLR 14 | /// unfortunately has no knowledge of it, it will be boxed as any other value 15 | /// type. The conversions are also available through the Value property and the 16 | /// parameterised constructor. 17 | /// 18 | /// Type of non-nullable reference to encapsulate 19 | public struct NonNullable : IEquatable> where T : class 20 | { 21 | private readonly T value; 22 | 23 | /// 24 | /// Creates a non-nullable value encapsulating the specified reference. 25 | /// 26 | public NonNullable(T value) 27 | { 28 | if (value == null) 29 | { 30 | throw new ArgumentNullException("value"); 31 | } 32 | this.value = value; 33 | } 34 | 35 | /// 36 | /// Retrieves the encapsulated value, or throws a NullReferenceException if 37 | /// this instance was created with the parameterless constructor or by default. 38 | /// 39 | public T Value 40 | { 41 | get 42 | { 43 | if (value == null) 44 | { 45 | throw new NullReferenceException(); 46 | } 47 | return value; 48 | } 49 | } 50 | 51 | /// 52 | /// Implicit conversion from the specified reference. 53 | /// 54 | public static implicit operator NonNullable(T value) 55 | { 56 | return new NonNullable(value); 57 | } 58 | 59 | /// 60 | /// Implicit conversion to the type parameter from the encapsulated value. 61 | /// 62 | public static implicit operator T(NonNullable wrapper) 63 | { 64 | return wrapper.Value; 65 | } 66 | 67 | /// 68 | /// Equality operator, which performs an identity comparison on the encapuslated 69 | /// references. No exception is thrown even if the references are null. 70 | /// 71 | public static bool operator ==(NonNullable first, NonNullable second) 72 | { 73 | return first.value == second.value; 74 | } 75 | 76 | /// 77 | /// Inequality operator, which performs an identity comparison on the encapuslated 78 | /// references. No exception is thrown even if the references are null. 79 | /// 80 | public static bool operator !=(NonNullable first, NonNullable second) 81 | { 82 | return first.value != second.value; 83 | } 84 | 85 | /// 86 | /// Equality is deferred to encapsulated references, but there is no equality 87 | /// between a NonNullable[T] and a T. This method never throws an exception, 88 | /// even if a null reference is encapsulated. 89 | /// 90 | public override bool Equals(object obj) 91 | { 92 | if (!(obj is NonNullable)) 93 | { 94 | return false; 95 | } 96 | return Equals((NonNullable) obj); 97 | } 98 | 99 | /// 100 | /// Type-safe (and non-boxing) equality check. 101 | /// 102 | public bool Equals(NonNullable other) 103 | { 104 | return object.Equals(this.value, other.value); 105 | } 106 | 107 | /// 108 | /// Type-safe (and non-boxing) static equality check. 109 | /// 110 | public static bool Equals(NonNullable first, NonNullable second) 111 | { 112 | return object.Equals(first.value, second.value); 113 | } 114 | 115 | /// 116 | /// Defers to the GetHashCode implementation of the encapsulated reference, or 0 if 117 | /// the reference is null. 118 | /// 119 | public override int GetHashCode() 120 | { 121 | return value == null ? 0 : value.GetHashCode(); 122 | } 123 | 124 | /// 125 | /// Defers to the ToString implementation of the encapsulated reference, or an 126 | /// empty string if the reference is null. 127 | /// 128 | public override string ToString() 129 | { 130 | return value == null ? "" : value.ToString(); 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Threading/ThreadPoolWorkItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace MiscUtil.Threading 5 | { 6 | /// 7 | /// Class encapsulating an item of work to be executed in a CustomThreadPool. 8 | /// 9 | public class ThreadPoolWorkItem 10 | { 11 | Delegate target; 12 | /// 13 | /// The target delegate for the work item. This is the delegate 14 | /// which is run when the work item is executed. 15 | /// 16 | public Delegate Target 17 | { 18 | get { return target; } 19 | } 20 | 21 | object[] parameters; 22 | /// 23 | /// The parameters passed to the delegate. This may be null, 24 | /// and will definitely be null if PreserveParameters is false 25 | /// and the work item has started executing. The contents of 26 | /// the returned array should not be changed. 27 | /// 28 | public object[] Parameters 29 | { 30 | get { return parameters; } 31 | } 32 | 33 | int priority; 34 | /// 35 | /// The priority of this work item compared with others. Note 36 | /// that this is entirely independent of the thread priority - it 37 | /// serves only to specify the order of execution of a work item. 38 | /// Items with a higher priority are added ahead of items with a lower 39 | /// priority in the queue. 40 | /// 41 | public int Priority 42 | { 43 | get { return priority; } 44 | } 45 | 46 | bool preserveParameters; 47 | /// 48 | /// Whether or not to preserve parameters during and after 49 | /// execution. If this is true, the parameters are available in 50 | /// the AfterWorkItem and WorkerException events of the containing 51 | /// CustomThreadPool. However, this means that the contents cannot 52 | /// be garbage collected until after the work item has finished 53 | /// executing, which may be costly in some situations. 54 | /// 55 | public bool PreserveParameters 56 | { 57 | get { return preserveParameters; } 58 | } 59 | 60 | object id; 61 | /// 62 | /// The ID of the work item, which may be null. This is provided 63 | /// by the caller when the work item is constructed, and is used 64 | /// for cancellation purposes. 65 | /// 66 | public object ID 67 | { 68 | get { return id; } 69 | } 70 | 71 | /// 72 | /// Creates a new instance of this class. 73 | /// 74 | /// The ID of the work item. May be null. 75 | /// 76 | /// Whether or not the parameter array should be preserved during the work item's 77 | /// execution to allow the information to be retrieved in the WorkerException and 78 | /// AfterWorkItem events. 79 | /// 80 | /// 81 | /// Whether or not the parameter array provided should be cloned. This should be 82 | /// true if the contents of the passed array will be changed by the caller afterwards, 83 | /// but false in the common case of creating the array solely for the purpose of 84 | /// constructing this work item. Note that the values within the array are not cloned 85 | /// - just the array itself. 86 | /// 87 | /// The priority of this work item. 88 | /// 89 | /// The delegate to run when the work item is executed. Must not be null. 90 | /// 91 | /// 92 | /// The parameters to pass to the target delegate. May be null if the delegate 93 | /// takes no parameters. 94 | /// 95 | public ThreadPoolWorkItem (object id, bool preserveParameters, bool cloneParameters, 96 | int priority, Delegate target, params object[] parameters) 97 | { 98 | if (target==null) 99 | { 100 | throw new ArgumentNullException("target"); 101 | } 102 | this.id = id; 103 | this.priority = priority; 104 | this.preserveParameters = preserveParameters; 105 | this.target = target; 106 | if (parameters != null) 107 | { 108 | this.parameters = (cloneParameters ? (object[])parameters.Clone() : parameters); 109 | } 110 | } 111 | 112 | /// 113 | /// Creates a new work item with the given target delegate and parameters. 114 | /// The parameters (if any) are cloned on construction and preserved during 115 | /// the work item's execution. The ID of the constructed work item is null, 116 | /// and the priority is 0. 117 | /// 118 | /// 119 | /// The delegate to run when the work item is executed. Must not be null. 120 | /// 121 | /// 122 | /// The parameters to pass to the target delegate. May be null if the delegate 123 | /// takes no parameters. 124 | /// 125 | public ThreadPoolWorkItem (Delegate target, params object[] parameters) 126 | : this ((object)null, true, true, 0, target, parameters) 127 | { 128 | } 129 | 130 | /// 131 | /// Invokes the work item. 132 | /// 133 | internal void Invoke() 134 | { 135 | object[] p = parameters; 136 | if (!preserveParameters) 137 | { 138 | parameters = null; 139 | } 140 | // Should be faster than dynamic invoke 141 | if (target is ThreadStart) 142 | { 143 | ((ThreadStart)target)(); 144 | } 145 | else 146 | { 147 | target.DynamicInvoke(p); 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Collections/ProjectionComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | using MiscUtil.Extensions; 5 | 6 | namespace MiscUtil.Collections 7 | { 8 | /// 9 | /// Non-generic class to produce instances of the generic class, 10 | /// optionally using type inference. 11 | /// 12 | public static class ProjectionComparer 13 | { 14 | /// 15 | /// Creates an instance of ProjectionComparer using the specified projection. 16 | /// 17 | /// Type parameter for the elements to be compared 18 | /// Type parameter for the keys to be compared, after being projected from the elements 19 | /// Projection to use when determining the key of an element 20 | /// A comparer which will compare elements by projecting each element to its key, and comparing keys 21 | public static ProjectionComparer Create(Func projection) 22 | { 23 | return new ProjectionComparer(projection); 24 | } 25 | 26 | /// 27 | /// Creates an instance of ProjectionComparer using the specified projection. 28 | /// The ignored parameter is solely present to aid type inference. 29 | /// 30 | /// Type parameter for the elements to be compared 31 | /// Type parameter for the keys to be compared, after being projected from the elements 32 | /// Value is ignored - type may be used by type inference 33 | /// Projection to use when determining the key of an element 34 | /// A comparer which will compare elements by projecting each element to its key, and comparing keys 35 | public static ProjectionComparer Create 36 | (TSource ignored, 37 | Func projection) 38 | { 39 | return new ProjectionComparer(projection); 40 | } 41 | 42 | } 43 | 44 | /// 45 | /// Class generic in the source only to produce instances of the 46 | /// doubly generic class, optionally using type inference. 47 | /// 48 | public static class ProjectionComparer 49 | { 50 | /// 51 | /// Creates an instance of ProjectionComparer using the specified projection. 52 | /// 53 | /// Type parameter for the keys to be compared, after being projected from the elements 54 | /// Projection to use when determining the key of an element 55 | /// A comparer which will compare elements by projecting each element to its key, and comparing keys 56 | public static ProjectionComparer Create(Func projection) 57 | { 58 | return new ProjectionComparer(projection); 59 | } 60 | } 61 | 62 | /// 63 | /// Comparer which projects each element of the comparison to a key, and then compares 64 | /// those keys using the specified (or default) comparer for the key type. 65 | /// 66 | /// Type of elements which this comparer will be asked to compare 67 | /// Type of the key projected from the element 68 | public class ProjectionComparer : IComparer 69 | { 70 | readonly Func projection; 71 | readonly IComparer comparer; 72 | 73 | /// 74 | /// Creates a new instance using the specified projection, which must not be null. 75 | /// The default comparer for the projected type is used. 76 | /// 77 | /// Projection to use during comparisons 78 | public ProjectionComparer(Func projection) 79 | : this (projection, null) 80 | { 81 | } 82 | 83 | /// 84 | /// Creates a new instance using the specified projection, which must not be null. 85 | /// 86 | /// Projection to use during comparisons 87 | /// The comparer to use on the keys. May be null, in 88 | /// which case the default comparer will be used. 89 | public ProjectionComparer(Func projection, IComparer comparer) 90 | { 91 | projection.ThrowIfNull("projection"); 92 | this.comparer = comparer ?? Comparer.Default; 93 | this.projection = projection; 94 | } 95 | 96 | /// 97 | /// Compares x and y by projecting them to keys and then comparing the keys. 98 | /// Null values are not projected; they obey the 99 | /// standard comparer contract such that two null values are equal; any null value is 100 | /// less than any non-null value. 101 | /// 102 | public int Compare(TSource x, TSource y) 103 | { 104 | // Don't want to project from nullity 105 | if (x==null && y==null) 106 | { 107 | return 0; 108 | } 109 | if (x==null) 110 | { 111 | return -1; 112 | } 113 | if (y==null) 114 | { 115 | return 1; 116 | } 117 | return comparer.Compare(projection(x), projection(y)); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /ApplicationChooser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Reflection; 5 | 6 | namespace MiscUtil 7 | { 8 | /// 9 | /// Class allowing a user to choose a console application to run, after 10 | /// examining an assembly for all classes containing a static Main method, either 11 | /// parameterless or with a string array parameter. 12 | /// 13 | public class ApplicationChooser 14 | { 15 | const string Keys = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 16 | 17 | /// 18 | /// Displays entry points and prompts the user to choose one. 19 | /// 20 | /// Type within the assembly containing the applications. This type is 21 | /// not included in the list of entry points to run. 22 | /// Arguments to pass in for methods which have a single string[] parameter. 23 | public static void Run(Type type, string[] args) 24 | { 25 | Assembly assembly = type.Assembly; 26 | 27 | List entryPoints = new List(); 28 | foreach (Type candidate in assembly.GetTypes()) 29 | { 30 | if (candidate == type) 31 | { 32 | continue; 33 | } 34 | MethodBase entryPoint = GetEntryPoint(candidate); 35 | if (entryPoint != null) 36 | { 37 | entryPoints.Add(entryPoint); 38 | } 39 | } 40 | 41 | entryPoints.Sort(delegate (MethodBase x, MethodBase y) { return x.DeclaringType.Name.CompareTo(y.DeclaringType.Name); }); 42 | 43 | if (entryPoints.Count == 0) 44 | { 45 | Console.WriteLine("No entry points found. Press return to exit."); 46 | Console.ReadLine(); 47 | return; 48 | } 49 | 50 | for (int i = 0; i < entryPoints.Count; i++) 51 | { 52 | Console.WriteLine("{0}: {1}", Keys[i], 53 | GetEntryPointName(entryPoints[i])); 54 | } 55 | Console.WriteLine(); 56 | Console.Write("Entry point to run? "); 57 | Console.Out.Flush(); 58 | char key = Console.ReadKey().KeyChar; 59 | Console.WriteLine(); 60 | 61 | // "Enter" means "Oops, let's just quit" 62 | if (key == '\r') 63 | { 64 | return; 65 | } 66 | 67 | int entry = Keys.IndexOf(char.ToUpper(key)); 68 | if (entry == -1 || entry >= entryPoints.Count) 69 | { 70 | Console.WriteLine("Invalid choice"); 71 | } 72 | else 73 | { 74 | try 75 | { 76 | MethodBase main = entryPoints[entry]; 77 | main.Invoke(null, main.GetParameters().Length == 0 ? null : new object[] { args }); 78 | } 79 | catch (Exception e) 80 | { 81 | Console.WriteLine("Exception: {0}", e); 82 | } 83 | } 84 | Console.WriteLine(); 85 | Console.WriteLine("Press return to exit."); 86 | Console.ReadLine(); 87 | } 88 | 89 | private static object GetEntryPointName(MethodBase methodBase) 90 | { 91 | Type type = methodBase.DeclaringType; 92 | 93 | object[] descriptions = type.GetCustomAttributes(typeof(DescriptionAttribute), false); 94 | return descriptions.Length == 0 ? 95 | type.Name : 96 | string.Format("{0} [{1}]", type.Name, ((DescriptionAttribute)descriptions[0]).Description); 97 | } 98 | 99 | /// 100 | /// Returns the entry point for a method, or null if no entry points can be used. 101 | /// An entry point taking string[] is preferred to one with no parameters. 102 | /// 103 | internal static MethodBase GetEntryPoint(Type type) 104 | { 105 | if (type.IsGenericTypeDefinition || type.IsGenericType) 106 | { 107 | return null; 108 | } 109 | 110 | BindingFlags anyStatic = BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; 111 | // Can't use GetMethod directly as then we can't ignore generic methods :( 112 | 113 | MethodInfo[] methods = type.GetMethods(anyStatic); 114 | 115 | MethodInfo parameterless = null; 116 | MethodInfo stringArrayParameter = null; 117 | 118 | foreach (MethodInfo method in methods) 119 | { 120 | if (method.Name != "Main") 121 | { 122 | continue; 123 | } 124 | if (method.IsGenericMethod || method.IsGenericMethodDefinition) 125 | { 126 | continue; 127 | } 128 | ParameterInfo[] parameters = method.GetParameters(); 129 | if (parameters.Length == 0) 130 | { 131 | parameterless = method; 132 | } 133 | else 134 | { 135 | if (parameters.Length == 1 && 136 | !parameters[0].IsOut && 137 | !parameters[0].IsOptional && 138 | parameters[0].ParameterType==typeof(string[])) 139 | { 140 | stringArrayParameter = method; 141 | } 142 | } 143 | } 144 | 145 | // Prefer the version with parameters, return null if neither have been found 146 | return stringArrayParameter ?? parameterless; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /Linq/KeyValueTuple.cs: -------------------------------------------------------------------------------- 1 | namespace MiscUtil.Linq 2 | { 3 | /// 4 | /// Generic tuple for a key and a single value 5 | /// 6 | /// The Type of the key 7 | /// The Type of the value 8 | public struct KeyValueTuple 9 | { 10 | readonly TKey key; 11 | readonly T value; 12 | /// 13 | /// The key for the tuple 14 | /// 15 | public TKey Key 16 | { 17 | get { return key; } 18 | } 19 | /// 20 | /// The value for the tuple 21 | /// 22 | public T Value 23 | { 24 | get { return value; } 25 | } 26 | /// 27 | /// Creates a new tuple with the given key and value 28 | /// 29 | public KeyValueTuple(TKey key, T value) 30 | { 31 | this.key = key; 32 | this.value = value; 33 | } 34 | } 35 | /// 36 | /// Generic tuple for a key and a pair of values 37 | /// 38 | /// The Type of the key 39 | /// The Type of the first value 40 | /// The Type of the second value 41 | public struct KeyValueTuple 42 | { 43 | readonly TKey key; 44 | readonly T1 value1; 45 | readonly T2 value2; 46 | /// 47 | /// The key for the tuple 48 | /// 49 | public TKey Key 50 | { 51 | get { return key; } 52 | } 53 | /// 54 | /// The first value 55 | /// 56 | public T1 Value1 57 | { 58 | get { return value1; } 59 | } 60 | /// 61 | /// The second value 62 | /// 63 | public T2 Value2 64 | { 65 | get { return value2; } 66 | } 67 | /// 68 | /// Creates a new tuple with the given key and values 69 | /// 70 | public KeyValueTuple(TKey key, T1 value1, T2 value2) 71 | { 72 | this.key = key; 73 | this.value1 = value1; 74 | this.value2 = value2; 75 | } 76 | } 77 | /// 78 | /// Generic tuple for a key and a trio of values 79 | /// 80 | /// The Type of the key 81 | /// The Type of the first value 82 | /// The Type of the second value 83 | /// The Type of the third value 84 | public struct KeyValueTuple 85 | { 86 | readonly TKey key; 87 | readonly T1 value1; 88 | readonly T2 value2; 89 | readonly T3 value3; 90 | /// 91 | /// The key for the tuple 92 | /// 93 | public TKey Key 94 | { 95 | get { return key; } 96 | } 97 | /// 98 | /// The first value 99 | /// 100 | public T1 Value1 101 | { 102 | get { return value1; } 103 | } 104 | /// 105 | /// The second value 106 | /// 107 | public T2 Value2 108 | { 109 | get { return value2; } 110 | } 111 | /// 112 | /// The third value 113 | /// 114 | public T3 Value3 115 | { 116 | get { return value3; } 117 | } 118 | /// 119 | /// Creates a new tuple with the given key and values 120 | /// 121 | public KeyValueTuple(TKey key, T1 value1, T2 value2, T3 value3) 122 | { 123 | this.key = key; 124 | this.value1 = value1; 125 | this.value2 = value2; 126 | this.value3 = value3; 127 | } 128 | } 129 | /// 130 | /// Generic tuple for a key and a quartet of values 131 | /// 132 | /// The Type of the key 133 | /// The Type of the first value 134 | /// The Type of the second value 135 | /// The Type of the third value 136 | /// The Type of the fourth value 137 | public struct KeyValueTuple 138 | { 139 | readonly TKey key; 140 | readonly T1 value1; 141 | readonly T2 value2; 142 | readonly T3 value3; 143 | readonly T4 value4; 144 | 145 | /// 146 | /// The key for the tuple 147 | /// 148 | public TKey Key 149 | { 150 | get { return key; } 151 | } 152 | /// 153 | /// The first value 154 | /// 155 | public T1 Value1 156 | { 157 | get { return value1; } 158 | } 159 | /// 160 | /// The second value 161 | /// 162 | public T2 Value2 163 | { 164 | get { return value2; } 165 | } 166 | /// 167 | /// The third value 168 | /// 169 | public T3 Value3 170 | { 171 | get { return value3; } 172 | } 173 | /// 174 | /// The fourth value 175 | /// 176 | public T4 Value4 177 | { 178 | get { return value4; } 179 | } 180 | /// 181 | /// Creates a new tuple with the given key and values 182 | /// 183 | public KeyValueTuple(TKey key, T1 value1, T2 value2, T3 value3, T4 value4) 184 | { 185 | this.key = key; 186 | this.value1 = value1; 187 | this.value2 = value2; 188 | this.value3 = value3; 189 | this.value4 = value4; 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /Threading/OrderedLock.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | 3 | namespace MiscUtil.Threading 4 | { 5 | /// 6 | /// Class used for locking, as an alternative to just locking on normal monitors. 7 | /// Allows for timeouts when locking, and each Lock method returns a token which 8 | /// must then be disposed of to release the internal monitor (i.e. to unlock). 9 | /// All properties and methods of this class are thread-safe. 10 | /// 11 | public class OrderedLock : SyncLock 12 | { 13 | #region Fields which aren't backing properties 14 | /// 15 | /// Lock count (incremented with Lock, decremented with Unlock). 16 | /// 17 | int count; 18 | #endregion 19 | 20 | #region Properties 21 | volatile Thread owner; 22 | /// 23 | /// The current owner of the lock, if any. 24 | /// 25 | public Thread Owner 26 | { 27 | get { return owner; } 28 | } 29 | 30 | volatile OrderedLock innerLock; 31 | /// 32 | /// Gets or sets the "inner" lock for this lock. This lock must not be acquired 33 | /// after the inner one, unless it has already been acquired previously. 34 | /// Inner locks are transitive - if A has an inner lock B, and B has 35 | /// an inner lock C, then C is also effectively an inner lock of A. 36 | /// If this property to null, this lock is considered not to have an inner lock. 37 | /// 38 | public OrderedLock InnerLock 39 | { 40 | set 41 | { 42 | innerLock = value; 43 | } 44 | get 45 | { 46 | return innerLock; 47 | } 48 | } 49 | #endregion 50 | 51 | #region Constructors 52 | /// 53 | /// Creates a new lock with no name, and the default timeout specified by DefaultDefaultTimeout. 54 | /// 55 | public OrderedLock() : base() 56 | { 57 | } 58 | 59 | /// 60 | /// Creates a new lock with the specified name, and the default timeout specified by 61 | /// DefaultDefaultTimeout. 62 | /// 63 | /// The name of the new lock 64 | public OrderedLock (string name) : base(name) 65 | { 66 | } 67 | 68 | /// 69 | /// Creates a new lock with no name, and the specified default timeout 70 | /// 71 | /// Default timeout, in milliseconds 72 | public OrderedLock(int defaultTimeout) : base(defaultTimeout) 73 | { 74 | } 75 | 76 | /// 77 | /// Creates a new lock with the specified name, and an 78 | /// infinite default timeout. 79 | /// 80 | /// The name of the new lock 81 | /// 82 | /// Default timeout, in milliseconds. Use Timeout.Infinite 83 | /// for an infinite timeout, or a non-negative number otherwise. 84 | /// 85 | public OrderedLock (string name, int defaultTimeout) : base (name, defaultTimeout) 86 | { 87 | } 88 | #endregion 89 | 90 | #region Deadlock detection 91 | /// 92 | /// Sets the "inner" lock for this lock, returning this lock. This 93 | /// is a convenience method for setting InnerLock as part of a variable 94 | /// declaration. 95 | /// 96 | /// 97 | /// OrderedLock inner = new OrderedLock(); 98 | /// OrderedLock outer = new OrderedLock().SetInnerLock(inner); 99 | /// 100 | /// The inner 101 | /// This lock is returned. 102 | public OrderedLock SetInnerLock (OrderedLock inner) 103 | { 104 | InnerLock = inner; 105 | return this; 106 | } 107 | #endregion 108 | 109 | #region Lock methods 110 | /// 111 | /// Locks the monitor, with the specified timeout. This implementation validates 112 | /// the ordering of locks, and maintains the current owner. 113 | /// 114 | /// The timeout, in milliseconds. Must be Timeout.Infinite, 115 | /// or non-negative. 116 | /// A lock token which should be disposed to release the lock 117 | /// The operation times out. 118 | /// 119 | /// The lock order would be violated if this lock were taken out. (i.e. attempting 120 | /// to acquire the lock could cause deadlock.) 121 | /// 122 | public override LockToken Lock (int timeout) 123 | { 124 | // Check whether we should be allowed to take out this lock, according to 125 | // the inner locks we have. 126 | // Performance note: This would be in a separate method, but the cost of 127 | // making a method call (which can't be inlined in this case) is sufficiently 128 | // high as to make it worth manually inlining. 129 | OrderedLock inner = InnerLock; 130 | // Performance note: This would be a single if statement with shortcutting, 131 | // but fetching the current thread is mildly expensive. 132 | if (inner!=null) 133 | { 134 | Thread currentThread = Thread.CurrentThread; 135 | if (Owner!=currentThread) 136 | { 137 | while (inner != null) 138 | { 139 | if (inner.Owner==currentThread) 140 | { 141 | throw new LockOrderException("Unable to acquire lock {0} as lock {1} is already held", 142 | Name, inner.Name); 143 | } 144 | inner = inner.InnerLock; 145 | } 146 | } 147 | } 148 | 149 | LockToken ret = base.Lock(timeout); 150 | 151 | // Now remember that we've locked, and set the owner if necessary 152 | // Performance note: On a single processor, it is slightly cheaper 153 | // to assign owner every time, without a test. On multiple processor 154 | // boxes, it is cheaper to avoid the volatile write. 155 | if (Interlocked.Increment(ref count)==1) 156 | { 157 | owner = Thread.CurrentThread; 158 | } 159 | return ret; 160 | } 161 | 162 | /// 163 | /// Unlocks the monitor, decreasing the count and setting the owner to null 164 | /// if the count becomes 0. 165 | /// 166 | protected internal override void Unlock() 167 | { 168 | base.Unlock(); 169 | if (Interlocked.Decrement(ref count)==0) 170 | { 171 | owner = null; 172 | } 173 | } 174 | #endregion 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /Collections/ProjectionEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using MiscUtil.Extensions; 4 | 5 | namespace MiscUtil.Collections 6 | { 7 | /// 8 | /// Non-generic class to produce instances of the generic class, 9 | /// optionally using type inference. 10 | /// 11 | public static class ProjectionEqualityComparer 12 | { 13 | /// 14 | /// Creates an instance of ProjectionEqualityComparer using the specified projection. 15 | /// 16 | /// Type parameter for the elements to be compared 17 | /// Type parameter for the keys to be compared, after being projected from the elements 18 | /// Projection to use when determining the key of an element 19 | /// A comparer which will compare elements by projecting each element to its key, and comparing keys 20 | public static ProjectionEqualityComparer Create(Func projection) 21 | { 22 | return new ProjectionEqualityComparer(projection); 23 | } 24 | 25 | /// 26 | /// Creates an instance of ProjectionEqualityComparer using the specified projection. 27 | /// The ignored parameter is solely present to aid type inference. 28 | /// 29 | /// Type parameter for the elements to be compared 30 | /// Type parameter for the keys to be compared, after being projected from the elements 31 | /// Value is ignored - type may be used by type inference 32 | /// Projection to use when determining the key of an element 33 | /// A comparer which will compare elements by projecting each element to its key, and comparing keys 34 | public static ProjectionEqualityComparer Create 35 | (TSource ignored, 36 | Func projection) 37 | { 38 | return new ProjectionEqualityComparer(projection); 39 | } 40 | 41 | } 42 | 43 | /// 44 | /// Class generic in the source only to produce instances of the 45 | /// doubly generic class, optionally using type inference. 46 | /// 47 | public static class ProjectionEqualityComparer 48 | { 49 | /// 50 | /// Creates an instance of ProjectionEqualityComparer using the specified projection. 51 | /// 52 | /// Type parameter for the keys to be compared, after being projected from the elements 53 | /// Projection to use when determining the key of an element 54 | /// A comparer which will compare elements by projecting each element to its key, and comparing keys 55 | public static ProjectionEqualityComparer Create(Func projection) 56 | { 57 | return new ProjectionEqualityComparer(projection); 58 | } 59 | } 60 | 61 | /// 62 | /// Comparer which projects each element of the comparison to a key, and then compares 63 | /// those keys using the specified (or default) comparer for the key type. 64 | /// 65 | /// Type of elements which this comparer will be asked to compare 66 | /// Type of the key projected from the element 67 | public class ProjectionEqualityComparer : IEqualityComparer 68 | { 69 | readonly Func projection; 70 | readonly IEqualityComparer comparer; 71 | 72 | /// 73 | /// Creates a new instance using the specified projection, which must not be null. 74 | /// The default comparer for the projected type is used. 75 | /// 76 | /// Projection to use during comparisons 77 | public ProjectionEqualityComparer(Func projection) 78 | : this(projection, null) 79 | { 80 | } 81 | 82 | /// 83 | /// Creates a new instance using the specified projection, which must not be null. 84 | /// 85 | /// Projection to use during comparisons 86 | /// The comparer to use on the keys. May be null, in 87 | /// which case the default comparer will be used. 88 | public ProjectionEqualityComparer(Func projection, IEqualityComparer comparer) 89 | { 90 | projection.ThrowIfNull("projection"); 91 | this.comparer = comparer ?? EqualityComparer.Default; 92 | this.projection = projection; 93 | } 94 | 95 | /// 96 | /// Compares the two specified values for equality by applying the projection 97 | /// to each value and then using the equality comparer on the resulting keys. Null 98 | /// references are never passed to the projection. 99 | /// 100 | public bool Equals(TSource x, TSource y) 101 | { 102 | if (x == null && y == null) 103 | { 104 | return true; 105 | } 106 | if (x == null || y == null) 107 | { 108 | return false; 109 | } 110 | return comparer.Equals(projection(x), projection(y)); 111 | } 112 | 113 | /// 114 | /// Produces a hash code for the given value by projecting it and 115 | /// then asking the equality comparer to find the hash code of 116 | /// the resulting key. 117 | /// 118 | public int GetHashCode(TSource obj) 119 | { 120 | if (obj == null) 121 | { 122 | throw new ArgumentNullException("obj"); 123 | } 124 | return comparer.GetHashCode(projection(obj)); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Threading/SyncLock.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace MiscUtil.Threading 5 | { 6 | /// 7 | /// Class used for locking, as an alternative to just locking on normal monitors. 8 | /// Allows for timeouts when locking, and each Lock method returns a token which 9 | /// must then be disposed of to release the internal monitor (i.e. to unlock). 10 | /// All properties and methods of this class are thread-safe. 11 | /// 12 | public class SyncLock 13 | { 14 | #region Fields which aren't backing properties 15 | /// 16 | /// Lock for static mutable properties. 17 | /// 18 | static object staticLock = new object(); 19 | #endregion 20 | 21 | #region Properties 22 | /// 23 | /// The default timeout for new instances of this class 24 | /// where the default timeout isn't otherwise specified. 25 | /// Defaults to Timeout.Infinite. 26 | /// 27 | static int defaultDefaultTimeout = Timeout.Infinite; 28 | static int DefaultDefaultTimeout 29 | { 30 | get 31 | { 32 | lock (staticLock) 33 | { 34 | return defaultDefaultTimeout; 35 | } 36 | } 37 | set 38 | { 39 | if (value < Timeout.Infinite) 40 | { 41 | throw new ArgumentOutOfRangeException ("Invalid timeout specified"); 42 | } 43 | lock (staticLock) 44 | { 45 | defaultDefaultTimeout = value; 46 | } 47 | } 48 | } 49 | 50 | int defaultTimeout; 51 | /// 52 | /// The default timeout for the 53 | /// 54 | public int DefaultTimeout 55 | { 56 | get { return defaultTimeout; } 57 | } 58 | 59 | string name; 60 | /// 61 | /// The name of this lock. 62 | /// 63 | public string Name 64 | { 65 | get { return name; } 66 | } 67 | 68 | object monitor = new object(); 69 | /// 70 | /// The internal monitor used for locking. While this 71 | /// is owned by the thread, it can be used for waiting 72 | /// and pulsing in the usual way. Note that manually entering/exiting 73 | /// this monitor could result in the lock malfunctioning. 74 | /// 75 | public object Monitor 76 | { 77 | get { return monitor; } 78 | } 79 | #endregion 80 | 81 | #region Constructors 82 | /// 83 | /// Creates a new lock with no name, and the default timeout specified by DefaultDefaultTimeout. 84 | /// 85 | public SyncLock() : this (null, DefaultDefaultTimeout) 86 | { 87 | } 88 | 89 | /// 90 | /// Creates a new lock with the specified name, and the default timeout specified by 91 | /// DefaultDefaultTimeout. 92 | /// 93 | /// The name of the new lock 94 | public SyncLock (string name) : this (name, DefaultDefaultTimeout) 95 | { 96 | } 97 | 98 | /// 99 | /// Creates a new lock with no name, and the specified default timeout 100 | /// 101 | /// Default timeout, in milliseconds 102 | public SyncLock(int defaultTimeout) : this (null, defaultTimeout) 103 | { 104 | } 105 | 106 | /// 107 | /// Creates a new lock with the specified name, and an 108 | /// infinite default timeout. 109 | /// 110 | /// The name of the new lock 111 | /// 112 | /// Default timeout, in milliseconds. Use Timeout.Infinite 113 | /// for an infinite timeout, or a non-negative number otherwise. 114 | /// 115 | public SyncLock (string name, int defaultTimeout) 116 | { 117 | if (defaultTimeout < Timeout.Infinite) 118 | { 119 | throw new ArgumentOutOfRangeException ("Invalid timeout specified"); 120 | } 121 | if (name==null) 122 | { 123 | name = "Anonymous Lock"; 124 | } 125 | this.name = name; 126 | this.defaultTimeout = defaultTimeout; 127 | } 128 | #endregion 129 | 130 | #region Lock methods 131 | /// 132 | /// Locks the monitor, with the default timeout. 133 | /// 134 | /// A lock token which should be disposed to release the lock 135 | /// The operation times out. 136 | public LockToken Lock() 137 | { 138 | return Lock(defaultTimeout); 139 | } 140 | 141 | /// 142 | /// Locks the monitor, with the specified timeout. 143 | /// 144 | /// The timeout duration. When converted to milliseconds, 145 | /// must be Timeout.Infinite, or non-negative. 146 | /// A lock token which should be disposed to release the lock 147 | /// The operation times out. 148 | public LockToken Lock (TimeSpan timeout) 149 | { 150 | long millis = (long) timeout.TotalMilliseconds; 151 | if (millis < Timeout.Infinite || millis > int.MaxValue) 152 | { 153 | throw new ArgumentOutOfRangeException ("Invalid timeout specified"); 154 | } 155 | return Lock ((int)millis); 156 | } 157 | 158 | /// 159 | /// Locks the monitor, with the specified timeout. Derived classes may override 160 | /// this method to change the behaviour; the other calls to Lock all result in 161 | /// a call to this method. This implementation checks the validity of the timeout, 162 | /// calls Monitor.TryEnter (throwing an exception if appropriate) and returns a 163 | /// new LockToken. 164 | /// 165 | /// The timeout, in milliseconds. Must be Timeout.Infinite, 166 | /// or non-negative. 167 | /// A lock token which should be disposed to release the lock 168 | /// The operation times out. 169 | public virtual LockToken Lock (int timeout) 170 | { 171 | if (timeout < Timeout.Infinite) 172 | { 173 | throw new ArgumentOutOfRangeException ("Invalid timeout specified"); 174 | } 175 | 176 | if (!System.Threading.Monitor.TryEnter(monitor, timeout)) 177 | { 178 | throw new LockTimeoutException ("Failed to acquire lock {0}", name); 179 | } 180 | return new LockToken(this); 181 | } 182 | 183 | /// 184 | /// Unlocks the monitor. This method may be overridden in derived classes 185 | /// to change the behaviour. This implementation simply calls Monitor.Exit. 186 | /// 187 | protected internal virtual void Unlock() 188 | { 189 | System.Threading.Monitor.Exit(monitor); 190 | } 191 | #endregion 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Linq/EditableLookup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using MiscUtil.Extensions; 4 | 5 | namespace MiscUtil.Linq 6 | { 7 | /// 8 | /// Simple non-unique map wrapper 9 | /// 10 | /// 11 | /// ApplyResultSelector (from Lookup[TKey, TElement] is not implemented, 12 | /// since the caller could just as easily (or more-so) use .Select() with 13 | /// a Func[IGrouping[TKey, TElement], TResult], since 14 | /// IGrouping[TKey, TElement] already includes both the "TKey Key" 15 | /// and the IEnumerable[TElement]. 16 | /// 17 | public sealed partial class EditableLookup : ILookup 18 | { 19 | private readonly Dictionary groups; 20 | /// 21 | /// Creates a new EditableLookup using the default key-comparer 22 | /// 23 | public EditableLookup() : this(null) { } 24 | /// 25 | /// Creates a new EditableLookup using the specified key-comparer 26 | /// 27 | /// 28 | public EditableLookup(IEqualityComparer keyComparer) 29 | { 30 | groups = new Dictionary( 31 | keyComparer ?? EqualityComparer.Default); 32 | } 33 | /// 34 | /// Does the lookup contain any value(s) for the given key? 35 | /// 36 | public bool Contains(TKey key) 37 | { 38 | LookupGrouping group; 39 | return groups.TryGetValue(key, out group) ? group.Count > 0 : false; 40 | } 41 | /// 42 | /// Does the lookup the specific key/value pair? 43 | /// 44 | public bool Contains(TKey key, TElement value) 45 | { 46 | LookupGrouping group; 47 | return groups.TryGetValue(key, out group) ? group.Contains(value) : false; 48 | } 49 | /// 50 | /// Adds a key/value pair to the lookup 51 | /// 52 | /// If the value is already present it will be duplicated 53 | public void Add(TKey key, TElement value) 54 | { 55 | LookupGrouping group; 56 | if (!groups.TryGetValue(key, out group)) 57 | { 58 | group = new LookupGrouping(key); 59 | groups.Add(key, group); 60 | } 61 | group.Add(value); 62 | } 63 | /// 64 | /// Adds a range of values against a single key 65 | /// 66 | /// Any values already present will be duplicated 67 | public void AddRange(TKey key, IEnumerable values) 68 | { 69 | values.ThrowIfNull("values"); 70 | LookupGrouping group; 71 | if (!groups.TryGetValue(key, out group)) 72 | { 73 | group = new LookupGrouping(key); 74 | groups.Add(key, group); 75 | } 76 | foreach (TElement value in values) 77 | { 78 | group.Add(value); 79 | } 80 | if (group.Count == 0) 81 | { 82 | groups.Remove(key); // nothing there after all! 83 | } 84 | } 85 | /// 86 | /// Add all key/value pairs from the supplied lookup 87 | /// to the current lookup 88 | /// 89 | /// Any values already present will be duplicated 90 | public void AddRange(ILookup lookup) 91 | { 92 | lookup.ThrowIfNull("lookup"); ; 93 | foreach (IGrouping group in lookup) 94 | { 95 | AddRange(group.Key, group); 96 | } 97 | } 98 | /// 99 | /// Remove all values from the lookup for the given key 100 | /// 101 | /// True if any items were removed, else false 102 | public bool Remove(TKey key) 103 | { 104 | return groups.Remove(key); 105 | } 106 | /// 107 | /// Remove the specific key/value pair from the lookup 108 | /// 109 | /// True if the item was found, else false 110 | public bool Remove(TKey key, TElement value) 111 | { 112 | LookupGrouping group; 113 | if (groups.TryGetValue(key, out group)) 114 | { 115 | bool removed = group.Remove(value); 116 | if (removed && group.Count == 0) 117 | { 118 | groups.Remove(key); 119 | } 120 | return removed; 121 | } 122 | return false; 123 | } 124 | /// 125 | /// Trims the inner data-structure to remove 126 | /// any surplus space 127 | /// 128 | public void TrimExcess() 129 | { 130 | foreach (var group in groups.Values) 131 | { 132 | group.TrimExcess(); 133 | } 134 | } 135 | /// 136 | /// Returns the number of dictinct keys in the lookup 137 | /// 138 | public int Count 139 | { 140 | get { return groups.Count; } 141 | } 142 | 143 | private static readonly IEnumerable Empty = new TElement[0]; 144 | /// 145 | /// Returns the set of values for the given key 146 | /// 147 | public IEnumerable this[TKey key] 148 | { 149 | get 150 | { 151 | LookupGrouping group; 152 | if (groups.TryGetValue(key, out group)) 153 | { 154 | return group; 155 | } 156 | return Empty; 157 | } 158 | } 159 | /// 160 | /// Returns the sequence of keys and their contained values 161 | /// 162 | public IEnumerator> GetEnumerator() 163 | { 164 | foreach (var group in groups.Values) 165 | { 166 | yield return group; 167 | } 168 | } 169 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 170 | { 171 | return GetEnumerator(); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Conversion/DoubleConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace MiscUtil.Conversion 5 | { 6 | /// 7 | /// A class to allow the conversion of doubles to string representations of 8 | /// their exact decimal values. The implementation aims for readability over 9 | /// efficiency. 10 | /// 11 | public class DoubleConverter 12 | { 13 | /// 14 | /// Converts the given double to a string representation of its 15 | /// exact decimal value. 16 | /// 17 | /// The double to convert. 18 | /// A string representation of the double's exact decimal value. 19 | public static string ToExactString (double d) 20 | { 21 | if (double.IsPositiveInfinity(d)) 22 | return "+Infinity"; 23 | if (double.IsNegativeInfinity(d)) 24 | return "-Infinity"; 25 | if (double.IsNaN(d)) 26 | return "NaN"; 27 | 28 | // Translate the double into sign, exponent and mantissa. 29 | long bits = BitConverter.DoubleToInt64Bits(d); 30 | bool negative = (bits < 0); 31 | int exponent = (int) ((bits >> 52) & 0x7ffL); 32 | long mantissa = bits & 0xfffffffffffffL; 33 | 34 | // Subnormal numbers; exponent is effectively one higher, 35 | // but there's no extra normalisation bit in the mantissa 36 | if (exponent==0) 37 | { 38 | exponent++; 39 | } 40 | // Normal numbers; leave exponent as it is but add extra 41 | // bit to the front of the mantissa 42 | else 43 | { 44 | mantissa = mantissa | (1L<<52); 45 | } 46 | 47 | // Bias the exponent. It's actually biased by 1023, but we're 48 | // treating the mantissa as m.0 rather than 0.m, so we need 49 | // to subtract another 52 from it. 50 | exponent -= 1075; 51 | 52 | if (mantissa == 0) 53 | { 54 | return "0"; 55 | } 56 | 57 | /* Normalize */ 58 | while((mantissa & 1) == 0) 59 | { /* i.e., Mantissa is even */ 60 | mantissa >>= 1; 61 | exponent++; 62 | } 63 | 64 | // Construct a new decimal expansion with the mantissa 65 | ArbitraryDecimal ad = new ArbitraryDecimal (mantissa); 66 | 67 | // If the exponent is less than 0, we need to repeatedly 68 | // divide by 2 - which is the equivalent of multiplying 69 | // by 5 and dividing by 10. 70 | if (exponent < 0) 71 | { 72 | for (int i=0; i < -exponent; i++) 73 | ad.MultiplyBy(5); 74 | ad.Shift(-exponent); 75 | } 76 | // Otherwise, we need to repeatedly multiply by 2 77 | else 78 | { 79 | for (int i=0; i < exponent; i++) 80 | ad.MultiplyBy(2); 81 | } 82 | 83 | // Finally, return the string with an appropriate sign 84 | if (negative) 85 | return "-"+ad.ToString(); 86 | else 87 | return ad.ToString(); 88 | } 89 | 90 | /// 91 | /// Private class used for manipulating sequences of decimal digits. 92 | /// 93 | class ArbitraryDecimal 94 | { 95 | /// Digits in the decimal expansion, one byte per digit 96 | byte[] digits; 97 | /// 98 | /// How many digits are *after* the decimal point 99 | /// 100 | int decimalPoint=0; 101 | 102 | /// 103 | /// Constructs an arbitrary decimal expansion from the given long. 104 | /// The long must not be negative. 105 | /// 106 | internal ArbitraryDecimal (long x) 107 | { 108 | string tmp = x.ToString(CultureInfo.InvariantCulture); 109 | digits = new byte[tmp.Length]; 110 | for (int i=0; i < tmp.Length; i++) 111 | digits[i] = (byte) (tmp[i]-'0'); 112 | Normalize(); 113 | } 114 | 115 | /// 116 | /// Multiplies the current expansion by the given amount, which should 117 | /// only be 2 or 5. 118 | /// 119 | internal void MultiplyBy(int amount) 120 | { 121 | byte[] result = new byte[digits.Length+1]; 122 | for (int i=digits.Length-1; i >= 0; i--) 123 | { 124 | int resultDigit = digits[i]*amount+result[i+1]; 125 | result[i]=(byte)(resultDigit/10); 126 | result[i+1]=(byte)(resultDigit%10); 127 | } 128 | if (result[0] != 0) 129 | { 130 | digits=result; 131 | } 132 | else 133 | { 134 | Array.Copy (result, 1, digits, 0, digits.Length); 135 | } 136 | Normalize(); 137 | } 138 | 139 | /// 140 | /// Shifts the decimal point; a negative value makes 141 | /// the decimal expansion bigger (as fewer digits come after the 142 | /// decimal place) and a positive value makes the decimal 143 | /// expansion smaller. 144 | /// 145 | internal void Shift (int amount) 146 | { 147 | decimalPoint += amount; 148 | } 149 | 150 | /// 151 | /// Removes leading/trailing zeroes from the expansion. 152 | /// 153 | internal void Normalize() 154 | { 155 | int first; 156 | for (first=0; first < digits.Length; first++) 157 | if (digits[first]!=0) 158 | break; 159 | int last; 160 | for (last=digits.Length-1; last >= 0; last--) 161 | if (digits[last]!=0) 162 | break; 163 | 164 | if (first==0 && last==digits.Length-1) 165 | return; 166 | 167 | byte[] tmp = new byte[last-first+1]; 168 | for (int i=0; i < tmp.Length; i++) 169 | tmp[i]=digits[i+first]; 170 | 171 | decimalPoint -= digits.Length-(last+1); 172 | digits=tmp; 173 | } 174 | 175 | /// 176 | /// Converts the value to a proper decimal string representation. 177 | /// 178 | public override String ToString() 179 | { 180 | char[] digitString = new char[digits.Length]; 181 | for (int i=0; i < digits.Length; i++) 182 | digitString[i] = (char)(digits[i]+'0'); 183 | 184 | // Simplest case - nothing after the decimal point, 185 | // and last real digit is non-zero, eg value=35 186 | if (decimalPoint==0) 187 | { 188 | return new string (digitString); 189 | } 190 | 191 | // Fairly simple case - nothing after the decimal 192 | // point, but some 0s to add, eg value=350 193 | if (decimalPoint < 0) 194 | { 195 | return new string (digitString)+ 196 | new string ('0', -decimalPoint); 197 | } 198 | 199 | // Nothing before the decimal point, eg 0.035 200 | if (decimalPoint >= digitString.Length) 201 | { 202 | return "0."+ 203 | new string ('0',(decimalPoint-digitString.Length))+ 204 | new string (digitString); 205 | } 206 | 207 | // Most complicated case - part of the string comes 208 | // before the decimal point, part comes after it, 209 | // eg 3.5 210 | return new string (digitString, 0, 211 | digitString.Length-decimalPoint)+ 212 | "."+ 213 | new string (digitString, 214 | digitString.Length-decimalPoint, 215 | decimalPoint); 216 | } 217 | } 218 | } 219 | } -------------------------------------------------------------------------------- /Linq/Extensions/TypeExt.cs: -------------------------------------------------------------------------------- 1 | #if DOTNET35 2 | using System; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using System.Text; 6 | using MiscUtil.Extensions; 7 | 8 | namespace MiscUtil.Linq.Extensions 9 | { 10 | /// 11 | /// Provides extension methods to System.Type to provide simple 12 | /// and efficient access to delegates representing reflection 13 | /// operations. 14 | /// 15 | public static class TypeExt 16 | { 17 | #region Ctor 18 | 19 | private static ConstructorInfo GetConstructor(Type type, params Type[] argumentTypes) 20 | { 21 | type.ThrowIfNull("type"); 22 | argumentTypes.ThrowIfNull("argumentTypes"); 23 | 24 | ConstructorInfo ci = type.GetConstructor(argumentTypes); 25 | if (ci == null) 26 | { 27 | StringBuilder sb = new StringBuilder(); 28 | sb.Append(type.Name).Append(" has no ctor("); 29 | for (int i = 0; i < argumentTypes.Length; i++) 30 | { 31 | if(i > 0) { 32 | sb.Append(','); 33 | } 34 | sb.Append(argumentTypes[i].Name); 35 | } 36 | sb.Append(')'); 37 | throw new InvalidOperationException(sb.ToString()); 38 | } 39 | return ci; 40 | } 41 | /// 42 | /// Obtains a delegate to invoke a parameterless constructor 43 | /// 44 | /// The base/interface type to yield as the 45 | /// new value; often object except for factory pattern implementations 46 | /// The Type to be created 47 | /// A delegate to the constructor if found, else null 48 | public static Func Ctor(this Type type) 49 | { 50 | ConstructorInfo ci = GetConstructor(type, Type.EmptyTypes); 51 | return Expression.Lambda>( 52 | Expression.New(ci)).Compile(); 53 | } 54 | /// 55 | /// Obtains a delegate to invoke a constructor which takes a parameter 56 | /// 57 | /// The type of the constructor parameter 58 | /// The base/interface type to yield as the 59 | /// new value; often object except for factory pattern implementations 60 | /// The Type to be created 61 | /// A delegate to the constructor if found, else null 62 | public static Func 63 | Ctor(this Type type) 64 | { 65 | ConstructorInfo ci = GetConstructor(type, typeof(TArg1)); 66 | ParameterExpression 67 | param1 = Expression.Parameter(typeof(TArg1), "arg1"); 68 | 69 | return Expression.Lambda>( 70 | Expression.New(ci, param1), param1).Compile(); 71 | } 72 | /// 73 | /// Obtains a delegate to invoke a constructor with multiple parameters 74 | /// 75 | /// The type of the first constructor parameter 76 | /// The type of the second constructor parameter 77 | /// The base/interface type to yield as the 78 | /// new value; often object except for factory pattern implementations 79 | /// The Type to be created 80 | /// A delegate to the constructor if found, else null 81 | public static Func 82 | Ctor(this Type type) 83 | { 84 | ConstructorInfo ci = GetConstructor(type, typeof(TArg1), typeof(TArg2)); 85 | ParameterExpression 86 | param1 = Expression.Parameter(typeof(TArg1), "arg1"), 87 | param2 = Expression.Parameter(typeof(TArg2), "arg2"); 88 | 89 | return Expression.Lambda>( 90 | Expression.New(ci, param1, param2), param1, param2).Compile(); 91 | } 92 | /// 93 | /// Obtains a delegate to invoke a constructor with multiple parameters 94 | /// 95 | /// The type of the first constructor parameter 96 | /// The type of the second constructor parameter 97 | /// The type of the third constructor parameter 98 | /// The base/interface type to yield as the 99 | /// new value; often object except for factory pattern implementations 100 | /// The Type to be created 101 | /// A delegate to the constructor if found, else null 102 | public static Func 103 | Ctor(this Type type) 104 | { 105 | ConstructorInfo ci = GetConstructor(type, typeof(TArg1), typeof(TArg2), typeof(TArg3)); 106 | ParameterExpression 107 | param1 = Expression.Parameter(typeof(TArg1), "arg1"), 108 | param2 = Expression.Parameter(typeof(TArg2), "arg2"), 109 | param3 = Expression.Parameter(typeof(TArg3), "arg3"); 110 | 111 | return Expression.Lambda>( 112 | Expression.New(ci, param1, param2, param3), 113 | param1, param2, param3).Compile(); 114 | } 115 | /// 116 | /// Obtains a delegate to invoke a constructor with multiple parameters 117 | /// 118 | /// The type of the first constructor parameter 119 | /// The type of the second constructor parameter 120 | /// The type of the third constructor parameter 121 | /// The type of the fourth constructor parameter 122 | /// The base/interface type to yield as the 123 | /// new value; often object except for factory pattern implementations 124 | /// The Type to be created 125 | /// A delegate to the constructor if found, else null 126 | public static Func 127 | Ctor(this Type type) 128 | { 129 | ConstructorInfo ci = GetConstructor(type, typeof(TArg1), typeof(TArg2), typeof(TArg3), typeof(TArg4)); 130 | ParameterExpression 131 | param1 = Expression.Parameter(typeof(TArg1), "arg1"), 132 | param2 = Expression.Parameter(typeof(TArg2), "arg2"), 133 | param3 = Expression.Parameter(typeof(TArg3), "arg3"), 134 | param4 = Expression.Parameter(typeof(TArg4), "arg4"); 135 | 136 | return Expression.Lambda>( 137 | Expression.New(ci, param1, param2, param3, param4), 138 | param1, param2, param3, param4).Compile(); 139 | } 140 | #endregion 141 | 142 | } 143 | } 144 | #endif -------------------------------------------------------------------------------- /Threading/ThreadController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace MiscUtil.Threading 5 | { 6 | /// 7 | /// Class designed to control a worker thread (co-operatively). 8 | /// 9 | public class ThreadController 10 | { 11 | #region Fields not related to properties 12 | /// 13 | /// Lock used throughout for all state management. 14 | /// (This is unrelated to the "state" variable.) 15 | /// 16 | readonly object stateLock = new object(); 17 | 18 | /// 19 | /// The delegate to be invoked when the thread is started. 20 | /// 21 | ControlledThreadStart starter; 22 | 23 | /// 24 | /// State to pass to the "starter" delegate when the thread is started. 25 | /// This reference is discarded when the new thread is started, so 26 | /// it won't prevent garbage collection. 27 | /// 28 | object state; 29 | #endregion 30 | 31 | #region Properties 32 | bool started; 33 | /// 34 | /// Whether the thread has been started. A thread can only 35 | /// be started once. 36 | /// 37 | public bool Started 38 | { 39 | get 40 | { 41 | lock(stateLock) 42 | { 43 | return started; 44 | } 45 | } 46 | } 47 | 48 | Thread thread; 49 | /// 50 | /// Thread being controlled. May be null if it hasn't 51 | /// been created yet. 52 | /// 53 | public Thread Thread 54 | { 55 | get 56 | { 57 | lock (stateLock) 58 | { 59 | return thread; 60 | } 61 | } 62 | } 63 | 64 | bool stopping; 65 | /// 66 | /// Whether or not the thread is stopping. This may be used 67 | /// by the thread itself to test whether or not to stop, as 68 | /// well as by clients checking status. To see whether the 69 | /// thread has actually finished or not, use the IsAlive 70 | /// property of the thread itself. 71 | /// 72 | public bool Stopping 73 | { 74 | get 75 | { 76 | lock (stateLock) 77 | { 78 | return stopping; 79 | } 80 | } 81 | } 82 | #endregion 83 | 84 | #region Events 85 | ExceptionHandler exceptionDelegate; 86 | /// 87 | /// Event raised if the controlled thread throws an unhandled exception. 88 | /// The exception is not propagated beyond the controller by default, however 89 | /// by adding an ExceptionHandler which simply rethrows the exception, 90 | /// it will propagate. Note that in this case any further ExceptionHandlers 91 | /// added after the propagating one will not be executed. This event is 92 | /// raised in the worker thread. 93 | /// 94 | public event ExceptionHandler Exception 95 | { 96 | add 97 | { 98 | lock (stateLock) 99 | { 100 | exceptionDelegate += value; 101 | } 102 | } 103 | remove 104 | { 105 | lock (stateLock) 106 | { 107 | exceptionDelegate -= value; 108 | } 109 | } 110 | } 111 | 112 | ThreadProgress finishedDelegate; 113 | /// 114 | /// Event raised when the thread has finished and all exception handlers 115 | /// have executed (if an exception was raised). Note that this event is 116 | /// raised even if one of the exception handlers propagates the exception 117 | /// up to the top level. This event is raised in the worker thread. 118 | /// 119 | public event ThreadProgress Finished 120 | { 121 | add 122 | { 123 | lock (stateLock) 124 | { 125 | finishedDelegate += value; 126 | } 127 | } 128 | remove 129 | { 130 | lock (stateLock) 131 | { 132 | finishedDelegate -= value; 133 | } 134 | } 135 | } 136 | 137 | ThreadProgress stopRequestedDelegate; 138 | /// 139 | /// Event raised when a stop is requested. Worker threads 140 | /// may register for this event to allow them to respond to 141 | /// stop requests in a timely manner. The event is raised 142 | /// in the thread which calls the Stop method. 143 | /// 144 | public event ThreadProgress StopRequested 145 | { 146 | add 147 | { 148 | lock (stateLock) 149 | { 150 | stopRequestedDelegate += value; 151 | } 152 | } 153 | remove 154 | { 155 | lock (stateLock) 156 | { 157 | stopRequestedDelegate -= value; 158 | } 159 | } 160 | } 161 | #endregion 162 | 163 | #region Constructors 164 | /// 165 | /// Creates a new controller. 166 | /// 167 | /// The delegate to invoke when the thread is started. 168 | /// Must not be null. 169 | /// The state to pass to the delegate. May be null. 170 | public ThreadController(ControlledThreadStart starter, object state) 171 | { 172 | if (starter==null) 173 | { 174 | throw new ArgumentNullException("starter"); 175 | } 176 | this.starter = starter; 177 | this.state = state; 178 | } 179 | 180 | /// 181 | /// Creates a new controller without specifying a state object to 182 | /// pass when the delegate is invoked. 183 | /// 184 | /// The delegate to invoke when the thread is started. 185 | public ThreadController(ControlledThreadStart starter) : this (starter, null) 186 | { 187 | } 188 | #endregion 189 | 190 | #region Controlling methods 191 | /// 192 | /// Creates the thread to later be started. This enables 193 | /// properties of the thread to be manipulated before the thread 194 | /// is started. 195 | /// 196 | /// The thread has already been created. 197 | public void CreateThread() 198 | { 199 | lock (stateLock) 200 | { 201 | if (thread != null) 202 | { 203 | throw new InvalidOperationException("Thread has already been created"); 204 | } 205 | thread = new Thread(new ThreadStart(RunTask)); 206 | } 207 | } 208 | 209 | /// 210 | /// Starts the task in a separate thread, creating it if it hasn't already been 211 | /// created with the CreateThread method. 212 | /// 213 | /// The thread has already been started. 214 | public void Start() 215 | { 216 | lock (stateLock) 217 | { 218 | if (started) 219 | { 220 | throw new InvalidOperationException("Thread has already been created"); 221 | } 222 | if (thread==null) 223 | { 224 | thread = new Thread(new ThreadStart(RunTask)); 225 | } 226 | thread.Start(); 227 | started = true; 228 | } 229 | } 230 | 231 | /// 232 | /// Tell the thread being controlled by this controller to stop. 233 | /// This call does not throw an exception if the thread hasn't been 234 | /// created, or has already been told to stop - it is therefore safe 235 | /// to call at any time, regardless of other information about the 236 | /// state of the controller. Depending on the way in which the controlled 237 | /// thread is running, it may not take notice of the request to stop 238 | /// for some time. 239 | /// 240 | public void Stop() 241 | { 242 | lock (stateLock) 243 | { 244 | stopping = true; 245 | } 246 | ThreadProgress handler; 247 | lock (stateLock) 248 | { 249 | handler = stopRequestedDelegate; 250 | } 251 | if (handler != null) 252 | { 253 | handler(this); 254 | } 255 | } 256 | #endregion 257 | 258 | #region Private methods 259 | /// 260 | /// Runs the task specified by starter, catching exceptions and propagating them 261 | /// to the Exception event. 262 | /// 263 | void RunTask() 264 | { 265 | try 266 | { 267 | // Allow state to be garbage collected during execution 268 | object stateTmp = state; 269 | state = null; 270 | starter(this, stateTmp); 271 | } 272 | catch (Exception e) 273 | { 274 | ExceptionHandler handler; 275 | lock (stateLock) 276 | { 277 | handler = exceptionDelegate; 278 | } 279 | if (handler != null) 280 | { 281 | handler(this, e); 282 | } 283 | } 284 | finally 285 | { 286 | ThreadProgress handler; 287 | lock (stateLock) 288 | { 289 | handler = finishedDelegate; 290 | } 291 | if (handler != null) 292 | { 293 | handler(this); 294 | } 295 | } 296 | } 297 | #endregion 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /Linq/Extensions/DataProducerExt.Conversion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using MiscUtil.Extensions; 6 | 7 | namespace MiscUtil.Linq.Extensions 8 | { 9 | public static partial class DataProducerExt 10 | { 11 | /// 12 | /// Converts an IDataProducer into an IFuture[IEnumerable]. The results 13 | /// are buffered in memory (as a list), so be warned that this loses the "streaming" 14 | /// nature of most of the IDataProducer extension methods. The "future" nature of 15 | /// the result ensures that all results are produced before the enumeration can take 16 | /// place. 17 | /// 18 | /// This will force all values to be buffered 19 | public static IFuture> AsFutureEnumerable(this IDataProducer source) 20 | { 21 | source.ThrowIfNull("source"); 22 | 23 | Future> ret = new Future>(); 24 | 25 | List list = new List(); 26 | source.DataProduced += value => list.Add(value); 27 | source.EndOfData += () => ret.Value = list; 28 | 29 | return ret; 30 | } 31 | 32 | /// 33 | /// Converts an IDataProducer into an IEnumerable. The results 34 | /// are buffered in memory (as a list), so be warned that this loses the "streaming" 35 | /// nature of most of the IDataProducer extension methods. The list is returned 36 | /// immediately, but further data productions add to it. You must therefore be careful 37 | /// when the list is used - it is a good idea to only use it after all data has been 38 | /// produced. 39 | /// 40 | /// This will force all values to be buffered 41 | public static IEnumerable AsEnumerable(this IDataProducer source) 42 | { 43 | source.ThrowIfNull("source"); 44 | 45 | return source.ToList(); 46 | } 47 | 48 | /// 49 | /// Converts an IDataProducer into a list. An empty list is returned immediately, 50 | /// and any results produced are added to it. 51 | /// 52 | /// This will force all values to be buffered 53 | public static List ToList(this IDataProducer source) 54 | { 55 | source.ThrowIfNull("source"); 56 | 57 | List list = new List(); 58 | source.DataProduced += value => list.Add(value); 59 | 60 | return list; 61 | } 62 | 63 | /// 64 | /// Converts an IDataProducer into a future array. 65 | /// 66 | /// This will force all values to be buffered 67 | public static IFuture ToFutureArray(this IDataProducer source) 68 | { 69 | source.ThrowIfNull("source"); 70 | 71 | Future ret = new Future(); 72 | List list = source.ToList(); 73 | 74 | source.EndOfData += () => ret.Value = list.ToArray(); 75 | 76 | return ret; 77 | } 78 | 79 | /// 80 | /// Converts an IDataProducer into a lookup. 81 | /// 82 | /// A transform function to produce a result element value from each element. 83 | /// A function to extract a key from each element. 84 | /// Used to compare keys. 85 | /// The data source. 86 | /// This will force all values to be buffered 87 | public static ILookup ToLookup( 88 | this IDataProducer source, 89 | Func keySelector, Func elementSelector, IEqualityComparer keyComparer) 90 | { 91 | source.ThrowIfNull("source"); 92 | keySelector.ThrowIfNull("keySelector"); 93 | elementSelector.ThrowIfNull("elementSelector"); 94 | keyComparer.ThrowIfNull("keyComparer"); 95 | 96 | EditableLookup lookup = new EditableLookup(keyComparer); 97 | source.DataProduced += t => lookup.Add(keySelector(t), elementSelector(t)); 98 | source.EndOfData += () => lookup.TrimExcess(); 99 | return lookup; 100 | } 101 | 102 | /// 103 | /// Converts an IDataProducer into a lookup. 104 | /// 105 | /// A function to extract a key from each element. 106 | /// The data source. 107 | /// This will force all values to be buffered 108 | public static ILookup ToLookup( 109 | this IDataProducer source, 110 | Func keySelector) 111 | { 112 | return ToLookup(source, keySelector, t => t, EqualityComparer.Default); 113 | } 114 | /// 115 | /// Converts an IDataProducer into a lookup. 116 | /// 117 | /// A function to extract a key from each element. 118 | /// Used to compare keys. 119 | /// The data source. 120 | /// This will force all values to be buffered 121 | public static ILookup ToLookup( 122 | this IDataProducer source, 123 | Func keySelector, IEqualityComparer keyComparer) 124 | { 125 | return ToLookup(source, keySelector, t => t, keyComparer); 126 | } 127 | /// 128 | /// Converts an IDataProducer into a lookup. 129 | /// 130 | /// A transform function to produce a result element value from each element. 131 | /// A function to extract a key from each element. 132 | /// The data source. 133 | /// This will force all values to be buffered 134 | public static ILookup ToLookup( 135 | this IDataProducer source, 136 | Func keySelector, Func elementSelector) 137 | { 138 | return ToLookup(source, keySelector, elementSelector, EqualityComparer.Default); 139 | } 140 | 141 | /// 142 | /// Converts an IDataProducer into a dictionary. 143 | /// 144 | /// A transform function to produce a result element value from each element. 145 | /// A function to extract a key from each element. 146 | /// Used to compare keys. 147 | /// The data source. 148 | /// This will force all values to be buffered 149 | public static IDictionary ToDictionary( 150 | this IDataProducer source, 151 | Func keySelector, Func elementSelector, IEqualityComparer keyComparer) 152 | { 153 | source.ThrowIfNull("source"); 154 | keySelector.ThrowIfNull("keySelector"); 155 | elementSelector.ThrowIfNull("elementSelector"); 156 | keyComparer.ThrowIfNull("keyComparer"); 157 | 158 | Dictionary dict = new Dictionary(keyComparer); 159 | source.DataProduced += t => dict.Add(keySelector(t), elementSelector(t)); 160 | return dict; 161 | } 162 | /// 163 | /// Converts an IDataProducer into a dictionary. 164 | /// 165 | /// A function to extract a key from each element. 166 | /// The data source. 167 | /// This will force all values to be buffered 168 | public static IDictionary ToDictionary( 169 | this IDataProducer source, 170 | Func keySelector) 171 | { 172 | return ToDictionary(source, keySelector, t => t, EqualityComparer.Default); 173 | } 174 | /// 175 | /// Converts an IDataProducer into a dictionary. 176 | /// 177 | /// A function to extract a key from each element. 178 | /// Used to compare keys. 179 | /// The data source. 180 | /// This will force all values to be buffered 181 | public static IDictionary ToDictionary( 182 | this IDataProducer source, 183 | Func keySelector, IEqualityComparer keyComparer) 184 | { 185 | return ToDictionary(source, keySelector, t => t, keyComparer); 186 | } 187 | /// 188 | /// Converts an IDataProducer into a dictionary. 189 | /// 190 | /// A transform function to produce a result element value from each element. 191 | /// A function to extract a key from each element. 192 | /// The data source. 193 | /// This will force all values to be buffered 194 | public static IDictionary ToDictionary( 195 | this IDataProducer source, 196 | Func keySelector, Func elementSelector) 197 | { 198 | return ToDictionary(source, keySelector, elementSelector, EqualityComparer.Default); 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /MiscUtil.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.30729 7 | 2.0 8 | {4FC1D5F4-661F-48DC-B397-4CDCB7D61ABE} 9 | Library 10 | Properties 11 | MiscUtil 12 | MiscUtil 13 | 14 | 15 | 3.5 16 | 17 | 18 | publish\ 19 | true 20 | Disk 21 | false 22 | Foreground 23 | 7 24 | Days 25 | false 26 | false 27 | true 28 | 0 29 | 1.0.0.%2a 30 | false 31 | false 32 | true 33 | v3.5 34 | true 35 | ..\MiscUtil.snk 36 | 37 | 38 | true 39 | full 40 | false 41 | bin\Debug\ 42 | TRACE;DEBUG;DOTNET35 43 | prompt 44 | 4 45 | 46 | 47 | pdbonly 48 | true 49 | bin\Release\ 50 | TRACE;DOTNET35 51 | prompt 52 | 4 53 | false 54 | bin\Release\MiscUtil.xml 55 | 56 | 57 | bin\Release 2.0\ 58 | TRACE 59 | bin\Release 2.0\MiscUtil.xml 60 | true 61 | pdbonly 62 | AnyCPU 63 | true 64 | GlobalSuppressions.cs 65 | prompt 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 3.5 75 | global 76 | 77 | 78 | 3.5 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | EditableLookup.cs 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | DataProducerExt.cs 143 | 144 | 145 | DataProducerExt.cs 146 | 147 | 148 | DataProducerExt.cs 149 | 150 | 151 | DataProducerExt.cs 152 | 153 | 154 | DataProducerExt.cs 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | False 191 | .NET Framework 2.0 %28x86%29 192 | true 193 | 194 | 195 | 196 | 197 | False 198 | .NET Framework 3.0 %28x86%29 199 | false 200 | 201 | 202 | False 203 | .NET Framework 3.5 204 | false 205 | 206 | 207 | 208 | 215 | --------------------------------------------------------------------------------