/Collections/Generic/CircularQueue.cs |
@@ -0,0 +1,232 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System.Collections.Generic; |
|
namespace wasSharp.Collections.Generic |
{ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2016 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// A circular queue implementation based on linked lists. |
/// </summary> |
/// <typeparam name="T">the type of value to store</typeparam> |
public class CircularQueue<T> |
{ |
private readonly LinkedList<T> Store = new LinkedList<T>(); |
private LinkedListNode<T> CurrentNode; |
|
private readonly object SyncRoot = new object(); |
|
public CircularQueue() |
{ |
} |
|
public CircularQueue(IEnumerable<T> items) |
{ |
Enqueue(items); |
} |
|
public CircularQueue(CircularQueue<T> queue) |
{ |
lock (SyncRoot) |
{ |
lock (queue.SyncRoot) |
{ |
foreach (var item in queue.Items) |
{ |
Store.AddLast(item); |
} |
|
if (CurrentNode == null) |
CurrentNode = Store.First; |
} |
} |
} |
|
public int Count |
{ |
get |
{ |
lock (SyncRoot) |
{ |
return Store.Count; |
} |
} |
} |
|
private T GetNext |
{ |
get |
{ |
lock (SyncRoot) |
{ |
if (CurrentNode == null) |
return default(T); |
|
var value = CurrentNode.Value; |
|
switch (CurrentNode.Next != null) |
{ |
case true: |
CurrentNode = CurrentNode.Next; |
break; |
default: |
CurrentNode = Store.First; |
break; |
} |
|
return value; |
} |
} |
} |
|
public IEnumerable<T> Items |
{ |
get |
{ |
lock (SyncRoot) |
{ |
if (CurrentNode == null) |
yield break; |
|
var node = CurrentNode; |
do |
{ |
yield return node.Value; |
node = node.Next; |
} while (node != null); |
} |
} |
} |
|
public void Enqueue(IEnumerable<T> items) |
{ |
lock (SyncRoot) |
{ |
foreach (var i in items) |
Store.AddLast(i); |
|
if (CurrentNode == null) |
CurrentNode = Store.First; |
} |
} |
|
public void Enqueue(T item) |
{ |
lock (SyncRoot) |
{ |
Store.AddLast(item); |
|
if (CurrentNode == null) |
CurrentNode = Store.First; |
} |
} |
|
public T Dequeue() |
{ |
lock (SyncRoot) |
{ |
return GetNext; |
} |
} |
|
public IEnumerable<T> Dequeue(int count = 1) |
{ |
if (count <= 0) |
yield break; |
|
lock (SyncRoot) |
{ |
if (CurrentNode == null) |
yield break; |
|
do |
{ |
yield return GetNext; |
} while (--count != 0); |
} |
} |
|
public void Clear() |
{ |
lock (SyncRoot) |
{ |
Store.Clear(); |
|
CurrentNode = null; |
} |
} |
|
public bool Contains(T item) |
{ |
lock (SyncRoot) |
{ |
return Store.Contains(item); |
} |
} |
|
public void CopyTo(T[] array, int arrayIndex) |
{ |
lock (SyncRoot) |
{ |
Store.CopyTo(array, arrayIndex); |
} |
} |
|
public bool Remove(T item) |
{ |
lock (SyncRoot) |
{ |
var node = Store.Find(item); |
if (node == null) |
return false; |
if (CurrentNode.Equals(node)) |
{ |
switch (node.Next != null) |
{ |
case true: |
CurrentNode = node.Next; |
break; |
default: |
CurrentNode = Store.First; |
break; |
} |
} |
Store.Remove(node); |
return true; |
} |
} |
|
public void RemoveAll(IEnumerable<T> items) |
{ |
var itemSet = new HashSet<T>(items); |
lock (SyncRoot) |
{ |
var node = CurrentNode; |
do |
{ |
var next = node.Next; |
if (itemSet.Contains(node.Value)) |
{ |
switch (next != null) |
{ |
case true: |
CurrentNode = next; |
break; |
default: |
CurrentNode = Store.First; |
break; |
} |
Store.Remove(node); |
} |
node = next; |
} while (node != null); |
} |
} |
} |
} |
/Collections/Generic/RangeCollection.cs |
@@ -0,0 +1,58 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System.Collections; |
using System.Collections.Generic; |
using System.Linq; |
|
namespace wasSharp.Collections.Generic |
{ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2016 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// A collection that maps ranges to values with O(1) complexity |
/// lookups and O(n) insertions. |
/// </summary> |
/// <typeparam name="T">the type of value to store</typeparam> |
public class RangeCollection<T> : IEnumerable |
{ |
private readonly Dictionary<int, T> map; |
|
public RangeCollection(int min, int max) |
{ |
map = new Dictionary<int, T>(max - min); |
} |
|
public T this[int x] |
{ |
get |
{ |
T value; |
return map.TryGetValue(x, out value) ? value : default(T); |
} |
} |
|
public IEnumerator GetEnumerator() |
{ |
return ((IEnumerable) map).GetEnumerator(); |
} |
|
/// <summary> |
/// Map a value to a range. |
/// </summary> |
/// <param name="Value">the value for the range</param> |
/// <param name="min">the minimal range</param> |
/// <param name="max">the maximal range</param> |
public void Add(T Value, int min, int max) |
{ |
foreach (var i in Enumerable.Range(min, max - min + 1)) |
{ |
map.Add(i, Value); |
} |
} |
} |
} |
/Collections/Generic/SerializableDictionary.cs |
@@ -0,0 +1,129 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Collections.Generic; |
using System.IO; |
using System.Xml; |
using System.Xml.Schema; |
using System.Xml.Serialization; |
|
namespace wasSharp.Collections.Generic |
{ |
/// <summary> |
/// A serializable dictionary class. |
/// </summary> |
/// <typeparam name="TKey">the key</typeparam> |
/// <typeparam name="TValue">the value</typeparam> |
[XmlRoot("Dictionary")] |
public class SerializableDictionary<TKey, TValue> |
: Dictionary<TKey, TValue>, IXmlSerializable |
{ |
#region IXmlSerializable Members |
|
public SerializableDictionary(IEnumerable<KeyValuePair<TKey, TValue>> kvp) |
{ |
foreach (var i in kvp) |
{ |
Add(i.Key, i.Value); |
} |
} |
|
public SerializableDictionary() |
{ |
} |
|
/// <summary> |
/// Deep-clones the serializable dictionary. |
/// </summary> |
/// <returns>a deep clone of the original dictionary</returns> |
public SerializableDictionary<TKey, TValue> Clone() |
{ |
SerializableDictionary<TKey, TValue> clone; |
try |
{ |
using (var writer = new MemoryStream()) |
{ |
var serializer = |
new XmlSerializer( |
typeof(SerializableDictionary<TKey, TValue>)); |
serializer.Serialize(writer, this); |
writer.Seek(0, SeekOrigin.Begin); |
clone = (SerializableDictionary<TKey, TValue>) |
new XmlSerializer( |
typeof(SerializableDictionary<TKey, TValue>)) |
.Deserialize(writer); |
} |
} |
/* cloning failed so return an empty dictionary */ |
catch (Exception) |
{ |
clone = new SerializableDictionary<TKey, TValue>(); |
} |
return clone; |
} |
|
public XmlSchema GetSchema() |
{ |
return null; |
} |
|
public void ReadXml(XmlReader reader) |
{ |
var keySerializer = new XmlSerializer(typeof(TKey)); |
var valueSerializer = new XmlSerializer(typeof(TValue)); |
|
var wasEmpty = reader.IsEmptyElement; |
reader.Read(); |
|
if (wasEmpty) |
return; |
|
while (!reader.NodeType.Equals(XmlNodeType.EndElement)) |
{ |
reader.ReadStartElement("Item"); |
|
reader.ReadStartElement("Key"); |
var key = (TKey) keySerializer.Deserialize(reader); |
reader.ReadEndElement(); |
|
reader.ReadStartElement("Value"); |
var value = (TValue) valueSerializer.Deserialize(reader); |
reader.ReadEndElement(); |
|
Add(key, value); |
|
reader.ReadEndElement(); |
reader.MoveToContent(); |
} |
reader.ReadEndElement(); |
} |
|
public void WriteXml(XmlWriter writer) |
{ |
var keySerializer = new XmlSerializer(typeof(TKey)); |
var valueSerializer = new XmlSerializer(typeof(TValue)); |
|
foreach (var key in Keys) |
{ |
writer.WriteStartElement("Item"); |
|
writer.WriteStartElement("Key"); |
keySerializer.Serialize(writer, key); |
writer.WriteEndElement(); |
|
writer.WriteStartElement("Value"); |
var value = this[key]; |
valueSerializer.Serialize(writer, value); |
writer.WriteEndElement(); |
|
writer.WriteEndElement(); |
} |
} |
|
#endregion |
} |
} |
/Collections/Generic/SerializableSortedDictionary.cs |
@@ -0,0 +1,129 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Collections.Generic; |
using System.IO; |
using System.Xml; |
using System.Xml.Schema; |
using System.Xml.Serialization; |
|
namespace wasSharp.Collections.Generic |
{ |
/// <summary> |
/// A serializable sorted dictionary class. |
/// </summary> |
/// <typeparam name="TKey">the key</typeparam> |
/// <typeparam name="TValue">the value</typeparam> |
[XmlRoot("SortedDictionary")] |
public class SerializableSortedDictionary<TKey, TValue> |
: SortedDictionary<TKey, TValue>, IXmlSerializable |
{ |
#region IXmlSerializable Members |
|
public SerializableSortedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> kvp) |
{ |
foreach (var i in kvp) |
{ |
Add(i.Key, i.Value); |
} |
} |
|
public SerializableSortedDictionary() |
{ |
} |
|
/// <summary> |
/// Deep-clones the serializable dictionary. |
/// </summary> |
/// <returns>a deep clone of the original dictionary</returns> |
public SerializableSortedDictionary<TKey, TValue> Clone() |
{ |
SerializableSortedDictionary<TKey, TValue> clone; |
try |
{ |
using (var writer = new MemoryStream()) |
{ |
var serializer = |
new XmlSerializer( |
typeof(SerializableDictionary<TKey, TValue>)); |
serializer.Serialize(writer, this); |
writer.Seek(0, SeekOrigin.Begin); |
clone = (SerializableSortedDictionary<TKey, TValue>) |
new XmlSerializer( |
typeof(SerializableSortedDictionary<TKey, TValue>)) |
.Deserialize(writer); |
} |
} |
/* cloning failed so return an empty dictionary */ |
catch (Exception) |
{ |
clone = new SerializableSortedDictionary<TKey, TValue>(); |
} |
return clone; |
} |
|
public XmlSchema GetSchema() |
{ |
return null; |
} |
|
public void ReadXml(XmlReader reader) |
{ |
var keySerializer = new XmlSerializer(typeof(TKey)); |
var valueSerializer = new XmlSerializer(typeof(TValue)); |
|
var wasEmpty = reader.IsEmptyElement; |
reader.Read(); |
|
if (wasEmpty) |
return; |
|
while (!reader.NodeType.Equals(XmlNodeType.EndElement)) |
{ |
reader.ReadStartElement("Item"); |
|
reader.ReadStartElement("Key"); |
var key = (TKey) keySerializer.Deserialize(reader); |
reader.ReadEndElement(); |
|
reader.ReadStartElement("Value"); |
var value = (TValue) valueSerializer.Deserialize(reader); |
reader.ReadEndElement(); |
|
Add(key, value); |
|
reader.ReadEndElement(); |
reader.MoveToContent(); |
} |
reader.ReadEndElement(); |
} |
|
public void WriteXml(XmlWriter writer) |
{ |
var keySerializer = new XmlSerializer(typeof(TKey)); |
var valueSerializer = new XmlSerializer(typeof(TValue)); |
|
foreach (var key in Keys) |
{ |
writer.WriteStartElement("Item"); |
|
writer.WriteStartElement("Key"); |
keySerializer.Serialize(writer, key); |
writer.WriteEndElement(); |
|
writer.WriteStartElement("Value"); |
var value = this[key]; |
valueSerializer.Serialize(writer, value); |
writer.WriteEndElement(); |
|
writer.WriteEndElement(); |
} |
} |
|
#endregion |
} |
} |
/Collections/Specialized/ObservableHashSet.cs |
@@ -0,0 +1,136 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Collections; |
using System.Collections.Generic; |
using System.Collections.Specialized; |
using System.Linq; |
|
namespace wasSharp.Collections.Specialized |
{ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// An implementation of an observable HashSet. |
/// </summary> |
/// <typeparam name="T">the object type</typeparam> |
public class ObservableHashSet<T> : ICollection<T>, INotifyCollectionChanged |
{ |
private readonly HashSet<T> store = new HashSet<T>(); |
|
public ObservableHashSet(HashSet<T> set) |
{ |
UnionWith(set); |
} |
|
public ObservableHashSet() |
{ |
} |
|
public ObservableHashSet(T item) |
{ |
Add(item); |
} |
|
public ObservableHashSet(ObservableHashSet<T> other) |
{ |
UnionWith(other); |
} |
|
public ObservableHashSet(IEnumerable<T> list) |
{ |
UnionWith(list); |
} |
|
public bool IsVirgin { get; private set; } = true; |
|
public IEnumerator<T> GetEnumerator() |
{ |
return store.GetEnumerator(); |
} |
|
IEnumerator IEnumerable.GetEnumerator() |
{ |
return GetEnumerator(); |
} |
|
public void Add(T item) |
{ |
store.Add(item); |
IsVirgin = false; |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item)); |
} |
|
public void Clear() |
{ |
store.Clear(); |
if (!IsVirgin) |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); |
IsVirgin = false; |
} |
|
public bool Contains(T item) |
{ |
return store.Contains(item); |
} |
|
public void CopyTo(T[] array, int arrayIndex) |
{ |
store.CopyTo(array, arrayIndex); |
} |
|
public bool Remove(T item) |
{ |
var removed = store.Remove(item); |
IsVirgin = false; |
if (removed) |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item)); |
return removed; |
} |
|
public int Count => store.Count; |
|
public bool IsReadOnly => false; |
|
public event NotifyCollectionChangedEventHandler CollectionChanged; |
|
public void UnionWith(IEnumerable<T> list) |
{ |
var added = new List<T>(list.Except(store)); |
store.UnionWith(added); |
if (!IsVirgin && added.Any()) |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, added)); |
IsVirgin = false; |
} |
|
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args) |
{ |
CollectionChanged?.Invoke(this, args); |
} |
|
public void ExceptWith(IEnumerable<T> list) |
{ |
var removed = new List<T>(list.Intersect(store)); |
store.ExceptWith(removed); |
if (!IsVirgin && removed.Any()) |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, |
removed)); |
IsVirgin = false; |
} |
|
public void RemoveWhere(Func<T, bool> func) |
{ |
var removed = new List<T>(store.Where(func)); |
store.ExceptWith(removed); |
if (!IsVirgin && removed.Any()) |
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, |
removed)); |
IsVirgin = false; |
} |
} |
} |
/Collections/Utilities/Extensions.cs |
@@ -0,0 +1,58 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System.Collections.Generic; |
using System.Linq; |
|
namespace wasSharp.Collections.Utilities |
{ |
public static class Extensions |
{ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2016 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Returns true of an enumerable contains more than one element. |
/// </summary> |
/// <typeparam name="T">the type of the enumeration</typeparam> |
/// <param name="e">the enumeration</param> |
/// <returns>true if enumeration contains more than one element</returns> |
/// <remarks>O(2) worst case</remarks> |
public static bool Some<T>(this IEnumerable<T> e) |
{ |
var i = 0; |
using (var iter = e.GetEnumerator()) |
{ |
while (iter.MoveNext()) |
{ |
if (++i > 1) |
return true; |
} |
return false; |
} |
} |
|
/// <summary> |
/// Compares two dictionaries for equality. |
/// </summary> |
/// <typeparam name="TKey">key type</typeparam> |
/// <typeparam name="TValue">value type</typeparam> |
/// <param name="dictionary">dictionary to compare</param> |
/// <param name="otherDictionary">dictionary to compare to</param> |
/// <returns>true if the dictionaries contain the same elements</returns> |
public static bool ContentEquals<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, |
IDictionary<TKey, TValue> otherDictionary) |
{ |
return |
(dictionary ?? new Dictionary<TKey, TValue>()).Count.Equals( |
(otherDictionary ?? new Dictionary<TKey, TValue>()).Count) && |
(otherDictionary ?? new Dictionary<TKey, TValue>()) |
.OrderBy(kvp => kvp.Key) |
.SequenceEqual((dictionary ?? new Dictionary<TKey, TValue>()) |
.OrderBy(kvp => kvp.Key)); |
} |
} |
} |
/Reflection.cs |
@@ -8,7 +8,6 @@ |
using System.Collections.Generic; |
using System.Linq; |
using System.Reflection; |
using wasSharp; |
|
namespace wasSharp |
{ |
@@ -25,7 +24,7 @@ |
{ |
return (T) value.GetType() |
.GetRuntimeField(value.ToString()) |
.GetCustomAttributes(typeof (T), false) |
.GetCustomAttributes(typeof(T), false) |
.SingleOrDefault(); |
} |
|
@@ -54,9 +53,9 @@ |
public static IEnumerable<string> GetEnumNames<T>() |
{ |
return |
typeof (T).GetRuntimeFields().ToArray() |
typeof(T).GetRuntimeFields().ToArray() |
.AsParallel() |
.Select(o => o.GetCustomAttribute(typeof (NameAttribute), false)) |
.Select(o => o.GetCustomAttribute(typeof(NameAttribute), false)) |
.Select(o => (o as NameAttribute)?.Name) |
.Where(o => !string.IsNullOrEmpty(o)) |
.Select(o => o); |
@@ -71,7 +70,7 @@ |
/// <returns>the values of the enumeration</returns> |
public static IEnumerable<T> GetEnumValues<T>() |
{ |
return Enum.GetValues(typeof (T)).Cast<object>().Select(value => (T) value); |
return Enum.GetValues(typeof(T)).Cast<object>().Select(value => (T) value); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
@@ -86,7 +85,7 @@ |
{ |
var attribute = value.GetType() |
.GetRuntimeField(value.ToString()) |
.GetCustomAttributes(typeof (NameAttribute), false) |
.GetCustomAttributes(typeof(NameAttribute), false) |
.SingleOrDefault() as NameAttribute; |
return attribute?.Name; |
} |
@@ -103,7 +102,7 @@ |
{ |
var attribute = value.GetType() |
.GetRuntimeField(value.ToString()) |
.GetCustomAttributes(typeof (DescriptionAttribute), false) |
.GetCustomAttributes(typeof(DescriptionAttribute), false) |
.SingleOrDefault() as DescriptionAttribute; |
return attribute?.Description; |
} |
@@ -119,11 +118,12 @@ |
/// <returns>the value or the default of T if case no name attribute found</returns> |
public static T GetEnumValueFromName<T>(string name) |
{ |
var field = typeof (T).GetRuntimeFields().ToArray() |
var field = typeof(T).GetRuntimeFields().ToArray() |
.AsParallel().SelectMany(f => f.GetCustomAttributes( |
typeof (NameAttribute), false), ( |
f, a) => new {Field = f, Att = a}).SingleOrDefault(a => Strings.StringEquals(((NameAttribute) a.Att) |
.Name, name, StringComparison.Ordinal)); |
typeof(NameAttribute), false), ( |
f, a) => new {Field = f, Att = a}) |
.SingleOrDefault(a => Strings.StringEquals(((NameAttribute) a.Att) |
.Name, name, StringComparison.Ordinal)); |
return field != null ? (T) field.Field.GetValue(Activator.CreateInstance<T>()) : default(T); |
} |
|
@@ -139,8 +139,8 @@ |
/// <returns>the description or the empty string</returns> |
public static string GetStructureMemberName<T>(T structure, object item) where T : struct |
{ |
var field = typeof (T).GetRuntimeFields().ToArray() |
.AsParallel().SelectMany(f => f.GetCustomAttributes(typeof (NameAttribute), false), |
var field = typeof(T).GetRuntimeFields().ToArray() |
.AsParallel().SelectMany(f => f.GetCustomAttributes(typeof(NameAttribute), false), |
(f, a) => new {Field = f, Att = a}).SingleOrDefault(f => f.Field.GetValue(structure).Equals(item)); |
return field != null ? ((NameAttribute) field.Att).Name : string.Empty; |
} |
@@ -232,7 +232,7 @@ |
public static IEnumerable<Type> GetBaseTypes(this Type type) |
{ |
var baseType = type.GetTypeInfo().BaseType; |
if(baseType == null) |
if (baseType == null) |
yield break; |
yield return baseType; |
foreach (var t in GetBaseTypes(baseType)) |
/Timers/DecayingAlarm.cs |
@@ -0,0 +1,143 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Collections.Generic; |
using System.Diagnostics; |
using System.Linq; |
using System.Threading; |
using System.Xml.Serialization; |
|
namespace wasSharp.Timers |
{ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// An alarm class similar to the UNIX alarm with the added benefit |
/// of a decaying timer that tracks the time between rescheduling. |
/// </summary> |
/// <remarks> |
/// (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 |
/// </remarks> |
public class DecayingAlarm : IDisposable |
{ |
[Flags] |
public enum DECAY_TYPE |
{ |
[Reflection.NameAttribute("none")] [XmlEnum(Name = "none")] NONE = 0, |
[Reflection.NameAttribute("arithmetic")] [XmlEnum(Name = "arithmetic")] ARITHMETIC = 1, |
[Reflection.NameAttribute("geometric")] [XmlEnum(Name = "geometric")] GEOMETRIC = 2, |
[Reflection.NameAttribute("harmonic")] [XmlEnum(Name = "harmonic")] HARMONIC = 4, |
[Reflection.NameAttribute("weighted")] [XmlEnum(Name = "weighted")] WEIGHTED = 5 |
} |
|
private readonly DECAY_TYPE decay = DECAY_TYPE.NONE; |
private readonly Stopwatch elapsed = new Stopwatch(); |
private readonly object LockObject = new object(); |
private readonly HashSet<double> times = new HashSet<double>(); |
private Timer alarm; |
|
/// <summary> |
/// The default constructor using no decay. |
/// </summary> |
public DecayingAlarm() |
{ |
Signal = new ManualResetEvent(false); |
} |
|
/// <summary> |
/// The constructor for the DecayingAlarm class taking as parameter a decay type. |
/// </summary> |
/// <param name="decay">the type of decay: arithmetic, geometric, harmonic, heronian or quadratic</param> |
public DecayingAlarm(DECAY_TYPE decay) |
{ |
Signal = new ManualResetEvent(false); |
this.decay = decay; |
} |
|
public ManualResetEvent Signal { get; set; } |
|
public void Dispose() |
{ |
Dispose(true); |
GC.SuppressFinalize(this); |
} |
|
~DecayingAlarm() |
{ |
Dispose(false); |
} |
|
public void Alarm(double deadline) |
{ |
lock (LockObject) |
{ |
switch (alarm == null) |
{ |
case true: |
elapsed.Start(); |
alarm = new Timer(o => |
{ |
lock (LockObject) |
{ |
Signal.Set(); |
elapsed.Stop(); |
times.Clear(); |
alarm.Dispose(); |
alarm = null; |
} |
}, null, (int) deadline, 0); |
return; |
case false: |
elapsed.Stop(); |
times.Add(elapsed.ElapsedMilliseconds); |
switch (decay) |
{ |
case DECAY_TYPE.ARITHMETIC: |
alarm?.Change( |
(int) ((deadline + times.Aggregate((a, b) => b + a))/(1f + times.Count)), 0); |
break; |
case DECAY_TYPE.GEOMETRIC: |
alarm?.Change((int) Math.Pow(deadline*times.Aggregate((a, b) => b*a), |
1f/(1f + times.Count)), 0); |
break; |
case DECAY_TYPE.HARMONIC: |
alarm?.Change((int) ((1f + times.Count)/ |
(1f/deadline + times.Aggregate((a, b) => 1f/b + 1f/a))), 0); |
break; |
case DECAY_TYPE.WEIGHTED: |
var d = new HashSet<double>(times) {deadline}; |
var total = d.Aggregate((a, b) => b + a); |
alarm?.Change( |
(int) d.Aggregate((a, b) => Math.Pow(a, 2)/total + Math.Pow(b, 2)/total), 0); |
break; |
default: |
alarm?.Change((int) deadline, 0); |
break; |
} |
elapsed.Reset(); |
elapsed.Start(); |
break; |
} |
} |
} |
|
private void Dispose(bool dispose) |
{ |
if (alarm != null) |
{ |
alarm.Dispose(); |
alarm = null; |
} |
} |
|
public DecayingAlarm Clone() |
{ |
return new DecayingAlarm(decay); |
} |
} |
} |
/Timers/Timer.cs |
@@ -0,0 +1,114 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Threading; |
using System.Threading.Tasks; |
|
namespace wasSharp.Timers |
{ |
public delegate void TimerCallback(object state); |
|
public class Timer : IDisposable |
{ |
private static readonly Task CompletedTask = Task.FromResult(false); |
private readonly TimerCallback Callback; |
private readonly object State; |
private Task Delay; |
private bool Disposed; |
private int Period; |
private CancellationTokenSource TokenSource; |
|
public Timer(TimerCallback callback, object state, int dueTime, int period) |
{ |
Callback = callback; |
State = state; |
Period = period; |
Reset(dueTime); |
} |
|
public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period) |
: this(callback, state, (int) dueTime.TotalMilliseconds, (int) period.TotalMilliseconds) |
{ |
} |
|
public Timer(TimerCallback callback) : this(callback, null, TimeSpan.Zero, TimeSpan.Zero) |
{ |
} |
|
public void Dispose() |
{ |
Dispose(true); |
GC.SuppressFinalize(this); |
} |
|
~Timer() |
{ |
Dispose(false); |
} |
|
private void Dispose(bool cleanUpManagedObjects) |
{ |
if (cleanUpManagedObjects) |
Cancel(); |
Disposed = true; |
} |
|
public void Change(int dueTime, int period) |
{ |
Period = period; |
Reset(dueTime); |
} |
|
public void Change(uint dueTime, int period) |
{ |
Period = period; |
Change((int) dueTime, period); |
} |
|
public void Change(TimeSpan dueTime, TimeSpan period) |
{ |
Change((int) dueTime.TotalMilliseconds, (int) period.TotalMilliseconds); |
} |
|
private void Reset(int due) |
{ |
Cancel(); |
if (due <= 0) |
return; |
TokenSource = new CancellationTokenSource(); |
Action tick = null; |
tick = () => |
{ |
Task.Run(() => Callback(State)); |
if (Disposed) |
return; |
Delay = Period > 0 ? Task.Delay(Period, TokenSource.Token) : CompletedTask; |
if (Delay.IsCompleted) |
return; |
Delay.ContinueWith(t => tick(), TokenSource.Token); |
}; |
Delay = due > 0 ? Task.Delay(due, TokenSource.Token) : CompletedTask; |
if (Delay.IsCompleted) |
return; |
Delay.ContinueWith(t => tick(), TokenSource.Token); |
} |
|
public void Stop() |
{ |
Change(0, 0); |
} |
|
private void Cancel() |
{ |
if (TokenSource == null) |
return; |
TokenSource.Cancel(); |
TokenSource.Dispose(); |
TokenSource = null; |
} |
} |
} |
/Web/Utilities/Extensions.cs |
@@ -0,0 +1,94 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Linq; |
using System.Linq.Expressions; |
using System.Net; |
using System.Text.RegularExpressions; |
|
namespace wasSharp.Web.Utilities |
{ |
public static class Extensions |
{ |
private static readonly Func<string, string> directURIEscapeDataString = |
((Expression<Func<string, string>>) |
(data => string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766).AsParallel() |
.Select(o => Uri.EscapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - o*32766)))) |
.ToArray()))).Compile(); |
|
private static readonly Func<string, string> directURIUnescapeDataString = |
((Expression<Func<string, string>>) |
(data => string.Join("", Enumerable.Range(0, (data.Length + 32765)/32766).AsParallel() |
.Select( |
o => Uri.UnescapeDataString(data.Substring(o*32766, Math.Min(32766, data.Length - o*32766)))) |
.ToArray()))).Compile(); |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>RFC3986 URI Escapes a string</summary> |
/// <remarks> |
/// data - a string to escape |
/// </remarks> |
/// <returns>an RFC3986 escaped string</returns> |
public static string URIEscapeDataString(this string data) |
{ |
return directURIEscapeDataString(data); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>URI unescapes an RFC3986 URI escaped string</summary> |
/// <remarks> |
/// data - a string to unescape |
/// </remarks> |
/// <returns>the resulting string</returns> |
public static string URIUnescapeDataString(this string data) |
{ |
return directURIUnescapeDataString(data); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>RFC1738 URL Escapes a string</summary> |
/// <param name="data">a string to escape</param> |
/// <returns>an RFC1738 escaped string</returns> |
public static string URLEscapeDataString(this string data) |
{ |
return WebUtility.UrlEncode(data); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary>RFC1738 URL Unescape a string</summary> |
/// <param name="data">a string to unescape</param> |
/// <returns>an RFC1738 unescaped string</returns> |
public static string URLUnescapeDataString(this string data) |
{ |
return WebUtility.UrlDecode(data); |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <param name="prefix">a HttpListener prefix</param> |
/// <returns>the port of the HttpListener</returns> |
public static long GetPortFromPrefix(this string prefix) |
{ |
var split = Regex.Replace( |
prefix, |
@"^([a-zA-Z]+:\/\/)?([^\/]+)\/.*?$", |
"$2" |
).Split(':'); |
long port; |
return split.Length <= 1 || !long.TryParse(split[1], out port) ? 80 : port; |
} |
} |
} |
/Web/wasHTTPClient.cs |
@@ -0,0 +1,271 @@ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 // |
// Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
// rights of fair usage, the disclaimer and warranty conditions. // |
/////////////////////////////////////////////////////////////////////////// |
|
using System; |
using System.Collections.Generic; |
using System.Net; |
using System.Net.Http; |
using System.Net.Http.Headers; |
using System.Text; |
using System.Threading.Tasks; |
|
namespace wasSharp.Web |
{ |
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
// <summary>A portable HTTP client.</summar> |
public class wasHTTPClient |
{ |
private readonly HttpClient HTTPClient; |
private readonly string MediaType; |
|
public wasHTTPClient(ProductInfoHeaderValue userAgent, CookieContainer cookieContainer, string mediaType, |
uint timeout) : this(userAgent, cookieContainer, mediaType, null, null, timeout) |
{ |
} |
|
public wasHTTPClient(ProductInfoHeaderValue userAgent, string mediaType, uint timeout) |
: this(userAgent, new CookieContainer(), mediaType, null, null, timeout) |
{ |
} |
|
public wasHTTPClient(ProductInfoHeaderValue userAgent, CookieContainer cookieContainer, string mediaType, |
AuthenticationHeaderValue authentication, Dictionary<string, string> headers, uint timeout) |
{ |
var HTTPClientHandler = new HttpClientHandler |
{ |
CookieContainer = cookieContainer, |
UseCookies = true |
}; |
if (HTTPClientHandler.SupportsRedirectConfiguration) |
{ |
HTTPClientHandler.AllowAutoRedirect = true; |
} |
if (HTTPClientHandler.SupportsProxy) |
{ |
HTTPClientHandler.Proxy = WebRequest.DefaultWebProxy; |
HTTPClientHandler.UseProxy = true; |
} |
if (HTTPClientHandler.SupportsAutomaticDecompression) |
{ |
HTTPClientHandler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate; |
} |
HTTPClientHandler.ClientCertificateOptions = ClientCertificateOption.Automatic; |
|
HTTPClient = new HttpClient(HTTPClientHandler, false); |
HTTPClient.DefaultRequestHeaders.UserAgent.Add(userAgent); |
if (authentication != null) |
{ |
HTTPClient.DefaultRequestHeaders.Authorization = authentication; |
} |
if (headers != null) |
{ |
foreach (var header in headers) |
{ |
HTTPClient.DefaultRequestHeaders.Add(header.Key, header.Value); |
} |
} |
HTTPClient.Timeout = TimeSpan.FromMilliseconds(timeout); |
MediaType = mediaType; |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Sends a PUT request to an URL with binary data. |
/// </summary> |
/// <param name="URL">the url to send the message to</param> |
/// <param name="data">key-value pairs to send</param> |
/// <param name="headers">headers to send with the request</param> |
public async Task<byte[]> PUT(string URL, byte[] data, Dictionary<string, string> headers) |
{ |
try |
{ |
using (var content = new ByteArrayContent(data)) |
{ |
using ( |
var request = new HttpRequestMessage |
{ |
RequestUri = new Uri(URL), |
Method = HttpMethod.Put, |
Content = content |
}) |
{ |
foreach (var header in headers) |
{ |
request.Headers.Add(header.Key, header.Value); |
} |
using (var response = await HTTPClient.SendAsync(request)) |
{ |
return response.IsSuccessStatusCode |
? await response.Content.ReadAsByteArrayAsync() |
: null; |
} |
} |
} |
} |
catch (Exception) |
{ |
return null; |
} |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Sends a PUT request to an URL with text. |
/// </summary> |
/// <param name="URL">the url to send the message to</param> |
/// <param name="data">key-value pairs to send</param> |
/// <param name="headers">headers to send with the request</param> |
public async Task<byte[]> PUT(string URL, string data, Dictionary<string, string> headers) |
{ |
try |
{ |
using (var content = |
new StringContent(data, Encoding.UTF8, MediaType)) |
{ |
using ( |
var request = new HttpRequestMessage |
{ |
RequestUri = new Uri(URL), |
Method = HttpMethod.Put, |
Content = content |
}) |
{ |
foreach (var header in headers) |
{ |
request.Headers.Add(header.Key, header.Value); |
} |
using (var response = await HTTPClient.SendAsync(request)) |
{ |
return response.IsSuccessStatusCode |
? await response.Content.ReadAsByteArrayAsync() |
: null; |
} |
} |
} |
} |
catch (Exception) |
{ |
return null; |
} |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Sends a PUT request to an URL with binary data. |
/// </summary> |
/// <param name="URL">the url to send the message to</param> |
/// <param name="data">key-value pairs to send</param> |
public async Task<byte[]> PUT(string URL, byte[] data) |
{ |
try |
{ |
using (var content = new ByteArrayContent(data)) |
{ |
using (var response = await HTTPClient.PutAsync(URL, content)) |
{ |
return response.IsSuccessStatusCode |
? await response.Content.ReadAsByteArrayAsync() |
: null; |
} |
} |
} |
catch (Exception) |
{ |
return null; |
} |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Sends a PUT request to an URL with text. |
/// </summary> |
/// <param name="URL">the url to send the message to</param> |
/// <param name="data">key-value pairs to send</param> |
public async Task<byte[]> PUT(string URL, string data) |
{ |
try |
{ |
using (var content = |
new StringContent(data, Encoding.UTF8, MediaType)) |
{ |
using (var response = await HTTPClient.PutAsync(URL, content)) |
{ |
return response.IsSuccessStatusCode |
? await response.Content.ReadAsByteArrayAsync() |
: null; |
} |
} |
} |
catch (Exception) |
{ |
return null; |
} |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Sends a POST request to an URL with set key-value pairs. |
/// </summary> |
/// <param name="URL">the url to send the message to</param> |
/// <param name="message">key-value pairs to send</param> |
public async Task<byte[]> POST(string URL, Dictionary<string, string> message) |
{ |
try |
{ |
using (var content = |
new StringContent(KeyValue.Encode(message), Encoding.UTF8, MediaType)) |
{ |
using (var response = await HTTPClient.PostAsync(URL, content)) |
{ |
return response.IsSuccessStatusCode |
? await response.Content.ReadAsByteArrayAsync() |
: null; |
} |
} |
} |
catch (Exception) |
{ |
return null; |
} |
} |
|
/////////////////////////////////////////////////////////////////////////// |
// Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3 // |
/////////////////////////////////////////////////////////////////////////// |
/// <summary> |
/// Sends a GET request to an URL with set key-value pairs. |
/// </summary> |
/// <param name="URL">the url to send the message to</param> |
/// <param name="message">key-value pairs to send</param> |
public async Task<byte[]> GET(string URL, Dictionary<string, string> message) |
{ |
try |
{ |
using (var response = |
await HTTPClient.GetAsync(URL + "?" + KeyValue.Encode(message))) |
{ |
return response.IsSuccessStatusCode ? await response.Content.ReadAsByteArrayAsync() : null; |
} |
} |
catch (Exception) |
{ |
return null; |
} |
} |
} |
} |
/wasSharp.csproj |
@@ -36,7 +36,13 @@ |
<ItemGroup> |
<Compile Include="Arrays.cs" /> |
<Compile Include="BitTwiddling.cs" /> |
<Compile Include="Collections.cs" /> |
<Compile Include="Collections\Generic\CircularQueue.cs" /> |
<Compile Include="Collections\Specialized\ExtendedObservableCollection.cs" /> |
<Compile Include="Collections\Utilities\Extensions.cs" /> |
<Compile Include="Collections\Specialized\ObservableHashSet.cs" /> |
<Compile Include="Collections\Generic\RangeCollection.cs" /> |
<Compile Include="Collections\Generic\SerializableDictionary.cs" /> |
<Compile Include="Collections\Generic\SerializableSortedDictionary.cs" /> |
<Compile Include="Cryptography.cs" /> |
<Compile Include="CSV.cs" /> |
<Compile Include="IO.cs" /> |
@@ -44,12 +50,17 @@ |
<Compile Include="Numerics.cs" /> |
<Compile Include="Reflection.cs" /> |
<Compile Include="Strings.cs" /> |
<Compile Include="Time.cs" /> |
<Compile Include="Timers\DecayingAlarm.cs" /> |
<Compile Include="Timers\Utilities\Extensions.cs" /> |
<Compile Include="Timers\TimedThrottle.cs" /> |
<Compile Include="Timers\Timer.cs" /> |
<Compile Include="wasSharp.cs" /> |
<Compile Include="Properties\AssemblyInfo.cs" /> |
<Compile Include="Web.cs" /> |
<Compile Include="Web\Utilities\Extensions.cs" /> |
<Compile Include="Web\wasHTTPClient.cs" /> |
<Compile Include="XML.cs" /> |
</ItemGroup> |
<ItemGroup /> |
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> |
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. |
Other similar extension points exist, see Microsoft.Common.targets. |