mirror of
https://github.com/ICDSystems/ICD.Common.Utils.git
synced 2026-01-23 17:34:49 +00:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e2ec3de63 | ||
|
|
8ed856eb98 | ||
|
|
514c0eaec5 | ||
|
|
621d83d8dc | ||
|
|
588e3df86e | ||
|
|
336f39c9c2 | ||
|
|
5d3b80938e | ||
|
|
eb2a77b772 | ||
|
|
1c420d111b | ||
|
|
e1279fb860 | ||
|
|
bf91d9e87f | ||
|
|
1c89ebc5c2 | ||
|
|
2cd744eb1c | ||
|
|
20350c0ab7 | ||
|
|
177e59edb6 | ||
|
|
d8041bd94c | ||
|
|
f2122596b4 | ||
|
|
653bf361ef | ||
|
|
3689517124 | ||
|
|
6fdd5a138e | ||
|
|
fb267465ce | ||
|
|
f818653298 | ||
|
|
f7b5d07c38 | ||
|
|
1a931e6ffb | ||
|
|
2fab5d6fd4 | ||
|
|
8d683f875b | ||
|
|
74c96aac8a | ||
|
|
edc9fa300e | ||
|
|
37a21131d9 | ||
|
|
f674d4c60d | ||
|
|
ce163629f3 | ||
|
|
18b07abb44 | ||
|
|
4bc6258b62 | ||
|
|
086aee8167 | ||
|
|
cc115bafad | ||
|
|
00db478ef6 | ||
|
|
aa3fc3bccc | ||
|
|
2602380100 | ||
|
|
1f023c14d0 | ||
|
|
6ce52b74ac | ||
|
|
c500f1db5d | ||
|
|
da58379dfa | ||
|
|
1ae6a55c7d | ||
|
|
381b19f2e6 | ||
|
|
2f3b1ef57d | ||
|
|
da31fae213 | ||
|
|
e4b0c9f91a | ||
|
|
38dd85d79d | ||
|
|
c306dd6eb9 | ||
|
|
6fa3cc03ad | ||
|
|
eb58e65574 | ||
|
|
f24f9458ca | ||
|
|
79bbbe277f | ||
|
|
c03833bf9d | ||
|
|
f816ae771b | ||
|
|
a178ebe8cb | ||
|
|
3cddc14880 | ||
|
|
088222db47 | ||
|
|
9dcda7271d | ||
|
|
9dca46add5 | ||
|
|
35593bfa47 | ||
|
|
62abd624f8 | ||
|
|
c6a32da597 | ||
|
|
aeda403b42 | ||
|
|
548431c100 | ||
|
|
d98d222880 | ||
|
|
02758c6b8e | ||
|
|
6c3be1884a | ||
|
|
8da73593c4 | ||
|
|
f76f4773e5 | ||
|
|
567ee4a1ae | ||
|
|
72a4b19f97 | ||
|
|
b2c3ecb113 | ||
|
|
e55c2f9474 | ||
|
|
d99f0e889c | ||
|
|
cddc726d4d | ||
|
|
3f8e2b8df7 | ||
|
|
1bddca9a06 | ||
|
|
db8e725fa9 | ||
|
|
d9cf0bd3cf | ||
|
|
da8947aaa0 | ||
|
|
87427096d2 | ||
|
|
b1c7e527c4 | ||
|
|
718fc349f5 | ||
|
|
1a4159fc31 | ||
|
|
110f771cc5 | ||
|
|
e446f6e6ce | ||
|
|
7e48547483 | ||
|
|
4adebaf89b | ||
|
|
9abf384bb4 | ||
|
|
d24e17bc26 | ||
|
|
07b89edc66 | ||
|
|
e974ab655f | ||
|
|
e43a7200c6 | ||
|
|
b41a236716 | ||
|
|
38fa698d46 | ||
|
|
553a79bd17 | ||
|
|
d4f5413ce6 | ||
|
|
9b1ddf41d2 | ||
|
|
d22c3c2cd2 | ||
|
|
a5cc9e694a | ||
|
|
08f525eb3e | ||
|
|
a52b2d2c08 | ||
|
|
d33292ae8e | ||
|
|
dd794729e8 |
46
CHANGELOG.md
46
CHANGELOG.md
@@ -6,6 +6,52 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [3.7.0] - 2018-07-02
|
||||
### Added
|
||||
- Adding SequenceComparer for ordering collections of lists, arrays, etc
|
||||
- Added RateLimitedEventQueue collection for throttling events
|
||||
|
||||
### Changed
|
||||
- Potential fix for timer disposal on Net Standard
|
||||
- Added workaround for older RPC servers where the typestring being broadcast would stil include _SimplSharp, now will be stripped
|
||||
- Fixing bug where Timer.Reset() would continue repeating on an interval in Net Standard
|
||||
|
||||
## [3.6.0] - 2018-06-19
|
||||
### Added
|
||||
- Added ZIP features for examining the contents of an archive
|
||||
- Added FileNameEqualityComparer
|
||||
- Added BiDictionary for one-to-one maps
|
||||
|
||||
## [3.5.1] - 2018-06-04
|
||||
### Changed
|
||||
- PriorityQueue indexing fix
|
||||
- Pathfinding optimizations
|
||||
|
||||
## [3.5.0] - 2018-05-24
|
||||
### Added
|
||||
- Added GetFlagsExceptNone parameterless enum method
|
||||
- Added pathfinding methods for determining if a path exists
|
||||
|
||||
## [3.4.0] - 2018-05-23
|
||||
### Added
|
||||
- Added SetEquals method to IcdHashSet
|
||||
|
||||
## [3.3.0] - 2018-05-18
|
||||
### Added
|
||||
- Added IcdOrderedDictionary collection
|
||||
- Added PriorityQueue collection
|
||||
- Added IcdUriBuilder and UriExtensions for reading/writing URI data
|
||||
|
||||
## [3.2.0] - 2018-05-09
|
||||
### Added
|
||||
- Added util method for removing BOM characters from UTF8 data
|
||||
- Added extension method to convert from bool to ushort and back
|
||||
- Added extension method to cast enums to ushort value
|
||||
|
||||
## [3.1.0] - 2018-05-04
|
||||
### Added
|
||||
- Added Yield extension to return a single-item enumerable for an object.
|
||||
|
||||
## [3.0.0] - 2018-04-23
|
||||
### Added
|
||||
- Adding extension method for getting Informational Version from an Assembly
|
||||
|
||||
219
ICD.Common.Utils.Tests/Collections/BiDictionaryTest.cs
Normal file
219
ICD.Common.Utils.Tests/Collections/BiDictionaryTest.cs
Normal file
@@ -0,0 +1,219 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class BiDictionaryTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(2, "10");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(2, "10");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Keys.Count);
|
||||
|
||||
Assert.IsTrue(dict.Keys.Contains(2));
|
||||
Assert.IsTrue(dict.Keys.Contains(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Values.Count);
|
||||
|
||||
Assert.IsTrue(dict.Values.Contains("20"));
|
||||
Assert.IsTrue(dict.Values.Contains("30"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
|
||||
dict.Clear();
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsKey(1));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsKey(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
Assert.IsFalse(dict.ContainsValue("10"));
|
||||
|
||||
dict.Set(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.ContainsValue("10"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(1, "10"));
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(1, "20"));
|
||||
Assert.Throws<ArgumentException>(() => dict.Add(2, "10"));
|
||||
|
||||
Assert.DoesNotThrow(() => dict.Add(2, "20"));
|
||||
|
||||
Assert.AreEqual(2, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
|
||||
Assert.AreEqual(1, dict.GetKey("20"));
|
||||
Assert.AreEqual(3, dict.GetKey("30"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual(1, dict.GetKey("20"));
|
||||
Assert.AreEqual(3, dict.GetKey("30"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Set(1, "10");
|
||||
dict.Set(1, "20");
|
||||
dict.Set(3, "30");
|
||||
|
||||
Assert.AreEqual("20", dict.GetValue(1));
|
||||
Assert.AreEqual("30", dict.GetValue(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveKey(1);
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.AreEqual(1, dict.Count);
|
||||
|
||||
dict.RemoveValue("10");
|
||||
|
||||
Assert.AreEqual(0, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
string value;
|
||||
Assert.IsFalse(dict.TryGetValue(1, out value));
|
||||
// ReSharper disable once ExpressionIsAlwaysNull
|
||||
Assert.AreEqual(null, value);
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.TryGetValue(1, out value));
|
||||
Assert.AreEqual("10", value);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetKeyTest()
|
||||
{
|
||||
BiDictionary<int, string> dict = new BiDictionary<int, string>();
|
||||
|
||||
int value;
|
||||
Assert.IsFalse(dict.TryGetKey("10", out value));
|
||||
Assert.AreEqual(0, value);
|
||||
|
||||
dict.Add(1, "10");
|
||||
|
||||
Assert.IsTrue(dict.TryGetKey("10", out value));
|
||||
Assert.AreEqual(1, value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
126
ICD.Common.Utils.Tests/Collections/IcdOrderedDictionaryTest.cs
Normal file
126
ICD.Common.Utils.Tests/Collections/IcdOrderedDictionaryTest.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class IcdOrderedDictionaryTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{2, 20}
|
||||
};
|
||||
|
||||
Assert.AreEqual(3, dict.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsReadOnlyTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
|
||||
|
||||
Assert.IsFalse(dict.IsReadOnly);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void KeysTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] keys = dict.Keys.ToArray();
|
||||
|
||||
Assert.AreEqual(3, keys.Length);
|
||||
Assert.AreEqual(-1, keys[0]);
|
||||
Assert.AreEqual(0, keys[1]);
|
||||
Assert.AreEqual(1, keys[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValuesTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
int[] values = dict.Values.ToArray();
|
||||
|
||||
Assert.AreEqual(3, values.Length);
|
||||
Assert.AreEqual(-10, values[0]);
|
||||
Assert.AreEqual(0, values[1]);
|
||||
Assert.AreEqual(10, values[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IndexerTest()
|
||||
{
|
||||
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
|
||||
{
|
||||
{0, 0},
|
||||
{1, 10},
|
||||
{-1, -10}
|
||||
};
|
||||
|
||||
Assert.AreEqual(0, dict[0]);
|
||||
Assert.AreEqual(10, dict[1]);
|
||||
Assert.AreEqual(-10, dict[-1]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ContainsKeyTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RemoveTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryGetValueTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
185
ICD.Common.Utils.Tests/Collections/PriorityQueueTest.cs
Normal file
185
ICD.Common.Utils.Tests/Collections/PriorityQueueTest.cs
Normal file
@@ -0,0 +1,185 @@
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class PriorityQueueTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(2);
|
||||
queue.Enqueue(3);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IsSynchronizedTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
Assert.IsFalse(queue.IsSynchronized);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SyncRootTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
Assert.AreEqual(queue, queue.SyncRoot);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(2);
|
||||
queue.Enqueue(3);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnqueueTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(2);
|
||||
queue.Enqueue(3);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnqueuePriorityTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
queue.Enqueue(1, 3);
|
||||
queue.Enqueue(2, 2);
|
||||
queue.Enqueue(3, 1);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
List<int> dequeue = new List<int>
|
||||
{
|
||||
queue.Dequeue(),
|
||||
queue.Dequeue(),
|
||||
queue.Dequeue()
|
||||
};
|
||||
|
||||
Assert.AreEqual(3, dequeue[0]);
|
||||
Assert.AreEqual(2, dequeue[1]);
|
||||
Assert.AreEqual(1, dequeue[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnqueueFirstTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
queue.Enqueue(1, int.MaxValue);
|
||||
queue.Enqueue(2, int.MinValue);
|
||||
queue.EnqueueFirst(3);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
Assert.AreEqual(3, queue.Dequeue());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnqueueRemoveTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(2);
|
||||
queue.Enqueue(3);
|
||||
|
||||
queue.EnqueueRemove(4, i => i == 2);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
List<int> dequeue = new List<int>
|
||||
{
|
||||
queue.Dequeue(),
|
||||
queue.Dequeue(),
|
||||
queue.Dequeue()
|
||||
};
|
||||
|
||||
Assert.AreEqual(1, dequeue[0]);
|
||||
Assert.AreEqual(4, dequeue[1]);
|
||||
Assert.AreEqual(3, dequeue[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnqueueRemovePriorityTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DequeueTest()
|
||||
{
|
||||
PriorityQueue<int> queue = new PriorityQueue<int>();
|
||||
|
||||
queue.Enqueue(1, 3);
|
||||
queue.Enqueue(2, 2);
|
||||
queue.Enqueue(3, 1);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
|
||||
List<int> dequeue = new List<int>
|
||||
{
|
||||
queue.Dequeue(),
|
||||
queue.Dequeue(),
|
||||
queue.Dequeue()
|
||||
};
|
||||
|
||||
Assert.AreEqual(3, dequeue[0]);
|
||||
Assert.AreEqual(2, dequeue[1]);
|
||||
Assert.AreEqual(1, dequeue[2]);
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetEnumeratorTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CopyToTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
100
ICD.Common.Utils.Tests/Collections/RateLimitedEventQueueTest.cs
Normal file
100
ICD.Common.Utils.Tests/Collections/RateLimitedEventQueueTest.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Collections;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Collections
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class RateLimitedEventQueueTest
|
||||
{
|
||||
[Test]
|
||||
public void ItemDequeuedFeedbackTest()
|
||||
{
|
||||
List<GenericEventArgs<int>> callbacks = new List<GenericEventArgs<int>>();
|
||||
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 1000 })
|
||||
{
|
||||
queue.OnItemDequeued += (sender, args) => callbacks.Add(args);
|
||||
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
ThreadingUtils.Sleep(100);
|
||||
|
||||
Assert.AreEqual(1, callbacks.Count, "Initial enqueue did not trigger a dequeue");
|
||||
|
||||
queue.OnItemDequeued += (sender, args) => { ThreadingUtils.Sleep(1000); };
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Second enqueue did not dequeue");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(2, callbacks.Count, "Third enqueue did not wait for process to complete");
|
||||
|
||||
ThreadingUtils.Sleep(1000);
|
||||
|
||||
Assert.AreEqual(3, callbacks.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#region Properties
|
||||
|
||||
[TestCase(1000)]
|
||||
public void BetweenMillisecondsTest(long milliseconds)
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = milliseconds })
|
||||
Assert.AreEqual(milliseconds, queue.BetweenMilliseconds);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CountTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
Assert.AreEqual(3, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
[Test]
|
||||
public void EnqueueTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(10);
|
||||
queue.Enqueue(20);
|
||||
queue.Enqueue(30);
|
||||
|
||||
Assert.True(queue.SequenceEqual(new[] { 10, 20, 30 }));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearTest()
|
||||
{
|
||||
using (RateLimitedEventQueue<int> queue = new RateLimitedEventQueue<int> { BetweenMilliseconds = 100 * 1000 })
|
||||
{
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
queue.Enqueue(1);
|
||||
|
||||
queue.Clear();
|
||||
|
||||
Assert.AreEqual(0, queue.Count);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
33
ICD.Common.Utils.Tests/Comparers/SequenceComparerTest.cs
Normal file
33
ICD.Common.Utils.Tests/Comparers/SequenceComparerTest.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using ICD.Common.Utils.Comparers;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Comparers
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class SequenceComparerTest
|
||||
{
|
||||
[Test]
|
||||
public void CompareTest()
|
||||
{
|
||||
SequenceComparer<int> comparer = new SequenceComparer<int>();
|
||||
|
||||
// Equal
|
||||
int[] a = {1, 2, 3};
|
||||
int[] b = {1, 2, 3};
|
||||
|
||||
Assert.AreEqual(0, comparer.Compare(a, b));
|
||||
|
||||
// A comes before B
|
||||
a = new[] {1, 2};
|
||||
b = new[] {1, 2, 3};
|
||||
|
||||
Assert.AreEqual(-1, comparer.Compare(a, b));
|
||||
|
||||
// B comes before A
|
||||
a = new[] { 2, 2, 3 };
|
||||
b = new[] { 1, 2, 3 };
|
||||
|
||||
Assert.AreEqual(1, comparer.Compare(a, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
23
ICD.Common.Utils.Tests/EncodingUtilsTest.cs
Normal file
23
ICD.Common.Utils.Tests/EncodingUtilsTest.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class EncodingUtilsTest
|
||||
{
|
||||
[Test]
|
||||
public void StripUtf8BomTest()
|
||||
{
|
||||
Assert.Throws<ArgumentNullException>(() => EncodingUtils.StripUtf8Bom(null));
|
||||
|
||||
byte[] preamble = Encoding.UTF8.GetPreamble();
|
||||
string preambleString = Encoding.UTF8.GetString(preamble, 0, preamble.Length);
|
||||
|
||||
Assert.AreEqual(string.Empty, EncodingUtils.StripUtf8Bom(string.Empty));
|
||||
Assert.AreEqual("test", EncodingUtils.StripUtf8Bom("test"));
|
||||
Assert.AreEqual("test", EncodingUtils.StripUtf8Bom(preambleString + "test"));
|
||||
}
|
||||
}
|
||||
}
|
||||
22
ICD.Common.Utils.Tests/Extensions/UriExtensionsTest.cs
Normal file
22
ICD.Common.Utils.Tests/Extensions/UriExtensionsTest.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests.Extensions
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class UriExtensionsTest
|
||||
{
|
||||
[TestCase("http://username:password@test.com/", "username")]
|
||||
public void GetUserName(string uriString, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, new Uri(uriString).GetUserName());
|
||||
}
|
||||
|
||||
[TestCase("http://username:password@test.com/", "password")]
|
||||
public void GetPassword(string uriString, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, new Uri(uriString).GetPassword());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.2" />
|
||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
92
ICD.Common.Utils.Tests/IcdUriBuilderTest.cs
Normal file
92
ICD.Common.Utils.Tests/IcdUriBuilderTest.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ICD.Common.Utils.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public sealed class IcdUriBuilderTest
|
||||
{
|
||||
#region Properties
|
||||
|
||||
[TestCase("test")]
|
||||
public void FragmentTest(string fragment)
|
||||
{
|
||||
Assert.AreEqual(fragment, new IcdUriBuilder {Fragment = fragment}.Fragment);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
public void HostTest(string host)
|
||||
{
|
||||
Assert.AreEqual(host, new IcdUriBuilder {Host = host}.Host);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
public void PasswordTest(string fragment)
|
||||
{
|
||||
Assert.AreEqual(fragment, new IcdUriBuilder {Password = fragment}.Password);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
public void PathTest(string fragment)
|
||||
{
|
||||
Assert.AreEqual(fragment, new IcdUriBuilder {Path = fragment}.Path);
|
||||
}
|
||||
|
||||
[TestCase((ushort)80)]
|
||||
public void PortTest(ushort port)
|
||||
{
|
||||
Assert.AreEqual(port, new IcdUriBuilder {Port = port}.Port);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
public void QueryTest(string query)
|
||||
{
|
||||
Assert.AreEqual(query, new IcdUriBuilder {Query = query}.Query);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
public void SchemeTest(string scheme)
|
||||
{
|
||||
Assert.AreEqual(scheme, new IcdUriBuilder {Scheme = scheme}.Scheme);
|
||||
}
|
||||
|
||||
[TestCase("test")]
|
||||
public void UserNameTest(string userName)
|
||||
{
|
||||
Assert.AreEqual(userName, new IcdUriBuilder {UserName = userName}.UserName);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UriTest()
|
||||
{
|
||||
Assert.Inconclusive();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[TestCase("http://localhost/", null, null, null, null, (ushort)0, null, null, null)]
|
||||
[TestCase("http://localhost:80/", null, null, null, null, (ushort)80, null, null, null)]
|
||||
[TestCase("http://username@localhost/", null, null, null, null, (ushort)0, null, null, "username")]
|
||||
[TestCase("http://localhost/", null, null, "password", null, (ushort)0, null, null, null)]
|
||||
[TestCase("https://localhost/", null, null, null, null, (ushort)0, null, "https", null)]
|
||||
[TestCase("http://localhost/test", null, null, null, "test", (ushort)0, null, null, null)]
|
||||
[TestCase("http://localhost/test", null, null, null, "/test", (ushort)0, null, null, null)]
|
||||
[TestCase("http://localhost//test", null, null, null, "//test", (ushort)0, null, null, null)]
|
||||
public void ToStringTest(string expected, string fragment, string address, string password, string path, ushort port,
|
||||
string query, string scheme, string userName)
|
||||
{
|
||||
IcdUriBuilder builder = new IcdUriBuilder
|
||||
{
|
||||
Fragment = fragment,
|
||||
Host = address,
|
||||
Password = password,
|
||||
Path = path,
|
||||
Port = port,
|
||||
Query = query,
|
||||
Scheme = scheme,
|
||||
UserName = userName
|
||||
};
|
||||
|
||||
Assert.AreEqual(expected, builder.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,5 +170,20 @@ namespace ICD.Common.Utils.Tests
|
||||
Assert.IsTrue(StringUtils.TryParse("true", out testVal));
|
||||
Assert.AreEqual(true, testVal);
|
||||
}
|
||||
|
||||
[TestCase("test", "\"test\"")]
|
||||
[TestCase("\"test\"", "\"test\"")]
|
||||
[TestCase("test test", "\"test test\"")]
|
||||
public void EnquoteTest(string input, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, StringUtils.Enquote(input));
|
||||
}
|
||||
|
||||
[TestCase("\"test\"", "test")]
|
||||
[TestCase("\"test test\"", "test test")]
|
||||
public void UnEnquoteTest(string input, string expected)
|
||||
{
|
||||
Assert.AreEqual(expected, StringUtils.UnEnquote(input));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,14 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
foreach (Exception inner in e.LoaderExceptions)
|
||||
{
|
||||
if (inner is System.IO.FileNotFoundException)
|
||||
{
|
||||
Logger.AddEntry(eSeverity.Error,
|
||||
"{0} failed to cache assembly {1} - Could not find one or more dependencies by path",
|
||||
typeof(AttributeUtils).Name, assembly.GetName().Name);
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.AddEntry(eSeverity.Error, inner, "{0} failed to cache assembly {1}", typeof(AttributeUtils).Name,
|
||||
assembly.GetName().Name);
|
||||
}
|
||||
|
||||
186
ICD.Common.Utils/Collections/BiDictionary.cs
Normal file
186
ICD.Common.Utils/Collections/BiDictionary.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a 1-to-1 map of Keys to Values with O(1) Value->Key lookup time.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public sealed class BiDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly Dictionary<TKey, TValue> m_KeyToValue;
|
||||
private readonly Dictionary<TValue, TKey> m_ValueToKey;
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Count { get { return m_KeyToValue.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
|
||||
|
||||
public ICollection<TValue> Values { get { return m_ValueToKey.Keys; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public BiDictionary()
|
||||
{
|
||||
m_KeyToValue = new Dictionary<TKey, TValue>();
|
||||
m_ValueToKey = new Dictionary<TValue, TKey>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_KeyToValue.Clear();
|
||||
m_ValueToKey.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return m_KeyToValue.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool ContainsValue(TValue value)
|
||||
{
|
||||
return m_ValueToKey.ContainsKey(value);
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (ContainsKey(key))
|
||||
throw new ArgumentException("Key is already present in the collection", "key");
|
||||
|
||||
if (ContainsValue(value))
|
||||
throw new ArgumentException("Value is already present in the collection", "value");
|
||||
|
||||
m_KeyToValue.Add(key, value);
|
||||
m_ValueToKey.Add(value, key);
|
||||
}
|
||||
|
||||
public void Set(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
RemoveKey(key);
|
||||
RemoveValue(value);
|
||||
|
||||
Add(key, value);
|
||||
}
|
||||
|
||||
public TKey GetKey(TValue value)
|
||||
{
|
||||
return m_ValueToKey[value];
|
||||
}
|
||||
|
||||
public TValue GetValue(TKey key)
|
||||
{
|
||||
return m_KeyToValue[key];
|
||||
}
|
||||
|
||||
public bool RemoveKey(TKey key)
|
||||
{
|
||||
if (!ContainsKey(key))
|
||||
return false;
|
||||
|
||||
TValue value = m_KeyToValue[key];
|
||||
|
||||
m_KeyToValue.Remove(key);
|
||||
m_ValueToKey.Remove(value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveValue(TValue value)
|
||||
{
|
||||
if (!ContainsValue(value))
|
||||
return false;
|
||||
|
||||
TKey key = m_ValueToKey[value];
|
||||
|
||||
return RemoveKey(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
return m_KeyToValue.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public bool TryGetKey(TValue value, out TKey key)
|
||||
{
|
||||
return m_ValueToKey.TryGetValue(value, out key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary
|
||||
|
||||
TValue IDictionary<TKey, TValue>.this[TKey key] { get { return GetValue(key); } set { Set(key, value); } }
|
||||
|
||||
bool IDictionary<TKey, TValue>.Remove(TKey key)
|
||||
{
|
||||
return RemoveKey(key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return (m_KeyToValue as IDictionary<TKey, TValue>).Contains(item);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return RemoveKey(item.Key);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
|
||||
{
|
||||
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_KeyToValue.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,11 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Returns a set containing all of this sets items plus all of the items in the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Union(IEnumerable<T> set)
|
||||
{
|
||||
@@ -80,6 +85,11 @@ namespace ICD.Common.Utils.Collections
|
||||
return unionSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new set of this sets items exluding the items in the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Subtract(IEnumerable<T> set)
|
||||
{
|
||||
@@ -94,13 +104,11 @@ namespace ICD.Common.Utils.Collections
|
||||
return subtractSet;
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public bool IsSubsetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
return this.All(setToCompare.Contains);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all of the items that are common between this set and the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> Intersection(IcdHashSet<T> set)
|
||||
{
|
||||
@@ -126,32 +134,74 @@ namespace ICD.Common.Utils.Collections
|
||||
[PublicAPI]
|
||||
public IcdHashSet<T> NonIntersection(IcdHashSet<T> set)
|
||||
{
|
||||
return Subtract(set).Union(set.Subtract(this));
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
|
||||
return Subtract(set).Union(setToCompare.Subtract(this));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given set contains all of the items in this set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsSubsetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
|
||||
return this.All(setToCompare.Contains);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the given set contains all of the items in this set, and the sets are not equal.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsProperSubsetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
|
||||
// Is a proper subset if A is a subset of B and A != B
|
||||
return (IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this));
|
||||
return IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this set contains all of the items in the given set.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsSupersetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
|
||||
return setToCompare.IsSubsetOf(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this set contains all of the items in the given set, and the sets are not equal.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool IsProperSupersetOf(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
|
||||
// B is a proper superset of A if B is a superset of A and A != B
|
||||
return (IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this));
|
||||
return IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this set contains all of the items in the given set, and vice versa.
|
||||
/// </summary>
|
||||
/// <param name="set"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool SetEquals(IcdHashSet<T> set)
|
||||
{
|
||||
IcdHashSet<T> setToCompare = set ?? NullSet;
|
||||
|
||||
return IsSupersetOf(setToCompare) && setToCompare.IsSupersetOf(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -165,9 +215,9 @@ namespace ICD.Common.Utils.Collections
|
||||
/// <returns></returns>
|
||||
public bool Add(T item)
|
||||
{
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
// ReSharper disable CompareNonConstrainedGenericWithNull
|
||||
if (item == null)
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
// ReSharper restore CompareNonConstrainedGenericWithNull
|
||||
throw new ArgumentNullException("item");
|
||||
|
||||
if (m_Dict.ContainsKey(item))
|
||||
|
||||
159
ICD.Common.Utils/Collections/IcdOrderedDictionary.cs
Normal file
159
ICD.Common.Utils/Collections/IcdOrderedDictionary.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
|
||||
{
|
||||
private readonly List<TKey> m_OrderedKeys;
|
||||
private readonly Dictionary<TKey, TValue> m_Dictionary;
|
||||
private readonly IComparer<TKey> m_Comparer;
|
||||
|
||||
#region Properties
|
||||
|
||||
public int Count { get { return m_Dictionary.Count; } }
|
||||
|
||||
public bool IsReadOnly { get { return false; } }
|
||||
|
||||
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
|
||||
|
||||
public ICollection<TValue> Values
|
||||
{
|
||||
get
|
||||
{
|
||||
return m_OrderedKeys.Select(k => m_Dictionary[k])
|
||||
.ToArray(Count);
|
||||
}
|
||||
}
|
||||
|
||||
public TValue this[TKey key]
|
||||
{
|
||||
get { return m_Dictionary[key]; }
|
||||
set
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!ContainsKey(key))
|
||||
m_OrderedKeys.AddSorted(key, m_Comparer);
|
||||
|
||||
m_Dictionary[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdOrderedDictionary()
|
||||
: this(Comparer<TKey>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public IcdOrderedDictionary(IComparer<TKey> comparer)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
m_Comparer = comparer;
|
||||
m_OrderedKeys = new List<TKey>();
|
||||
m_Dictionary = new Dictionary<TKey, TValue>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
|
||||
{
|
||||
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
|
||||
.GetEnumerator();
|
||||
}
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (m_Dictionary.ContainsKey(key))
|
||||
throw new ArgumentException("An item with the same key has already been added.", "key");
|
||||
|
||||
this[key] = value;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_OrderedKeys.Clear();
|
||||
m_Dictionary.Clear();
|
||||
}
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return m_Dictionary.ContainsKey(key);
|
||||
}
|
||||
|
||||
public bool Remove(TKey key)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
if (!m_Dictionary.Remove(key))
|
||||
return false;
|
||||
|
||||
m_OrderedKeys.Remove(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
return m_Dictionary.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
TValue value;
|
||||
return TryGetValue(item.Key, out value) &&
|
||||
EqualityComparer<TValue>.Default.Equals(value, item.Value);
|
||||
}
|
||||
|
||||
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int index)
|
||||
{
|
||||
foreach (KeyValuePair<TKey, TValue> kvp in this)
|
||||
{
|
||||
array.SetValue(kvp, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||
{
|
||||
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
249
ICD.Common.Utils/Collections/PriorityQueue.cs
Normal file
249
ICD.Common.Utils/Collections/PriorityQueue.cs
Normal file
@@ -0,0 +1,249 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a first-in first-out collection with enhanced insertion features.
|
||||
/// </summary>
|
||||
public sealed class PriorityQueue<T> : IEnumerable<T>, ICollection
|
||||
{
|
||||
private readonly IcdOrderedDictionary<int, List<T>> m_PriorityToQueue;
|
||||
private int m_Count;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in the collection.
|
||||
/// </summary>
|
||||
public int Count { get { return m_Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Is the collection thread safe?
|
||||
/// </summary>
|
||||
public bool IsSynchronized { get { return false; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference for locking.
|
||||
/// </summary>
|
||||
public object SyncRoot { get { return this; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public PriorityQueue()
|
||||
{
|
||||
m_PriorityToQueue = new IcdOrderedDictionary<int, List<T>>();
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Clears the collection.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void Clear()
|
||||
{
|
||||
m_PriorityToQueue.Clear();
|
||||
m_Count = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to the end of the queue.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
Enqueue(item, int.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the item to the queue with the given priority.
|
||||
/// Lower values are dequeued first.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="priority"></param>
|
||||
[PublicAPI]
|
||||
public void Enqueue(T item, int priority)
|
||||
{
|
||||
if (!m_PriorityToQueue.ContainsKey(priority))
|
||||
m_PriorityToQueue.Add(priority, new List<T>());
|
||||
|
||||
m_PriorityToQueue[priority].Add(item);
|
||||
m_Count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the item at the beginning of the queue.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueFirst(T item)
|
||||
{
|
||||
const int priority = int.MinValue;
|
||||
|
||||
if (!m_PriorityToQueue.ContainsKey(priority))
|
||||
m_PriorityToQueue.Add(priority, new List<T>());
|
||||
|
||||
m_PriorityToQueue[priority].Insert(0, item);
|
||||
m_Count++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinant.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove(T item, Func<T, bool> remove)
|
||||
{
|
||||
if (remove == null)
|
||||
throw new ArgumentNullException("remove");
|
||||
|
||||
EnqueueRemove(item, remove, int.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes any items in the queue matching the predicate.
|
||||
/// Inserts the given item in the position of the first removed item, or at the end of the queue.
|
||||
/// This is useful for reducing duplication, or replacing items with something more pertinant.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="remove"></param>
|
||||
/// <param name="priority"></param>
|
||||
[PublicAPI]
|
||||
public void EnqueueRemove(T item, Func<T, bool> remove, int priority)
|
||||
{
|
||||
if (remove == null)
|
||||
throw new ArgumentNullException("remove");
|
||||
|
||||
bool inserted = false;
|
||||
|
||||
foreach (KeyValuePair<int, List<T>> kvp in m_PriorityToQueue)
|
||||
{
|
||||
int[] removeIndices =
|
||||
kvp.Value
|
||||
.FindIndices(v => remove(v))
|
||||
.Reverse()
|
||||
.ToArray();
|
||||
|
||||
if (removeIndices.Length == 0)
|
||||
continue;
|
||||
|
||||
foreach (int removeIndex in removeIndices)
|
||||
{
|
||||
kvp.Value.RemoveAt(removeIndex);
|
||||
m_Count--;
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
{
|
||||
int insertIndex = removeIndices[0];
|
||||
|
||||
if (insertIndex >= kvp.Value.Count)
|
||||
kvp.Value.Add(item);
|
||||
else
|
||||
kvp.Value.Insert(insertIndex, item);
|
||||
|
||||
m_Count++;
|
||||
|
||||
inserted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inserted)
|
||||
Enqueue(item, priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dequeues the first item with the lowest priority value.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public T Dequeue()
|
||||
{
|
||||
T output;
|
||||
if (TryDequeue(out output))
|
||||
return output;
|
||||
|
||||
throw new InvalidOperationException("The queue is empty.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to dequeue an item from the queue.
|
||||
/// </summary>
|
||||
/// <param name="output"></param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public bool TryDequeue(out T output)
|
||||
{
|
||||
output = default(T);
|
||||
|
||||
KeyValuePair<int, List<T>> kvp;
|
||||
if (!m_PriorityToQueue.TryFirst(out kvp))
|
||||
return false;
|
||||
|
||||
int priority = kvp.Key;
|
||||
List<T> queue = kvp.Value;
|
||||
|
||||
output = queue[0];
|
||||
queue.RemoveAt(0);
|
||||
|
||||
if (queue.Count == 0)
|
||||
m_PriorityToQueue.Remove(priority);
|
||||
|
||||
m_Count--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator for the items.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_PriorityToQueue.Values
|
||||
.SelectMany(v => v)
|
||||
.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the collection to the target array.
|
||||
/// </summary>
|
||||
/// <param name="array"></param>
|
||||
/// <param name="index"></param>
|
||||
public void CopyTo(Array array, int index)
|
||||
{
|
||||
foreach (T item in this)
|
||||
{
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Gets an enumerator for the items.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
182
ICD.Common.Utils/Collections/RateLimitedEventQueue.cs
Normal file
182
ICD.Common.Utils/Collections/RateLimitedEventQueue.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.EventArguments;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
using ICD.Common.Utils.Timers;
|
||||
|
||||
namespace ICD.Common.Utils.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
|
||||
/// </summary>
|
||||
public sealed class RateLimitedEventQueue<T> : IEnumerable<T>, ICollection, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Raised to handle to the next item in the queue.
|
||||
/// </summary>
|
||||
public event EventHandler<GenericEventArgs<T>> OnItemDequeued;
|
||||
|
||||
private readonly SafeTimer m_DequeueTimer;
|
||||
private readonly Queue<T> m_Queue;
|
||||
private readonly SafeCriticalSection m_QueueSection;
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the time between dequeues in milliseconds.
|
||||
/// </summary>
|
||||
public long BetweenMilliseconds { get; set; }
|
||||
|
||||
public int Count { get { return m_QueueSection.Execute(() => m_Queue.Count); } }
|
||||
|
||||
bool ICollection.IsSynchronized { get { return true; } }
|
||||
|
||||
object ICollection.SyncRoot { get { return this; } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public RateLimitedEventQueue()
|
||||
{
|
||||
m_Queue = new Queue<T>();
|
||||
m_QueueSection = new SafeCriticalSection();
|
||||
|
||||
m_DequeueTimer = SafeTimer.Stopped(DequeueTimerCallback);
|
||||
}
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Release resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
OnItemDequeued = null;
|
||||
|
||||
m_DequeueTimer.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues the given item.
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_Queue.Enqueue(item);
|
||||
|
||||
if (m_Queue.Count == 1)
|
||||
SendNext();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the queued items.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_DequeueTimer.Stop();
|
||||
m_Queue.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Sends the next pulse in the queue.
|
||||
/// </summary>
|
||||
private void SendNext()
|
||||
{
|
||||
if (!m_QueueSection.TryEnter())
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (m_Queue.Count == 0)
|
||||
return;
|
||||
|
||||
T item = m_Queue.Peek();
|
||||
|
||||
OnItemDequeued.Raise(this, new GenericEventArgs<T>(item));
|
||||
|
||||
m_DequeueTimer.Reset(BetweenMilliseconds);
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the dequeue timer elapses.
|
||||
/// </summary>
|
||||
private void DequeueTimerCallback()
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
m_Queue.Dequeue();
|
||||
SendNext();
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable/ICollection
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator());
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
m_QueueSection.Enter();
|
||||
|
||||
try
|
||||
{
|
||||
foreach (T item in this)
|
||||
{
|
||||
array.SetValue(item, index);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
m_QueueSection.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -135,6 +135,7 @@ namespace ICD.Common.Utils.Collections
|
||||
}
|
||||
set
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
@@ -172,6 +173,7 @@ namespace ICD.Common.Utils.Collections
|
||||
|
||||
public void Add(TKey key, TValue value)
|
||||
{
|
||||
// ReSharper disable once CompareNonConstrainedGenericWithNull
|
||||
if (key == null)
|
||||
throw new ArgumentNullException("key");
|
||||
|
||||
|
||||
29
ICD.Common.Utils/Comparers/FileNameEqualityComparer.cs
Normal file
29
ICD.Common.Utils/Comparers/FileNameEqualityComparer.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections.Generic;
|
||||
using ICD.Common.Utils.IO;
|
||||
|
||||
namespace ICD.Common.Utils.Comparers
|
||||
{
|
||||
public sealed class FileNameEqualityComparer : IEqualityComparer<string>
|
||||
{
|
||||
private static FileNameEqualityComparer s_Instance;
|
||||
|
||||
public static FileNameEqualityComparer Instance
|
||||
{
|
||||
get { return s_Instance ?? (s_Instance = new FileNameEqualityComparer()); }
|
||||
}
|
||||
|
||||
private FileNameEqualityComparer()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Equals(string x, string y)
|
||||
{
|
||||
return GetHashCode(x) == GetHashCode(y);
|
||||
}
|
||||
|
||||
public int GetHashCode(string obj)
|
||||
{
|
||||
return obj == null ? 0 : IcdPath.GetFileName(obj).GetHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
66
ICD.Common.Utils/Comparers/SequenceComparer.cs
Normal file
66
ICD.Common.Utils/Comparers/SequenceComparer.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ICD.Common.Utils.Comparers
|
||||
{
|
||||
public sealed class SequenceComparer<T> : IComparer<IEnumerable<T>>
|
||||
{
|
||||
private readonly IComparer<T> m_ItemComparer;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public SequenceComparer()
|
||||
: this(Comparer<T>.Default)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="comparer"></param>
|
||||
public SequenceComparer(IComparer<T> comparer)
|
||||
{
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
m_ItemComparer = comparer;
|
||||
}
|
||||
|
||||
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
|
||||
{
|
||||
if (x == null)
|
||||
throw new ArgumentNullException("x");
|
||||
|
||||
if (y == null)
|
||||
throw new ArgumentNullException("y");
|
||||
|
||||
using (IEnumerator<T> firstPos = x.GetEnumerator())
|
||||
{
|
||||
using (IEnumerator<T> secondPos = y.GetEnumerator())
|
||||
{
|
||||
bool hasFirst = firstPos.MoveNext();
|
||||
bool hasSecond = secondPos.MoveNext();
|
||||
|
||||
while (hasFirst && hasSecond)
|
||||
{
|
||||
int result = m_ItemComparer.Compare(firstPos.Current, secondPos.Current);
|
||||
if (result != 0)
|
||||
return result;
|
||||
|
||||
hasFirst = firstPos.MoveNext();
|
||||
hasSecond = secondPos.MoveNext();
|
||||
}
|
||||
|
||||
if (!hasFirst && !hasSecond)
|
||||
return 0;
|
||||
|
||||
if (!hasFirst)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
30
ICD.Common.Utils/EncodingUtils.cs
Normal file
30
ICD.Common.Utils/EncodingUtils.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using ICD.Common.Properties;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class EncodingUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Strips leading Byte Order Mark characters from the given UTF8 data.
|
||||
/// </summary>
|
||||
/// <param name="data">Input string to remove leading BOM chars from.</param>
|
||||
/// <returns>Input string with leading BOM chars removed.</returns>
|
||||
/// <exception cref="ArgumentNullException">Data is null.</exception>
|
||||
[PublicAPI]
|
||||
public static string StripUtf8Bom(string data)
|
||||
{
|
||||
if (data == null)
|
||||
throw new ArgumentNullException("data");
|
||||
|
||||
byte[] preamble = Encoding.UTF8.GetPreamble();
|
||||
string preambleString = Encoding.UTF8.GetString(preamble, 0, preamble.Length);
|
||||
|
||||
if (data.StartsWith(preambleString, StringComparison.Ordinal))
|
||||
data = data.Remove(0, preambleString.Length);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,6 +289,20 @@ namespace ICD.Common.Utils
|
||||
return s_EnumFlagsCache[type][value].Cast<T>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the set flags on the given enum type except 0.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetFlagsExceptNone<T>()
|
||||
{
|
||||
if (!IsEnumType<T>())
|
||||
throw new ArgumentException(string.Format("{0} is not an enum", typeof(T).Name));
|
||||
|
||||
T allValue = GetFlagsAllValue<T>();
|
||||
return GetFlagsExceptNone(allValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all of the set flags on the given enum except 0.
|
||||
/// </summary>
|
||||
|
||||
10
ICD.Common.Utils/Extensions/BoolExtensions.cs
Normal file
10
ICD.Common.Utils/Extensions/BoolExtensions.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class BoolExtensions
|
||||
{
|
||||
public static ushort ToUShort(this bool b)
|
||||
{
|
||||
return b ? (ushort)1 : (ushort)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,6 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <returns></returns>
|
||||
public static string ToLongTimeStringWithMilliseconds(this DateTime extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
// Todo - Better handle different cultures
|
||||
return extends.ToString("HH:mm:ss:fff");
|
||||
}
|
||||
|
||||
@@ -239,6 +239,26 @@ namespace ICD.Common.Utils.Extensions
|
||||
return change;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the sequence of items to the dictionary.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="items"></param>
|
||||
[PublicAPI]
|
||||
public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> extends, IEnumerable<KeyValuePair<TKey, TValue>> items)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (KeyValuePair<TKey, TValue> item in items)
|
||||
extends.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the sequence of items to the dictionary.
|
||||
/// </summary>
|
||||
|
||||
@@ -52,5 +52,13 @@ namespace ICD.Common.Utils.Extensions
|
||||
int num = (int)(object)value;
|
||||
return ((int)(object)extends & num) == num;
|
||||
}
|
||||
|
||||
public static ushort ToUShort(this Enum extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return (ushort)(object)extends;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,15 +42,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (predicate == null)
|
||||
throw new ArgumentNullException("predicate");
|
||||
|
||||
T output = defaultItem;
|
||||
|
||||
foreach (T item in extends.Where(predicate))
|
||||
{
|
||||
output = item;
|
||||
break;
|
||||
}
|
||||
|
||||
return output;
|
||||
T output;
|
||||
return extends.TryFirst(predicate, out output) ? output : defaultItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -163,24 +156,56 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (index < 0)
|
||||
throw new ArgumentException("Index must be greater or equal to 0", "index");
|
||||
|
||||
item = default(T);
|
||||
int eachIndex = 0;
|
||||
|
||||
foreach (T each in extends)
|
||||
try
|
||||
{
|
||||
if (eachIndex == index)
|
||||
{
|
||||
item = each;
|
||||
return true;
|
||||
}
|
||||
|
||||
eachIndex++;
|
||||
item = extends.ElementAt(index);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
/// <summary>
|
||||
/// Gets the element at the given index. Returns the specified default value if the index does not exist.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="defaultValue"></param>
|
||||
/// <returns></returns>
|
||||
public static T ElementAtOrDefault<T>(this IEnumerable<T> extends, int index, T defaultValue)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
T output;
|
||||
return extends.TryElementAt(index, out output) ? output : defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the two sequences for identical values.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static bool SequenceEqual<T>(this IEnumerable<T> extends, IEnumerable<T> other, IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return extends.SequenceEqual(other, comparer.Equals);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -196,7 +221,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
@@ -235,7 +260,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
return extends.ScrambledEquals(other, EqualityComparer<T>.Default);
|
||||
@@ -254,7 +279,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (extends == null)
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (comparer == null)
|
||||
@@ -304,9 +329,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (match == null)
|
||||
throw new ArgumentNullException("match");
|
||||
|
||||
return FindIndicesIterator(extends, match);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the indices that match the predicate.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="match"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<int> FindIndicesIterator<T>(IEnumerable<T> sequence, Predicate<T> match)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
foreach (T item in extends)
|
||||
foreach (T item in sequence)
|
||||
{
|
||||
if (match(item))
|
||||
yield return index;
|
||||
@@ -400,8 +437,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return PrependIterator(extends, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the item to the start of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> PrependIterator<T>(IEnumerable<T> sequence, T item)
|
||||
{
|
||||
yield return item;
|
||||
foreach (T next in extends)
|
||||
|
||||
foreach (T next in sequence)
|
||||
yield return next;
|
||||
}
|
||||
#endif
|
||||
@@ -422,9 +472,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
return PrependManyIterator(extends, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepends the items to the start of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> PrependManyIterator<T>(IEnumerable<T> sequence, params T[] items)
|
||||
{
|
||||
foreach (T item in items)
|
||||
yield return item;
|
||||
foreach (T each in extends)
|
||||
|
||||
foreach (T each in sequence)
|
||||
yield return each;
|
||||
}
|
||||
|
||||
@@ -441,8 +504,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
foreach (T first in extends)
|
||||
return AppendIterator(extends, item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the item to the end of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> AppendIterator<T>(IEnumerable<T> sequence, T item)
|
||||
{
|
||||
foreach (T first in sequence)
|
||||
yield return first;
|
||||
|
||||
yield return item;
|
||||
}
|
||||
#endif
|
||||
@@ -463,8 +539,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (items == null)
|
||||
throw new ArgumentNullException("items");
|
||||
|
||||
foreach (T each in extends)
|
||||
return AppendManyIterator(extends, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the items to the end of the sequence.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="items"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> AppendManyIterator<T>(IEnumerable<T> sequence, params T[] items)
|
||||
{
|
||||
foreach (T each in sequence)
|
||||
yield return each;
|
||||
|
||||
foreach (T item in items)
|
||||
yield return item;
|
||||
}
|
||||
@@ -482,9 +571,21 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return PadRightIterator(extends, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pads the given sequence to the given count size with default items.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> PadRightIterator<T>(IEnumerable<T> sequence, int count)
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
foreach (T item in extends)
|
||||
foreach (T item in sequence)
|
||||
{
|
||||
yield return item;
|
||||
index++;
|
||||
@@ -796,7 +897,18 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
using (IEnumerator<T> enumerator = extends.GetEnumerator())
|
||||
return PartitionIterator(extends, partitionSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Partitions a sequence into sequences of the given length.
|
||||
/// </summary>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="partitionSize"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<IEnumerable<T>> PartitionIterator<T>(IEnumerable<T> sequence, int partitionSize)
|
||||
{
|
||||
using (IEnumerator<T> enumerator = sequence.GetEnumerator())
|
||||
{
|
||||
while (enumerator.MoveNext())
|
||||
yield return YieldBatchElements(enumerator, partitionSize - 1);
|
||||
@@ -818,6 +930,17 @@ namespace ICD.Common.Utils.Extensions
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wraps this object instance into an IEnumerable consisting of a single item.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the object.</typeparam>
|
||||
/// <param name="item">The instance that will be wrapped.</param>
|
||||
/// <returns>An IEnumerable<T> consisting of a single item.</returns>
|
||||
public static IEnumerable<T> Yield<T>(this T item)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a sequence [A, B, C] returns a sequence [[A, B], [B, C]]
|
||||
/// </summary>
|
||||
@@ -825,6 +948,20 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T[]> GetAdjacentPairs<T>(this IEnumerable<T> extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return GetAdjacentPairsIterator(extends);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a sequence [A, B, C] returns a sequence [[A, B], [B, C]]
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T[]> GetAdjacentPairsIterator<T>(IEnumerable<T> extends)
|
||||
{
|
||||
T previous = default(T);
|
||||
bool first = true;
|
||||
@@ -832,7 +969,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
foreach (T item in extends)
|
||||
{
|
||||
if (!first)
|
||||
yield return new[] {previous, item};
|
||||
yield return new[] { previous, item };
|
||||
|
||||
first = false;
|
||||
previous = item;
|
||||
@@ -916,18 +1053,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
if (!sourceIterator.MoveNext())
|
||||
throw new InvalidOperationException("Sequence contains no elements");
|
||||
|
||||
TSource min = sourceIterator.Current;
|
||||
TKey minKey = selector(min);
|
||||
|
||||
while (sourceIterator.MoveNext())
|
||||
{
|
||||
TSource candidate = sourceIterator.Current;
|
||||
TKey candidateProjected = selector(candidate);
|
||||
|
||||
if (comparer.Compare(candidateProjected, minKey) >= 0)
|
||||
continue;
|
||||
|
||||
min = candidate;
|
||||
minKey = candidateProjected;
|
||||
}
|
||||
|
||||
return min;
|
||||
}
|
||||
}
|
||||
@@ -1001,16 +1142,33 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return ConsolidateIterator(extends, comparer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skips duplicate, consecutive items.
|
||||
/// E.g.
|
||||
/// [1, 2, 2, 3, 1, 1]
|
||||
/// Becomes
|
||||
/// [1, 2, 3, 1]
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="sequence"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> ConsolidateIterator<T>(IEnumerable<T> sequence, IComparer<T> comparer)
|
||||
{
|
||||
bool first = true;
|
||||
T last = default(T);
|
||||
|
||||
foreach (T item in extends)
|
||||
foreach (T item in sequence)
|
||||
{
|
||||
if (!first && comparer.Compare(last, item) == 0)
|
||||
continue;
|
||||
|
||||
first = false;
|
||||
last = item;
|
||||
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
@@ -1125,23 +1283,40 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <typeparam name="TFirst"></typeparam>
|
||||
/// <typeparam name="TSecond"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="other"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first,
|
||||
IEnumerable<TSecond> second,
|
||||
public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> extends,
|
||||
IEnumerable<TSecond> other,
|
||||
Func<TFirst, TSecond, TResult> callback)
|
||||
{
|
||||
if (first == null)
|
||||
throw new ArgumentNullException("first");
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
if (second == null)
|
||||
throw new ArgumentNullException("second");
|
||||
if (other == null)
|
||||
throw new ArgumentNullException("other");
|
||||
|
||||
if (callback == null)
|
||||
throw new ArgumentNullException("callback");
|
||||
|
||||
return ZipIterator(extends, other, callback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a specified function to the corresponding elements of two sequences, producing a sequence of the results.
|
||||
/// </summary>
|
||||
/// <typeparam name="TFirst"></typeparam>
|
||||
/// <typeparam name="TSecond"></typeparam>
|
||||
/// <typeparam name="TResult"></typeparam>
|
||||
/// <param name="first"></param>
|
||||
/// <param name="second"></param>
|
||||
/// <param name="callback"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first,
|
||||
IEnumerable<TSecond> second,
|
||||
Func<TFirst, TSecond, TResult> callback)
|
||||
{
|
||||
using (IEnumerator<TFirst> enumerator1 = first.GetEnumerator())
|
||||
{
|
||||
using (IEnumerator<TSecond> enumerator2 = second.GetEnumerator())
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -160,6 +161,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
if (extends.TokenType == JsonToken.String)
|
||||
return EnumUtils.Parse<T>(extends.GetValueAsString(), true);
|
||||
|
||||
return (T)(object)extends.GetValueAsInt();
|
||||
}
|
||||
|
||||
@@ -252,17 +254,30 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("read");
|
||||
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
yield break;
|
||||
return Enumerable.Empty<TItem>();
|
||||
|
||||
if (reader.TokenType != JsonToken.StartArray)
|
||||
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartArray, reader.TokenType));
|
||||
|
||||
return DeserializeArrayIterator(extends, reader, read);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes an array of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TItem"></typeparam>
|
||||
/// <param name="serializer"></param>
|
||||
/// <param name="reader"></param>
|
||||
/// <param name="read"></param>
|
||||
private static IEnumerable<TItem> DeserializeArrayIterator<TItem>(JsonSerializer serializer, JsonReader reader,
|
||||
Func<JsonSerializer, JsonReader, TItem> read)
|
||||
{
|
||||
// Step into the first value
|
||||
reader.Read();
|
||||
|
||||
while (reader.TokenType != JsonToken.EndArray)
|
||||
{
|
||||
TItem output = read(extends, reader);
|
||||
TItem output = read(serializer, reader);
|
||||
yield return output;
|
||||
|
||||
// Read out of the last value
|
||||
@@ -308,7 +323,8 @@ namespace ICD.Common.Utils.Extensions
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="reader"></param>
|
||||
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionary<TKey, TValue>(this JsonSerializer extends, JsonReader reader)
|
||||
public static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionary<TKey, TValue>(this JsonSerializer extends,
|
||||
JsonReader reader)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
@@ -317,11 +333,24 @@ namespace ICD.Common.Utils.Extensions
|
||||
throw new ArgumentNullException("reader");
|
||||
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
yield break;
|
||||
return Enumerable.Empty<KeyValuePair<TKey, TValue>>();
|
||||
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
throw new FormatException(string.Format("Expected token {0} got {1}", JsonToken.StartObject, reader.TokenType));
|
||||
|
||||
return DeserializeDictionaryIterator<TKey, TValue>(extends, reader);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializes a dictionary of items from the reader's current value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TKey"></typeparam>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
/// <param name="serializer"></param>
|
||||
/// <param name="reader"></param>
|
||||
private static IEnumerable<KeyValuePair<TKey, TValue>> DeserializeDictionaryIterator<TKey, TValue>(
|
||||
JsonSerializer serializer, JsonReader reader)
|
||||
{
|
||||
// Step into the first key
|
||||
reader.Read();
|
||||
|
||||
@@ -332,7 +361,7 @@ namespace ICD.Common.Utils.Extensions
|
||||
// Step into the value
|
||||
reader.Read();
|
||||
|
||||
TValue value = extends.Deserialize<TValue>(reader);
|
||||
TValue value = serializer.Deserialize<TValue>(reader);
|
||||
|
||||
yield return new KeyValuePair<TKey, TValue>(key, value);
|
||||
|
||||
|
||||
@@ -83,5 +83,48 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
return extends.GetCustomAttributes<T>(inherits).First();
|
||||
}
|
||||
|
||||
#if NETSTANDARD
|
||||
/// <summary>
|
||||
/// Returns the custom attributes attached to the member.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="inherits"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>(this Type extends)
|
||||
where T : Attribute
|
||||
{
|
||||
return extends.GetCustomAttributes<T>(true)
|
||||
.Union(extends.GetInterfaces()
|
||||
.SelectMany(interfaceType => interfaceType
|
||||
.GetCustomAttributes<T>(true)))
|
||||
.Distinct();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the custom attributes attached to the member.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="inherits"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<T> GetCustomAttributesIncludingBaseInterfaces<T>(this MemberInfo extends)
|
||||
where T : Attribute
|
||||
{
|
||||
return extends.GetCustomAttributes<T>(true)
|
||||
.Union(extends.DeclaringType?
|
||||
.GetInterfaces()
|
||||
.SelectMany(interfaceType => interfaceType
|
||||
.GetMember(
|
||||
extends.Name,
|
||||
extends.MemberType,
|
||||
BindingFlags.Instance)
|
||||
.FirstOrDefault()?
|
||||
.GetCustomAttributes<T>(true) ?? Enumerable.Empty<T>())?
|
||||
.Except(null) ?? Enumerable.Empty<T>())
|
||||
.Distinct();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,21 +88,34 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (count < 1)
|
||||
throw new ArgumentException("Value must be greater or equal to 1", "count");
|
||||
|
||||
return SplitIterator(extends, delimeter, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits the string by the given delimiter, returning up to the given number of substrings.
|
||||
/// E.g. "a:b:c".Split(':', 2) returns ["a", "b:c"]
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="delimeter"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<string> SplitIterator(string value, char delimeter, int count)
|
||||
{
|
||||
if (count < 2)
|
||||
{
|
||||
yield return extends;
|
||||
yield return value;
|
||||
yield break;
|
||||
}
|
||||
|
||||
int index = extends.IndexOf(delimeter);
|
||||
int index = value.IndexOf(delimeter);
|
||||
if (index < 0)
|
||||
{
|
||||
yield return extends;
|
||||
yield return value;
|
||||
yield break;
|
||||
}
|
||||
|
||||
string first = extends.Substring(0, index);
|
||||
string second = extends.Substring(index + 1);
|
||||
string first = value.Substring(0, index);
|
||||
string second = value.Substring(index + 1);
|
||||
count--;
|
||||
|
||||
yield return first;
|
||||
@@ -131,8 +144,6 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string by a given substring.
|
||||
/// Taken from
|
||||
/// https://social.msdn.microsoft.com/Forums/en-US/914a350f-e0e9-45e0-91a4-6b4b2168e780/string-split-function
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <param name="delimeter"></param>
|
||||
@@ -146,19 +157,32 @@ namespace ICD.Common.Utils.Extensions
|
||||
if (delimeter == null)
|
||||
throw new ArgumentNullException("delimeter");
|
||||
|
||||
return SplitIterator(extends, delimeter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits a string by a given substring.
|
||||
/// Taken from
|
||||
/// https://social.msdn.microsoft.com/Forums/en-US/914a350f-e0e9-45e0-91a4-6b4b2168e780/string-split-function
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="delimeter"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<string> SplitIterator(string value, string delimeter)
|
||||
{
|
||||
int dSum = 0;
|
||||
int sSum = 0;
|
||||
int length = extends.Length;
|
||||
int length = value.Length;
|
||||
int delimiterLength = delimeter.Length;
|
||||
|
||||
if (delimiterLength == 0 || length == 0 || length < delimiterLength)
|
||||
{
|
||||
yield return extends;
|
||||
yield return value;
|
||||
yield break;
|
||||
}
|
||||
|
||||
char[] cd = delimeter.ToCharArray();
|
||||
char[] cs = extends.ToCharArray();
|
||||
char[] cs = value.ToCharArray();
|
||||
|
||||
for (int i = 0; i < delimiterLength; i++)
|
||||
{
|
||||
@@ -169,22 +193,22 @@ namespace ICD.Common.Utils.Extensions
|
||||
int start = 0;
|
||||
for (int i = start; i < length - delimiterLength; i++)
|
||||
{
|
||||
if (i >= start && dSum == sSum && extends.Substring(i, delimiterLength) == delimeter)
|
||||
if (i >= start && dSum == sSum && value.Substring(i, delimiterLength) == delimeter)
|
||||
{
|
||||
yield return extends.Substring(start, i - start);
|
||||
yield return value.Substring(start, i - start);
|
||||
start = i + delimiterLength;
|
||||
}
|
||||
|
||||
sSum += cs[i + delimiterLength] - cs[i];
|
||||
}
|
||||
|
||||
if (dSum == sSum && extends.Substring(length - delimiterLength, delimiterLength) == delimeter)
|
||||
if (dSum == sSum && value.Substring(length - delimiterLength, delimiterLength) == delimeter)
|
||||
{
|
||||
yield return extends.Substring(start, length - delimiterLength - start);
|
||||
yield return value.Substring(start, length - delimiterLength - start);
|
||||
yield return string.Empty;
|
||||
}
|
||||
else
|
||||
yield return extends.Substring(start, length - start);
|
||||
yield return value.Substring(start, length - start);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -7,9 +7,6 @@ namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static string ToReadableString(this TimeSpan extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
if (extends.Days > 0)
|
||||
|
||||
@@ -210,18 +210,15 @@ namespace ICD.Common.Utils.Extensions
|
||||
|
||||
if (!s_TypeBaseTypes.ContainsKey(extends))
|
||||
{
|
||||
Type[] types = GetBaseTypesInternal(extends).ToArray();
|
||||
Type[] types = GetBaseTypesIterator(extends).ToArray();
|
||||
s_TypeBaseTypes.Add(extends, types);
|
||||
}
|
||||
|
||||
return s_TypeBaseTypes[extends];
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetBaseTypesInternal(Type type)
|
||||
private static IEnumerable<Type> GetBaseTypesIterator(Type type)
|
||||
{
|
||||
if (type == null)
|
||||
throw new ArgumentNullException("type");
|
||||
|
||||
do
|
||||
{
|
||||
type = type
|
||||
|
||||
34
ICD.Common.Utils/Extensions/UriExtensions.cs
Normal file
34
ICD.Common.Utils/Extensions/UriExtensions.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class UriExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the username from the given URI.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetUserName(this Uri extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.UserInfo.Split(':').FirstOrDefault(string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the password from the given URI.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetPassword(this Uri extends)
|
||||
{
|
||||
if (extends == null)
|
||||
throw new ArgumentNullException("extends");
|
||||
|
||||
return extends.UserInfo.Split(':').Skip(1).FirstOrDefault(string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
ICD.Common.Utils/Extensions/UshortExtensions.cs
Normal file
10
ICD.Common.Utils/Extensions/UshortExtensions.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace ICD.Common.Utils.Extensions
|
||||
{
|
||||
public static class UShortExtensions
|
||||
{
|
||||
public static bool ToBool(this ushort u)
|
||||
{
|
||||
return u != 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,52 +1,51 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Condition=" '$(EAZFUSCATOR_NET_HOME)' != '' and Exists('$(EAZFUSCATOR_NET_HOME)\Integration\MSBuild\Eazfuscator.NET.targets') " Project="$(EAZFUSCATOR_NET_HOME)\Integration\MSBuild\Eazfuscator.NET.targets" />
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<AssemblyName>ICD.Common.Utils</AssemblyName>
|
||||
<RootNamespace>$(AssemblyName)</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Deterministic>true</Deterministic>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<Deterministic>true</Deterministic>
|
||||
<Authors>Chris Cameron, Jeff Thompson</Authors>
|
||||
<PackageId>ICD.Common.Utils</PackageId>
|
||||
<PackageProjectUrl></PackageProjectUrl>
|
||||
<RepositoryUrl>https://cs-gogs.icdpf.net/Common/Utils</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<EazfuscatorIntegration>MSBuild</EazfuscatorIntegration>
|
||||
<EazfuscatorActiveConfiguration>Release</EazfuscatorActiveConfiguration>
|
||||
<EazfuscatorCompatibilityVersion>2018.2</EazfuscatorCompatibilityVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DefineConstants>TRACE;DEBUG;STANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
|
||||
<DefineConstants>TRACE;STANDARD</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="SIMPLSharpLogs\**" />
|
||||
<EmbeddedResource Remove="SIMPLSharpLogs\**" />
|
||||
<None Remove="SIMPLSharpLogs\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="ObfuscationSettings.cs" />
|
||||
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="ICD.Common.projectinfo" />
|
||||
<None Remove="ICD.Common_SimplSharp.suo" />
|
||||
<None Remove="ICD.SimplSharp.projectinfo" />
|
||||
<None Remove="Properties\ControlSystem.cfg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.0.4" />
|
||||
<PackageReference Include="Microsoft.Data.SQLite" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>REM Eazfuscator.NET is integrated with this project at MSBuild level: https://www.gapotchenko.com/eazfuscator.net/kb/100036</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -74,9 +74,16 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Attributes\AbstractIcdAttribute.cs" />
|
||||
<Compile Include="Collections\BiDictionary.cs" />
|
||||
<Compile Include="Collections\IcdOrderedDictionary.cs" />
|
||||
<Compile Include="Collections\PriorityQueue.cs" />
|
||||
<Compile Include="Collections\RateLimitedEventQueue.cs" />
|
||||
<Compile Include="Collections\WeakKeyDictionary.cs" />
|
||||
<Compile Include="Comparers\FileNameEqualityComparer.cs" />
|
||||
<Compile Include="Comparers\PredicateComparer.cs" />
|
||||
<Compile Include="Comparers\SequenceComparer.cs" />
|
||||
<Compile Include="ConsoleColor.cs" />
|
||||
<Compile Include="EncodingUtils.cs" />
|
||||
<Compile Include="EventArguments\BoolEventArgs.cs" />
|
||||
<Compile Include="EventArguments\CharEventArgs.cs" />
|
||||
<Compile Include="EventArguments\DateTimeEventArgs.cs" />
|
||||
@@ -88,9 +95,16 @@
|
||||
<Compile Include="EventArguments\UShortEventArgs.cs" />
|
||||
<Compile Include="EventArguments\XmlRecursionEventArgs.cs" />
|
||||
<None Include="ObfuscationSettings.cs" />
|
||||
<Compile Include="Extensions\BoolExtensions.cs" />
|
||||
<Compile Include="Extensions\ByteExtensions.cs" />
|
||||
<Compile Include="Extensions\ListExtensions.cs" />
|
||||
<Compile Include="Comparers\PredicateEqualityComparer.cs" />
|
||||
<Compile Include="Extensions\UriExtensions.cs" />
|
||||
<Compile Include="Extensions\UshortExtensions.cs" />
|
||||
<Compile Include="IcdUriBuilder.cs" />
|
||||
<Compile Include="IO\Compression\IcdZipEntry.cs" />
|
||||
<Compile Include="IO\IcdBinaryReader.cs" />
|
||||
<Compile Include="IO\eSeekOrigin.cs" />
|
||||
<Compile Include="ProcessorUtils.SimplSharp.cs" />
|
||||
<Compile Include="ProcessorUtils.Standard.cs" />
|
||||
<Compile Include="ProgramUtils.SimplSharp.cs" />
|
||||
@@ -115,7 +129,7 @@
|
||||
<Compile Include="IcdEnvironment.cs" />
|
||||
<Compile Include="IcdEnvironment.SimplSharp.cs" />
|
||||
<Compile Include="IcdEnvironment.Standard.cs" />
|
||||
<Compile Include="IcdZip.cs" />
|
||||
<Compile Include="IO\Compression\IcdZip.cs" />
|
||||
<Compile Include="IO\IcdDirectory.cs" />
|
||||
<Compile Include="EnumUtils.cs" />
|
||||
<Compile Include="Extensions\AssemblyExtensions.cs" />
|
||||
@@ -171,6 +185,7 @@
|
||||
<Compile Include="Timers\Repeater.cs" />
|
||||
<Compile Include="Timers\SafeTimer.cs" />
|
||||
<Compile Include="TryUtils.cs" />
|
||||
<Compile Include="UriUtils.cs" />
|
||||
<Compile Include="Xml\IcdXmlConvert.cs" />
|
||||
<Compile Include="Xml\IcdXmlDocument.cs" />
|
||||
<Compile Include="Xml\IcdXmlException.cs" />
|
||||
|
||||
173
ICD.Common.Utils/IO/Compression/IcdZip.cs
Normal file
173
ICD.Common.Utils/IO/Compression/IcdZip.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Utils for managing archives.
|
||||
/// </summary>
|
||||
public static class IcdZip
|
||||
{
|
||||
private const int DIRECTORY_SIGNATURE = 0x06054B50;
|
||||
private const int ENTRY_SIGNATURE = 0x02014B50;
|
||||
|
||||
/// <summary>
|
||||
/// Unzips the archive at the given path.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="outputPath"></param>
|
||||
public static void Unzip(string path, string outputPath)
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath);
|
||||
if (result != CrestronZIP.ResultCode.ZR_OK)
|
||||
throw new InvalidOperationException(result.ToString());
|
||||
#else
|
||||
using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read))
|
||||
archive.ExtractToDirectory(outputPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence of files names in the archive at the given path.
|
||||
/// </summary>
|
||||
public static IEnumerable<string> GetFileNames(string path)
|
||||
{
|
||||
return GetEntries(path).Select(e => e.Name)
|
||||
.Where(f => !f.EndsWith("/"))
|
||||
.OrderBy(f => f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets sequence of zip file entries in the archive at the given path.
|
||||
/// </summary>
|
||||
public static IEnumerable<IcdZipEntry> GetEntries(string path)
|
||||
{
|
||||
/*
|
||||
Copyright (c) 2012-2013 Alexey Yakovlev
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using (IcdStream stream = IcdFile.OpenRead(path))
|
||||
{
|
||||
using (IcdBinaryReader reader = new IcdBinaryReader(stream))
|
||||
{
|
||||
if (stream.Length < 22)
|
||||
yield break;
|
||||
|
||||
stream.Seek(-22, eSeekOrigin.End);
|
||||
|
||||
// find directory signature
|
||||
while (reader.ReadInt32() != DIRECTORY_SIGNATURE)
|
||||
{
|
||||
if (stream.Position <= 5)
|
||||
yield break;
|
||||
|
||||
// move 1 byte back
|
||||
stream.Seek(-5, eSeekOrigin.Current);
|
||||
}
|
||||
|
||||
// read directory properties
|
||||
stream.Seek(6, eSeekOrigin.Current);
|
||||
ushort entries = reader.ReadUInt16();
|
||||
int difSize = reader.ReadInt32();
|
||||
uint dirOffset = reader.ReadUInt32();
|
||||
stream.Seek(dirOffset, eSeekOrigin.Begin);
|
||||
|
||||
// read directory entries
|
||||
for (int i = 0; i < entries; i++)
|
||||
{
|
||||
if (reader.ReadInt32() != ENTRY_SIGNATURE)
|
||||
continue;
|
||||
|
||||
// read file properties
|
||||
reader.ReadInt32();
|
||||
bool utf8 = (reader.ReadInt16() & 0x0800) != 0;
|
||||
short method = reader.ReadInt16();
|
||||
int timestamp = reader.ReadInt32();
|
||||
uint crc32 = reader.ReadUInt32();
|
||||
int compressedSize = reader.ReadInt32();
|
||||
int fileSize = reader.ReadInt32();
|
||||
short fileNameSize = reader.ReadInt16();
|
||||
short extraSize = reader.ReadInt16();
|
||||
short commentSize = reader.ReadInt16();
|
||||
int headerOffset = reader.ReadInt32();
|
||||
reader.ReadInt32();
|
||||
int fileHeaderOffset = reader.ReadInt32();
|
||||
byte[] fileNameBytes = reader.ReadBytes(fileNameSize);
|
||||
stream.Seek(extraSize, eSeekOrigin.Current);
|
||||
byte[] fileCommentBytes = reader.ReadBytes(commentSize);
|
||||
int fileDataOffset = CalculateFileDataOffset(stream, reader, fileHeaderOffset);
|
||||
|
||||
// decode zip file entry
|
||||
Encoding encoder = utf8 ? Encoding.UTF8 : Encoding.Default;
|
||||
|
||||
yield return new IcdZipEntry
|
||||
{
|
||||
Name = encoder.GetString(fileNameBytes, 0, fileNameBytes.Length),
|
||||
Comment = encoder.GetString(fileCommentBytes, 0, fileCommentBytes.Length),
|
||||
Crc32 = crc32,
|
||||
CompressedSize = compressedSize,
|
||||
OriginalSize = fileSize,
|
||||
HeaderOffset = fileHeaderOffset,
|
||||
DataOffset = fileDataOffset,
|
||||
Deflated = method == 8,
|
||||
Timestamp = ConvertToDateTime(timestamp)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int CalculateFileDataOffset(IcdStream stream, IcdBinaryReader reader, int fileHeaderOffset)
|
||||
{
|
||||
long position = stream.Position;
|
||||
stream.Seek(fileHeaderOffset + 26, eSeekOrigin.Begin);
|
||||
short fileNameSize = reader.ReadInt16();
|
||||
short extraSize = reader.ReadInt16();
|
||||
|
||||
int fileOffset = (int)stream.Position + fileNameSize + extraSize;
|
||||
stream.Seek(position, eSeekOrigin.Begin);
|
||||
return fileOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts DOS timestamp to a <see cref="DateTime"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="dosTimestamp">The DOS timestamp.</param>
|
||||
/// <returns>The <see cref="DateTime"/> instance.</returns>
|
||||
private static DateTime ConvertToDateTime(int dosTimestamp)
|
||||
{
|
||||
return new DateTime((dosTimestamp >> 25) + 1980,
|
||||
(dosTimestamp >> 21) & 15,
|
||||
(dosTimestamp >> 16) & 31,
|
||||
(dosTimestamp >> 11) & 31,
|
||||
(dosTimestamp >> 5) & 63,
|
||||
(dosTimestamp & 31) * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
59
ICD.Common.Utils/IO/Compression/IcdZipEntry.cs
Normal file
59
ICD.Common.Utils/IO/Compression/IcdZipEntry.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils.IO.Compression
|
||||
{
|
||||
/// <summary>
|
||||
/// Zip archive entry.
|
||||
/// </summary>
|
||||
public sealed class IcdZipEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name of a file or a directory.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comment.
|
||||
/// </summary>
|
||||
public string Comment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the CRC32.
|
||||
/// </summary>
|
||||
public uint Crc32 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compressed size of the file.
|
||||
/// </summary>
|
||||
public int CompressedSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the original size of the file.
|
||||
/// </summary>
|
||||
public int OriginalSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="IcdZipEntry" /> is deflated.
|
||||
/// </summary>
|
||||
public bool Deflated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a directory.
|
||||
/// </summary>
|
||||
public bool IsDirectory { get { return Name.EndsWith("/"); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="IcdZipEntry" /> is a file.
|
||||
/// </summary>
|
||||
public bool IsFile { get { return !IsDirectory; } }
|
||||
|
||||
public int HeaderOffset { get; set; }
|
||||
|
||||
public int DataOffset { get; set; }
|
||||
}
|
||||
}
|
||||
69
ICD.Common.Utils/IO/IcdBinaryReader.cs
Normal file
69
ICD.Common.Utils/IO/IcdBinaryReader.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public sealed class IcdBinaryReader : IDisposable
|
||||
{
|
||||
private readonly BinaryReader m_Reader;
|
||||
|
||||
public BinaryReader WrappedReader { get { return m_Reader; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
public IcdBinaryReader(IcdStream stream)
|
||||
: this(new BinaryReader(stream.WrappedStream))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="reader"></param>
|
||||
public IcdBinaryReader(BinaryReader reader)
|
||||
{
|
||||
m_Reader = reader;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_Reader.Dispose();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
m_Reader.Close();
|
||||
}
|
||||
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
return m_Reader.ReadUInt16();
|
||||
}
|
||||
|
||||
public int ReadInt32()
|
||||
{
|
||||
return m_Reader.ReadInt32();
|
||||
}
|
||||
|
||||
public short ReadInt16()
|
||||
{
|
||||
return m_Reader.ReadInt16();
|
||||
}
|
||||
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
return m_Reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public byte[] ReadBytes(short numberOfBytesToRead)
|
||||
{
|
||||
return m_Reader.ReadBytes(numberOfBytesToRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,11 @@ namespace ICD.Common.Utils.IO
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
public static IcdStream OpenRead(string path)
|
||||
{
|
||||
return new IcdFileStream(File.OpenRead(path));
|
||||
}
|
||||
|
||||
[PublicAPI]
|
||||
public static IcdFileStream OpenWrite(string path)
|
||||
{
|
||||
|
||||
@@ -68,5 +68,17 @@ namespace ICD.Common.Utils.IO
|
||||
|
||||
return Path.ChangeExtension(path, ext);
|
||||
}
|
||||
|
||||
public static string GetRelativePath(string folder, string filespec)
|
||||
{
|
||||
Uri pathUri = new Uri(filespec);
|
||||
// Folders must end in a slash
|
||||
if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString()))
|
||||
{
|
||||
folder += Path.DirectorySeparatorChar;
|
||||
}
|
||||
Uri folderUri = new Uri(folder);
|
||||
return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ namespace ICD.Common.Utils.IO
|
||||
|
||||
public Stream WrappedStream { get { return m_Stream; } }
|
||||
|
||||
public long Length { get { return m_Stream.Length; } }
|
||||
|
||||
public long Position { get { return m_Stream.Position; } }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
@@ -29,5 +33,10 @@ namespace ICD.Common.Utils.IO
|
||||
{
|
||||
m_Stream.Dispose();
|
||||
}
|
||||
|
||||
public void Seek(long offset, eSeekOrigin seekOrigin)
|
||||
{
|
||||
m_Stream.Seek(offset, seekOrigin.ToSeekOrigin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
40
ICD.Common.Utils/IO/eSeekOrigin.cs
Normal file
40
ICD.Common.Utils/IO/eSeekOrigin.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
#else
|
||||
using System.IO;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils.IO
|
||||
{
|
||||
public enum eSeekOrigin
|
||||
{
|
||||
Begin,
|
||||
Current,
|
||||
End,
|
||||
}
|
||||
|
||||
public static class SeekOriginExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the seek origin enum to a system seek origin.
|
||||
/// </summary>
|
||||
/// <param name="extends"></param>
|
||||
/// <returns></returns>
|
||||
public static SeekOrigin ToSeekOrigin(this eSeekOrigin extends)
|
||||
{
|
||||
switch (extends)
|
||||
{
|
||||
case eSeekOrigin.Begin:
|
||||
return SeekOrigin.Begin;
|
||||
case eSeekOrigin.Current:
|
||||
return SeekOrigin.Current;
|
||||
case eSeekOrigin.End:
|
||||
return SeekOrigin.End;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException("extends");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using ICD.Common.Properties;
|
||||
#if SIMPLSHARP
|
||||
@@ -74,7 +75,7 @@ namespace ICD.Common.Utils
|
||||
#else
|
||||
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
|
||||
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
|
||||
System.Console.Error.WriteLine(message);
|
||||
System.Console.WriteLine(message);
|
||||
System.Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
@@ -107,7 +108,7 @@ namespace ICD.Common.Utils
|
||||
#else
|
||||
System.Console.ForegroundColor = color.ToForegroundConsoleColor();
|
||||
System.Console.BackgroundColor = color.ToBackgroundConsoleColor();
|
||||
System.Console.Error.Write(message);
|
||||
System.Console.Write(message);
|
||||
System.Console.ResetColor();
|
||||
#endif
|
||||
}
|
||||
@@ -138,8 +139,10 @@ namespace ICD.Common.Utils
|
||||
return false;
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(str => callback(str), command, help, (ConsoleAccessLevelEnum)(int)accessLevel);
|
||||
#endif
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,16 +8,6 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
public static class IcdErrorLog
|
||||
{
|
||||
private const string CONSOLE_RED = "\x1B[31;1m";
|
||||
private const string CONSOLE_GREEN = "\x1B[32;1m";
|
||||
private const string CONSOLE_YELLOW = "\x1B[33;1m";
|
||||
private const string CONSOLE_BLUE = "\x1B[34;1m";
|
||||
//private const string CONSOLE_MAGENTA = "\x1B[35;1m";
|
||||
private const string CONSOLE_CYAN = "\x1B[36;1m";
|
||||
//private const string CONSOLE_WHITE = "\x1B[37;1m";
|
||||
private const string CONSOLE_YELLOW_ON_RED_BACKGROUND = "\x1B[93;41m";
|
||||
private const string CONSOLE_RESET = "\x1B[0m";
|
||||
|
||||
private static readonly SafeCriticalSection s_LoggingSection;
|
||||
|
||||
/// <summary>
|
||||
@@ -36,7 +26,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_RED);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_RED);
|
||||
ErrorLog.Error(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Red;
|
||||
@@ -64,7 +54,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_YELLOW);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_YELLOW);
|
||||
ErrorLog.Warn(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
@@ -92,7 +82,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_BLUE);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_BLUE);
|
||||
ErrorLog.Notice(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Blue;
|
||||
@@ -120,7 +110,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_GREEN);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_GREEN);
|
||||
ErrorLog.Ok(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Green;
|
||||
@@ -148,7 +138,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_YELLOW_ON_RED_BACKGROUND);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_YELLOW_ON_RED_BACKGROUND);
|
||||
ErrorLog.Exception(message, ex);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
@@ -179,7 +169,7 @@ namespace ICD.Common.Utils
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
message = FormatConsoleColor(message, CONSOLE_CYAN);
|
||||
message = FormatConsoleColor(message, ConsoleColorExtensions.CONSOLE_CYAN);
|
||||
ErrorLog.Info(message);
|
||||
#else
|
||||
System.Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
@@ -207,7 +197,7 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
private static string FormatConsoleColor(string text, string color)
|
||||
{
|
||||
return string.Format("{0}{1}{2}", color, text, CONSOLE_RESET);
|
||||
return string.Format("{0}{1}{2}", color, text, ConsoleColorExtensions.CONSOLE_RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
157
ICD.Common.Utils/IcdUriBuilder.cs
Normal file
157
ICD.Common.Utils/IcdUriBuilder.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using ICD.Common.Utils.Extensions;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple Compact Framework UriBuilder implementation.
|
||||
/// </summary>
|
||||
public sealed class IcdUriBuilder
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fragment portion of the URI.
|
||||
/// </summary>
|
||||
public string Fragment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Domain Name System (DNS) host name or IP address of a server.
|
||||
/// </summary>
|
||||
public string Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password associated with the user that accesses the URI.
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the path to the resource referenced by the URI.
|
||||
/// </summary>
|
||||
public string Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the port number of the URI.
|
||||
/// </summary>
|
||||
public ushort Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets any query information included in the URI.
|
||||
/// </summary>
|
||||
public string Query { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scheme name of the URI.
|
||||
/// </summary>
|
||||
public string Scheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user name associated with the user that accesses the URI.
|
||||
/// </summary>
|
||||
public string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Uri instance constructed by the specified UriBuilder instance.
|
||||
/// </summary>
|
||||
public Uri Uri { get { return new Uri(ToString()); } }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
public IcdUriBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
public IcdUriBuilder(string uri)
|
||||
: this(new Uri(uri))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
public IcdUriBuilder(Uri uri)
|
||||
{
|
||||
Fragment = uri.Fragment;
|
||||
Host = uri.Host;
|
||||
Password = uri.GetPassword();
|
||||
Path = uri.AbsolutePath;
|
||||
Port = (ushort)uri.Port;
|
||||
Query = uri.Query;
|
||||
Scheme = uri.Scheme;
|
||||
UserName = uri.GetUserName();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the string representation for the URI.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
// URI = scheme:[//authority]path[?query][#fragment]
|
||||
// authority = [userinfo@]host[:port]
|
||||
// userinfo = username[:password]
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
// Scheme
|
||||
string scheme = string.IsNullOrEmpty(Scheme) ? "http" : Scheme;
|
||||
builder.Append(scheme);
|
||||
builder.Append(':');
|
||||
|
||||
// Authority
|
||||
builder.Append("//");
|
||||
|
||||
if (!string.IsNullOrEmpty(UserName))
|
||||
{
|
||||
builder.Append(UserName);
|
||||
|
||||
if (!string.IsNullOrEmpty(Password))
|
||||
{
|
||||
builder.Append(':');
|
||||
builder.Append(Password);
|
||||
}
|
||||
|
||||
builder.Append('@');
|
||||
}
|
||||
|
||||
string host = string.IsNullOrEmpty(Host) ? "localhost" : Host;
|
||||
builder.Append(host);
|
||||
|
||||
if (Port != 0)
|
||||
{
|
||||
builder.Append(':');
|
||||
builder.Append(Port);
|
||||
}
|
||||
|
||||
// Path
|
||||
if (string.IsNullOrEmpty(Path) || !Path.StartsWith("/"))
|
||||
builder.Append('/');
|
||||
builder.Append(Path);
|
||||
|
||||
// Query
|
||||
if (!string.IsNullOrEmpty(Query))
|
||||
{
|
||||
builder.Append('?');
|
||||
builder.Append(Query);
|
||||
}
|
||||
|
||||
// Fragment
|
||||
if (!string.IsNullOrEmpty(Fragment))
|
||||
{
|
||||
builder.Append('#');
|
||||
builder.Append(Fragment);
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using System;
|
||||
#if SIMPLSHARP
|
||||
using Crestron.SimplSharp;
|
||||
#else
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Utils for managing archives.
|
||||
/// </summary>
|
||||
public static class IcdZip
|
||||
{
|
||||
/// <summary>
|
||||
/// Unzips the archive at the given path.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="outputPath"></param>
|
||||
/// <param name="message"></param>
|
||||
public static bool Unzip(string path, string outputPath, out string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
CrestronZIP.ResultCode result = CrestronZIP.Unzip(path, outputPath);
|
||||
message = result.ToString();
|
||||
return result == CrestronZIP.ResultCode.ZR_OK;
|
||||
#else
|
||||
using (ZipArchive archive = ZipFile.Open(path, ZipArchiveMode.Read))
|
||||
archive.ExtractToDirectory(outputPath);
|
||||
message = "Success";
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
message = e.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,6 +71,12 @@ namespace ICD.Common.Utils.Json
|
||||
string itemString = (string)token.SelectToken(ITEM_TOKEN);
|
||||
Type type = Type.GetType(typeString);
|
||||
|
||||
if (type == null)
|
||||
{
|
||||
typeString = typeString.Replace("_SimplSharp", "").Replace("_NetStandard", "");
|
||||
type = Type.GetType(typeString);
|
||||
}
|
||||
|
||||
return JsonConvert.DeserializeObject(itemString, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,18 +172,29 @@ namespace ICD.Common.Utils
|
||||
if (numbers == null)
|
||||
throw new ArgumentNullException("numbers");
|
||||
|
||||
return GetRangesIterator(numbers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes a sequence of numbers:
|
||||
/// 1, 3, 5, 6, 7, 8, 9, 10, 12
|
||||
/// And calculates the continuous ranges:
|
||||
/// (1, 1), (3, 3), (5, 10), (12, 12)
|
||||
/// </summary>
|
||||
private static IEnumerable<int[]> GetRangesIterator(IEnumerable<int> numbers)
|
||||
{
|
||||
int[] currentRange = null;
|
||||
|
||||
foreach (int number in numbers.Order())
|
||||
{
|
||||
if (currentRange == null)
|
||||
currentRange = new[] {number, number};
|
||||
currentRange = new[] { number, number };
|
||||
else if (currentRange[1] == number - 1)
|
||||
currentRange = new[] {currentRange[0], number};
|
||||
currentRange = new[] { currentRange[0], number };
|
||||
else
|
||||
{
|
||||
yield return currentRange;
|
||||
currentRange = new[] {number, number};
|
||||
currentRange = new[] { number, number };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,8 +97,7 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void RestartProgram()
|
||||
{
|
||||
// TODO
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -107,8 +106,7 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static void Reboot()
|
||||
{
|
||||
// TODO
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -4,4 +4,4 @@ using System.Reflection;
|
||||
[assembly: AssemblyCompany("ICD Systems")]
|
||||
[assembly: AssemblyProduct("ICD.Common.Utils")]
|
||||
[assembly: AssemblyCopyright("Copyright © ICD Systems 2018")]
|
||||
[assembly: AssemblyVersion("3.0.0.0")]
|
||||
[assembly: AssemblyVersion("3.7.0.0")]
|
||||
|
||||
@@ -71,6 +71,19 @@ namespace ICD.Common.Utils
|
||||
if (node == null)
|
||||
throw new ArgumentNullException("node");
|
||||
|
||||
return GetCliqueIterator(map, visited, node);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the clique containing the node.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="map"></param>
|
||||
/// <param name="visited"></param>
|
||||
/// <param name="node"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> GetCliqueIterator<T>(IDictionary<T, IEnumerable<T>> map, IcdHashSet<T> visited, T node)
|
||||
{
|
||||
if (visited.Contains(node))
|
||||
yield break;
|
||||
|
||||
@@ -87,6 +100,42 @@ namespace ICD.Common.Utils
|
||||
yield return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a path from the given root to the given child node.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="child"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <returns></returns>
|
||||
public static bool BreadthFirstSearch<T>(T root, T child, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
if (getChildren == null)
|
||||
throw new ArgumentNullException("getChildren");
|
||||
|
||||
return BreadthFirstSearchPath(root, child, getChildren) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a path from the given root to the given child node.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <param name="comparer"></param>
|
||||
/// <returns></returns>
|
||||
public static bool BreadthFirstSearch<T>(T root, T destination, Func<T, IEnumerable<T>> getChildren, IEqualityComparer<T> comparer)
|
||||
{
|
||||
if (getChildren == null)
|
||||
throw new ArgumentNullException("getChildren");
|
||||
|
||||
if (comparer == null)
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
return BreadthFirstSearchPath(root, destination, getChildren, comparer) != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all of the nodes in the tree via breadth-first search.
|
||||
/// </summary>
|
||||
@@ -99,6 +148,18 @@ namespace ICD.Common.Utils
|
||||
if (getChildren == null)
|
||||
throw new ArgumentNullException("getChildren");
|
||||
|
||||
return BreadthFirstSearchIterator(root, getChildren);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all of the nodes in the tree via breadth-first search.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="getChildren"></param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<T> BreadthFirstSearchIterator<T>(T root, Func<T, IEnumerable<T>> getChildren)
|
||||
{
|
||||
Queue<T> process = new Queue<T>();
|
||||
process.Enqueue(root);
|
||||
|
||||
@@ -155,7 +216,7 @@ namespace ICD.Common.Utils
|
||||
Queue<T> queue = new Queue<T>();
|
||||
queue.Enqueue(root);
|
||||
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>();
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
@@ -209,27 +270,23 @@ namespace ICD.Common.Utils
|
||||
throw new ArgumentNullException("comparer");
|
||||
|
||||
IcdHashSet<T> destinationsToBeProcessed = new IcdHashSet<T>(destinations);
|
||||
IcdHashSet<T> destinationsProcessed = new IcdHashSet<T>();
|
||||
Dictionary<T, IEnumerable<T>> pathsToReturn = new Dictionary<T, IEnumerable<T>>();
|
||||
|
||||
// Edge case, root is the destination
|
||||
foreach (T destination in
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)))
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(root, destination)).ToArray())
|
||||
{
|
||||
destinationsProcessed.Add(destination);
|
||||
destinationsToBeProcessed.Remove(destination);
|
||||
pathsToReturn.Add(destination, new[] {root});
|
||||
}
|
||||
|
||||
foreach (T destination in destinationsProcessed)
|
||||
destinationsToBeProcessed.Remove(destination);
|
||||
destinationsProcessed.Clear();
|
||||
if (destinationsToBeProcessed.Count == 0)
|
||||
return pathsToReturn;
|
||||
|
||||
Queue<T> queue = new Queue<T>();
|
||||
queue.Enqueue(root);
|
||||
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>();
|
||||
Dictionary<T, T> nodeParents = new Dictionary<T, T>(comparer);
|
||||
|
||||
while (queue.Count > 0)
|
||||
{
|
||||
@@ -242,17 +299,12 @@ namespace ICD.Common.Utils
|
||||
|
||||
T closureNode = node;
|
||||
foreach (T destination in
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)))
|
||||
destinationsToBeProcessed.Where(destination => comparer.Equals(closureNode, destination)).ToArray())
|
||||
{
|
||||
destinationsProcessed.Add(destination);
|
||||
destinationsToBeProcessed.Remove(destination);
|
||||
pathsToReturn.Add(destination, GetPath(destination, root, nodeParents, comparer).Reverse());
|
||||
}
|
||||
|
||||
foreach (T destination in destinationsProcessed)
|
||||
destinationsToBeProcessed.Remove(destination);
|
||||
|
||||
destinationsProcessed.Clear();
|
||||
|
||||
if (destinationsToBeProcessed.Count == 0)
|
||||
return pathsToReturn;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace ICD.Common.Utils
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Releasing a disposed mutex in this case is valid behaviour
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace ICD.Common.Utils.Services.Logging
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the hashcode for this instance.
|
||||
/// Gets the hashcode for this instance.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
|
||||
@@ -165,6 +165,9 @@ namespace ICD.Common.Utils
|
||||
[PublicAPI]
|
||||
public static byte[] ToBytes(string input)
|
||||
{
|
||||
if (input == null)
|
||||
throw new ArgumentNullException("input");
|
||||
|
||||
return Encoding.GetEncoding(28591).GetBytes(input);
|
||||
}
|
||||
|
||||
@@ -277,21 +280,20 @@ namespace ICD.Common.Utils
|
||||
throw new ArgumentNullException("convertFunc");
|
||||
|
||||
result = default(T);
|
||||
bool retVal = false;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
result = convertFunc(value);
|
||||
retVal = true;
|
||||
return true;
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
catch (InvalidCastException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -316,6 +318,9 @@ namespace ICD.Common.Utils
|
||||
/// <returns></returns>
|
||||
public static string NiceName(string name)
|
||||
{
|
||||
if (name == null)
|
||||
throw new ArgumentNullException("name");
|
||||
|
||||
Regex regex = new Regex(@"
|
||||
(?<=[A-Z])(?=[A-Z][a-z]) |
|
||||
(?<=[^A-Z])(?=[A-Z]) |
|
||||
@@ -544,6 +549,7 @@ namespace ICD.Common.Utils
|
||||
{
|
||||
if (string.IsNullOrEmpty(text))
|
||||
return true;
|
||||
|
||||
string trimmed = text.Trim();
|
||||
return trimmed.Length == 0;
|
||||
}
|
||||
@@ -682,5 +688,40 @@ namespace ICD.Common.Utils
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the value starts and ends with a double quote.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string Enquote(string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (!value.StartsWith('"'))
|
||||
value = '"' + value;
|
||||
|
||||
if (!value.EndsWith('"'))
|
||||
value += '"';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the start and trailing double quote from the string if BOTH are present.
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static string UnEnquote(string value)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException("value");
|
||||
|
||||
if (value.StartsWith('"') && value.EndsWith('"'))
|
||||
return value.Substring(1, value.Length - 2);
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,12 @@ namespace ICD.Common.Utils.Timers
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (IsDisposed)
|
||||
return;
|
||||
|
||||
Stop();
|
||||
m_Timer.Dispose();
|
||||
|
||||
IsDisposed = true;
|
||||
}
|
||||
|
||||
@@ -113,7 +117,7 @@ namespace ICD.Common.Utils.Timers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback is called after the dueTime milliseconds.
|
||||
/// Callback is called once after the dueTime milliseconds.
|
||||
/// </summary>
|
||||
/// <param name="dueTime"></param>
|
||||
public void Reset(long dueTime)
|
||||
@@ -121,7 +125,7 @@ namespace ICD.Common.Utils.Timers
|
||||
#if SIMPLSHARP
|
||||
m_Timer.Reset(dueTime);
|
||||
#else
|
||||
m_Timer.Change((int)dueTime, m_RepeatPeriod);
|
||||
m_Timer.Change((int)dueTime, Timeout.Infinite);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
31
ICD.Common.Utils/UriUtils.cs
Normal file
31
ICD.Common.Utils/UriUtils.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace ICD.Common.Utils
|
||||
{
|
||||
public static class UriUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to parse the given URI string into a System.Uri instance.
|
||||
/// </summary>
|
||||
/// <param name="uri"></param>
|
||||
/// <param name="output"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryParse(string uri, out Uri output)
|
||||
{
|
||||
output = null;
|
||||
|
||||
if (string.IsNullOrEmpty(uri))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
output = new Uri(uri);
|
||||
return true;
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,18 +117,11 @@ namespace ICD.Common.Utils.Xml
|
||||
if (m_Reader == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
#if SIMPLSHARP
|
||||
m_Reader.Dispose(true);
|
||||
#else
|
||||
m_Reader.Dispose();
|
||||
#endif
|
||||
}
|
||||
catch (XmlException e)
|
||||
{
|
||||
throw new IcdXmlException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void Skip()
|
||||
|
||||
Reference in New Issue
Block a user