Compare commits

..

105 Commits

Author SHA1 Message Date
Chris Cameron
9e2ec3de63 chore: Updating changelog and incrementing minor version 2018-07-02 09:38:50 -04:00
Rashod Davis
8ed856eb98 Merge branch 'RateLimit' of Common/Utils into dev 2018-06-29 21:28:01 +00:00
Chris Cameron
514c0eaec5 fix: Fixing bug where Timer.Reset() would continue repeating on an interval in Net Standard 2018-06-29 17:11:32 -04:00
Chris Cameron
621d83d8dc feat: Added RateLimitedEventQueue collection for throttling events 2018-06-29 16:37:45 -04:00
Chris Cameron
588e3df86e chore: Adding obfuscation to Net Standard 2018-06-29 10:07:51 -04:00
Drew Tingen
336f39c9c2 Merge branch 'MetLife_v5.3' of Common/Utils into dev 2018-06-25 20:12:38 +00:00
Jack Kanarish
5d3b80938e chore: update changelog 2018-06-25 15:48:35 -04:00
Jack Kanarish
eb2a77b772 fix: for backwards json compatibility, if get type fails, try again after stripping assembly suffixes 2018-06-25 15:42:37 -04:00
Chris Cameron
1c420d111b chore: Updating test framework and sqlite versions 2018-06-21 13:46:36 -04:00
Chris Cameron
e1279fb860 fix: Don't write console to error out 2018-06-21 13:11:35 -04:00
Chris Cameron
bf91d9e87f fix: Potential fix for timer disposal on Net Standard 2018-06-21 11:48:59 -04:00
Chris Cameron
1c89ebc5c2 refactor: Reducing duplication 2018-06-21 11:32:29 -04:00
Chris Cameron
2cd744eb1c fix: Adding missing file to csproj 2018-06-20 16:21:03 -04:00
Rashod Davis
20350c0ab7 Merge branch 'SequenceComparer' of Common/Utils into dev 2018-06-20 19:47:54 +00:00
Chris Cameron
177e59edb6 feat: Initial commit of SequenceComparer 2018-06-20 15:38:22 -04:00
Chris Cameron
d8041bd94c chore: Updating changelog, incrementing minor version 2018-06-19 14:43:39 -04:00
Chris Cameron
f2122596b4 test: Adding unit tests for BiDictionary 2018-06-18 10:06:11 -04:00
Chris Cameron
653bf361ef feat: Splitting iterators from validation 2018-06-14 11:21:46 -04:00
Chris Cameron
3689517124 feat: Adding UnEnquote string util method 2018-06-12 15:11:12 -04:00
Chris Cameron
6fdd5a138e feat: Adding SequenceEqual shim 2018-06-12 14:52:13 -04:00
Chris Cameron
fb267465ce fix: Return false when unable to add console command 2018-06-12 14:50:02 -04:00
Jack Kanarish
f818653298 Merge branch 'Enquote' of Common/Utils into dev 2018-06-12 18:07:48 +00:00
Chris Cameron
f7b5d07c38 feat: Adding enquote util method 2018-06-12 13:31:44 -04:00
Chris Cameron
1a931e6ffb feat: Additional validation 2018-06-12 13:30:45 -04:00
Chris Cameron
2fab5d6fd4 fix: Better set key/value behaviour for BiDictionary 2018-06-11 17:04:26 -04:00
Rashod Davis
8d683f875b Merge branch 'BiDictionary' of Common/Utils into dev 2018-06-11 20:07:49 +00:00
Chris Cameron
74c96aac8a fix: Prevent breaking BiDictionary mappings 2018-06-11 16:00:28 -04:00
Chris Cameron
edc9fa300e feat: Initial commit of BiDictionary 2018-06-11 15:38:28 -04:00
Chris Cameron
37a21131d9 feat: Extension method for populating a dict with a sequence of kvps 2018-06-11 11:48:36 -04:00
Chris Cameron
f674d4c60d refactor: Separating iterators from validation 2018-06-07 18:16:24 -04:00
Chris Cameron
ce163629f3 refactor: Removing redundant code 2018-06-07 16:32:09 -04:00
Chris Cameron
18b07abb44 refactor: Resolving code smells 2018-06-07 14:59:31 -04:00
Chris Cameron
4bc6258b62 refactor: Tidying 2018-06-07 13:12:41 -04:00
Chris Cameron
086aee8167 refactor: More appropriate exceptions 2018-06-07 12:29:07 -04:00
Chris Cameron
cc115bafad refactor: Removing redundant code 2018-06-07 11:26:47 -04:00
Chris Cameron
00db478ef6 fix: Fixing bad validation in EnumerableExtensions 2018-06-07 11:26:37 -04:00
Chris Cameron
aa3fc3bccc refactor: Removing redundant code 2018-06-07 11:23:44 -04:00
Chris Cameron
2602380100 refactor: IcdZip Unzip raises an exception instead of generating a message 2018-06-06 13:45:05 -04:00
Chris Cameron
1f023c14d0 chore: Updating changelog 2018-06-06 11:02:11 -04:00
Chris Cameron
6ce52b74ac feat: Moved FileNameEqualityComparer from Settings 2018-06-06 11:01:16 -04:00
Jeffery Thompson
c500f1db5d Merge branch 'zip' of Common/Utils into dev 2018-06-06 14:57:45 +00:00
Chris Cameron
da58379dfa refactor: Tidying 2018-06-06 10:54:39 -04:00
Chris Cameron
1ae6a55c7d docs: Adding MIT license to ZIP entries method 2018-06-06 10:40:15 -04:00
Chris Cameron
381b19f2e6 fix: Fixing net standard build 2018-06-06 10:39:54 -04:00
Chris Cameron
2f3b1ef57d feat: Adding methods for inspecting contents of a ZIP file 2018-06-06 10:02:39 -04:00
Chris Cameron
da31fae213 feat: Exposing OpenRead method in IcdFile 2018-06-06 10:02:00 -04:00
Chris Cameron
e4b0c9f91a feat: Initial commit of eSeekOrigin enum 2018-06-06 10:01:31 -04:00
Chris Cameron
38dd85d79d feat: Exposing additional stream features in IcdStream 2018-06-06 10:00:46 -04:00
Chris Cameron
c306dd6eb9 feat: Initial commit of IcdBinaryReader 2018-06-06 10:00:24 -04:00
Chris Cameron
6fa3cc03ad feat: Adding ElementAtOrDefault extension method that takes a default value 2018-06-05 13:40:53 -04:00
Chris Cameron
eb58e65574 perf: Don't reinvent the wheel for TryElementAt extension method 2018-06-05 13:40:26 -04:00
Chris Cameron
f24f9458ca chore: Updating changelog, incrementing patch version 2018-06-04 16:33:23 -04:00
Chris Cameron
79bbbe277f Merge remote-tracking branch 'origin/collections' into dev
# Conflicts:
#	ICD.Common.Utils/Collections/PriorityQueue.cs
2018-06-04 16:30:38 -04:00
Jack Kanarish
c03833bf9d fix: dont insert at an index which was removed 2018-05-31 17:28:02 -04:00
Jack Kanarish
f816ae771b chore: merge util changes from dev needed for sharp bug backport to metlife 5.2 2018-05-30 16:50:25 -04:00
Chris Cameron
a178ebe8cb docs: Adding missing comments to IcdHashSet 2018-05-25 16:40:37 -04:00
Chris Cameron
3cddc14880 perf: BreadthFirstSearchPathManyDestinations optimizations 2018-05-25 14:13:22 -04:00
Chris Cameron
088222db47 fix: Potential fix - use breadth first node comparer in parents map 2018-05-25 12:18:02 -04:00
Chris Cameron
9dcda7271d chore: Updating changelog and minor version 2018-05-24 14:28:07 -04:00
Chris Cameron
9dca46add5 feat: Pathfinding methods for determining if a path exists from a root to a destination 2018-05-24 10:59:51 -04:00
Chris Cameron
35593bfa47 feat: Adding GetFlagsExceptNone parameterless enum util method 2018-05-24 10:03:38 -04:00
Chris Cameron
62abd624f8 chore: Updating changelog, incrementing minor version 2018-05-23 10:29:53 -04:00
Chris Cameron
c6a32da597 Merge branch 'setequals' of Common/Utils into dev 2018-05-22 21:43:12 +00:00
Jack Kanarish
aeda403b42 feat: add setequals method to ICDHashSet 2018-05-22 17:41:27 -04:00
Chris Cameron
548431c100 chore: Updating changelog and minor version number 2018-05-18 11:35:31 -04:00
Chris Cameron
d98d222880 refactor: Removing unused using directive 2018-05-16 15:41:45 -04:00
Chris Cameron
02758c6b8e refactor: Whitespace 2018-05-15 15:03:24 -04:00
Chris Cameron
6c3be1884a feat: Adding constructors to IcdUriBuilder 2018-05-15 13:29:38 -04:00
Chris Cameron
8da73593c4 feat: Initial commit of UriUtils 2018-05-15 13:29:24 -04:00
Chris Cameron
f76f4773e5 Merge branch 'feature/interface-attributes' of Common/Utils into dev 2018-05-15 15:43:39 +00:00
Jeffery Thompson
567ee4a1ae fix: only bind to members of the same member type 2018-05-15 11:41:11 -04:00
Jeffery Thompson
72a4b19f97 feat: add ext methods to get interface attributes 2018-05-15 11:04:40 -04:00
Chris Cameron
b2c3ecb113 fix: Fixing bug where IcdUriBuilder was not correctly appending paths 2018-05-14 14:52:55 -04:00
Chris Cameron
e55c2f9474 fix: Fixing dumb mistake with UriExtensions not getting password from URI 2018-05-14 14:52:25 -04:00
Jack Kanarish
d99f0e889c Merge branch 'URI' of Common/Utils into dev 2018-05-11 20:03:30 +00:00
Chris Cameron
cddc726d4d fix: Fixing test cases 2018-05-11 15:57:04 -04:00
Chris Cameron
3f8e2b8df7 feat: Adding IcdUriBuilder and UriExtensions classes 2018-05-11 15:23:36 -04:00
Drew Tingen
1bddca9a06 Merge branch 'Collections' of Common/Utils into dev 2018-05-10 21:08:38 +00:00
Chris Cameron
db8e725fa9 feat: Adding additional enqueue methods to PriorityQueue 2018-05-10 17:02:14 -04:00
Chris Cameron
d9cf0bd3cf refactor: Changing ordered dictionary tests to avoid false positives 2018-05-10 17:01:16 -04:00
Chris Cameron
da8947aaa0 feat: Adding TryDequeue method to PriorityQueue 2018-05-10 13:55:57 -04:00
Chris Cameron
87427096d2 feat: Initial commit of PriorityQueue collection 2018-05-10 13:51:43 -04:00
Chris Cameron
b1c7e527c4 feat: Initial commit of IcdOrderedDictionary 2018-05-10 11:48:33 -04:00
Chris Cameron
718fc349f5 chore: Updating changelog and incrementing minor version 2018-05-09 11:48:47 -04:00
Chris Cameron
1a4159fc31 Merge remote-tracking branch 'origin/enumtoushort' into dev
# Conflicts:
#	CHANGELOG.md
2018-05-09 11:45:38 -04:00
Chris Cameron
110f771cc5 feat: Added util method for removing BOM characters from UTF8 data 2018-05-09 11:35:53 -04:00
Chris Cameron
e446f6e6ce Merge branch 'feature/get-relative-path' of Common/Utils into dev 2018-05-09 14:08:30 +00:00
Jeffery Thompson
7e48547483 feat: add IcdPath.GetRelativePath(string, string) 2018-05-08 17:37:50 -04:00
Jack Kanarish
4adebaf89b feat: added extension to convert from enum to ushort 2018-05-08 16:57:11 -04:00
Chris Cameron
9abf384bb4 docs: Fixing invalid xml 2018-05-07 14:26:11 -04:00
Chris Cameron
d24e17bc26 Merge branch 'EnumerableYield' of Common/Utils into dev 2018-05-04 21:12:57 +00:00
Drew Tingen
07b89edc66 chore: updating changelog and assembly version 2018-05-04 17:10:38 -04:00
Drew Tingen
e974ab655f Merge remote-tracking branch 'origin/dev' into EnumerableYield 2018-05-04 17:08:50 -04:00
Drew Tingen
e43a7200c6 feat: Adding Yield() enumerable extension, that turns an object into a single-item enumerable. 2018-05-04 17:07:29 -04:00
Chris Cameron
b41a236716 refactor: Resolving warning 2018-05-04 15:08:13 -04:00
Chris Cameron
38fa698d46 Merge branch 'boolAndUshortExtensions' of Common/Utils into dev 2018-05-03 16:11:49 +00:00
Jack Kanarish
553a79bd17 csproj changes 2018-05-03 12:11:03 -04:00
Jack Kanarish
d4f5413ce6 proper casing 2018-05-03 12:10:16 -04:00
Jack Kanarish
9b1ddf41d2 add bool and ushort extensions 2018-05-03 11:56:09 -04:00
Jack Kanarish
d22c3c2cd2 Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-05-02 15:38:00 -04:00
Chris Cameron
a5cc9e694a refactor: Tidying 2018-04-26 11:05:49 -04:00
Chris Cameron
08f525eb3e feat: Clearer log message for failing to cache assembly 2018-04-25 14:09:09 -04:00
Jack Kanarish
a52b2d2c08 Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-04-16 15:25:26 -04:00
Jack Kanarish
d33292ae8e Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-04-11 11:20:58 -04:00
Jack Kanarish
dd794729e8 Merge branch 'dev' of https://cs-gogs.icdpf.net/Common/Utils into dev 2018-04-10 11:07:26 -04:00
58 changed files with 3009 additions and 204 deletions

View File

@@ -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

View 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
}
}

View 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
}
}

View 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
}
}

View 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
}
}

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

View 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"));
}
}
}

View 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());
}
}
}

View File

@@ -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>

View 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());
}
}
}

View File

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

View File

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

View 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
}
}

View File

@@ -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))

View 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
}
}

View 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
}
}

View 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
}
}

View File

@@ -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");

View 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();
}
}
}

View 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;
}
}
}
}
}

View 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;
}
}
}

View File

@@ -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>

View 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;
}
}
}

View File

@@ -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");
}

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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&lt;T&gt; 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())

View File

@@ -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);

View File

@@ -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
}
}

View File

@@ -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>

View File

@@ -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)

View File

@@ -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

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

View File

@@ -0,0 +1,10 @@
namespace ICD.Common.Utils.Extensions
{
public static class UShortExtensions
{
public static bool ToBool(this ushort u)
{
return u != 0;
}
}
}

View File

@@ -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>

View File

@@ -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" />

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

View 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; }
}
}

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

View File

@@ -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)
{

View File

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

View File

@@ -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());
}
}
}

View 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");
}
}
}
}

View File

@@ -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
}
}
}

View File

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

View 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();
}
}
}

View File

@@ -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;
}
}
}
}

View File

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

View File

@@ -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 };
}
}

View File

@@ -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

View File

@@ -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")]

View File

@@ -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;
}

View File

@@ -66,6 +66,7 @@ namespace ICD.Common.Utils
}
catch (ObjectDisposedException)
{
// Releasing a disposed mutex in this case is valid behaviour
}
}

View File

@@ -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()

View File

@@ -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;
}
}
}

View File

@@ -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
}

View 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;
}
}
}
}

View File

@@ -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()