/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)); |
} |
} |
} |