wasSharp

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 9  →  ?path2? @ 10
File deleted
\ No newline at end of file
/Time.cs
File deleted
\ No newline at end of file
/Collections.cs
File deleted
\ No newline at end of file
/Web.cs
/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/ExtendedObservableCollection.cs
@@ -0,0 +1,43 @@
///////////////////////////////////////////////////////////////////////////
// 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.Collections.ObjectModel;
using System.Collections.Specialized;
 
namespace wasSharp.Collections.Specialized
{
/// <summary>
/// An observable collection allowing the add of a range of items.
/// </summary>
/// <typeparam name="T">the collection type</typeparam>
public class ExtendedObservableCollection<T> : ObservableCollection<T>
{
private bool _suppressNotification;
 
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_suppressNotification)
base.OnCollectionChanged(e);
}
 
public void AddRange(IEnumerable<T> list)
{
if (list == null)
throw new ArgumentNullException(nameof(list));
 
_suppressNotification = true;
 
foreach (var item in list)
{
Add(item);
}
_suppressNotification = false;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}
/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));
}
}
}
/IO.cs
@@ -7,7 +7,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
 
namespace wasSharp
@@ -50,9 +49,9 @@
/// <returns>path parts</returns>
public static IEnumerable<string> PathSplit(this string path, char separator, char? escape)
{
Stack<char> s = new Stack<char>();
StringBuilder p = new StringBuilder();
foreach (char c in path)
var s = new Stack<char>();
var p = new StringBuilder();
foreach (var c in path)
{
if (c == escape)
{
/Properties/AssemblyInfo.cs
@@ -26,4 +26,4 @@
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
 
[assembly: AssemblyVersion("1.31.*")]
[assembly: AssemblyVersion("1.32.*")]
/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/TimedThrottle.cs
@@ -0,0 +1,73 @@
///////////////////////////////////////////////////////////////////////////
// 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;
 
namespace wasSharp.Timers
{
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2015 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
/// <summary>
/// Given a number of allowed events per seconds, this class allows you
/// to determine via the IsSafe property whether it is safe to trigger
/// another lined-up event. This is mostly used to check that throttles
/// are being respected.
/// </summary>
public class TimedThrottle : IDisposable
{
private readonly uint EventsAllowed;
private readonly object LockObject = new object();
private Timer timer;
public uint TriggeredEvents;
 
public TimedThrottle(uint events, uint seconds)
{
EventsAllowed = events;
if (timer == null)
{
timer = new Timer(o =>
{
lock (LockObject)
{
TriggeredEvents = 0;
}
}, null, (int) seconds, (int) seconds);
}
}
 
public bool IsSafe
{
get
{
lock (LockObject)
{
return ++TriggeredEvents <= EventsAllowed;
}
}
}
 
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
 
~TimedThrottle()
{
Dispose(false);
}
 
private void Dispose(bool dispose)
{
if (timer != null)
{
timer.Dispose();
timer = null;
}
}
}
}
/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;
}
}
}
/Timers/Utilities/Extensions.cs
@@ -0,0 +1,35 @@
///////////////////////////////////////////////////////////////////////////
// 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;
 
namespace wasSharp.Timers.Utilities
{
public static class Extensions
{
/// <summary>
/// Convert an Unix timestamp to a DateTime structure.
/// </summary>
/// <param name="unixTimestamp">the Unix timestamp to convert</param>
/// <returns>the DateTime structure</returns>
/// <remarks>the function assumes UTC time</remarks>
public static DateTime UnixTimestampToDateTime(this uint unixTimestamp)
{
return new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(unixTimestamp).ToUniversalTime();
}
 
/// <summary>
/// Convert a DateTime structure to a Unix timestamp.
/// </summary>
/// <param name="dateTime">the DateTime structure to convert</param>
/// <returns>the Unix timestamp</returns>
/// <remarks>the function assumes UTC time</remarks>
public static uint DateTimeToUnixTimestamp(this DateTime dateTime)
{
return (uint) (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
}
}
/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.