Compare commits

...

61 Commits

Author SHA1 Message Date
Drew Tingen
0de417b665 chore: Update Changelog, increment assembly minor version 2023-06-18 09:55:30 -04:00
Drew Tingen
da57fcb988 test[BigEndianBitConverter]: finished unit testing 2023-06-18 09:52:47 -04:00
Drew Tingen
c6b6711eb2 refactor[BigEndianBitConverter]: Reduce code reuse in GetBytes methods 2023-06-18 09:52:18 -04:00
Drew Tingen
138a6f3470 fix[BigEndianBitConverter]: Fix ToLong and ToUlong overflow errors 2023-06-18 09:51:22 -04:00
Scott Pidzarko
e84bc721ee chore: Update SafeMutex.cs to remove commented out boilerplate
IDisposable boilerplate generates commented out finalizer. Removing commented out finalizer at request of upstream maintainer.
2023-06-18 08:38:38 -04:00
Scott Pidzarko
2988912d44 Update SafeMutex.cs to use correct class name in boilerplate commented out finalizer 2023-06-18 08:38:38 -04:00
Scott Pidzarko
be94503e6a Update SafeMutex.cs to implement IDisposable 2023-06-18 08:38:38 -04:00
Drew Tingen
6877b5c800 chore: changelog 2023-06-16 15:01:06 -04:00
Drew Tingen
569066edc4 feat[Types]: Add NotifyFlagsChanged, with abstract and generic classes 2023-06-16 14:59:33 -04:00
Drew Tingen
73a716f9b9 feat[Collections]: ReverseLookupDictionary for fast value access 2023-06-16 14:59:16 -04:00
Drew Tingen
4e430731cc feat[BigEndianBitConverter]: Added BigEndianBitConverter 2023-06-16 14:58:59 -04:00
Drew Tingen
2b8f2a125a feat[EnumUtils]: add GetinverseFlags method 2023-06-16 12:12:29 -04:00
Drew Tingen
cc25c41805 chore: Update changelog, increment assembly minor version 2023-03-22 08:13:59 -04:00
Drew Tingen
2487a70891 chore: Update copyright year 2023-03-22 08:12:27 -04:00
Drew Tingen
5c5a25775b chore: Remove Obfuscation 2023-03-21 16:57:38 -04:00
Drew Tingen
27f6267f54 feat: Nicer timespan readable formatting 2023-02-10 09:41:25 -05:00
Drew Tingen
430eac8cb6 Merge pull request #1 from scottpidzarko/patch-1
PR: Update SPlusUtils.cs to fix typo in XMLdoc
2023-01-10 11:32:40 -05:00
Scott Pidzarko
b47e305d9f Update SPlusUtils.cs
Fix typo.
2023-01-06 15:07:11 -08:00
astingen
0c8f8242e8 Create LICENSE.md 2022-12-24 17:45:39 -05:00
Drew Tingen
a52e77c1e7 chore:Increment major version, update changelog 2022-12-02 14:40:36 -05:00
Drew Tingen
2aa05f3ee8 fix(ThreadedWorkerQueue): Exception handling of exceptions in process action 2022-11-30 12:29:50 -05:00
Drew Tingen
2700f5018a fix: Fixed preprocessors in IcdDirectory 2022-11-30 12:29:50 -05:00
Drew Tingen
b09b22266b fix: Fixed CrestronRuntimeEnvironment uses in utils 2022-11-30 12:29:50 -05:00
Drew Tingen
1c206c5539 doc:Fixed doc typo 2022-11-30 12:29:50 -05:00
Drew Tingen
56a48175c6 feat(IcdEnvironment):Updated environment to sperate Crestron platform for environment for SW on VC4 2022-11-30 12:29:41 -05:00
Drew Tingen
0acd0ae5d4 chore: Update Changelog, Increment assembly patch version 2022-07-11 18:54:12 -04:00
Drew Tingen
d2856c1983 fix: Fix Preprocessors and NetStandard package references 2022-07-11 14:48:10 -04:00
Drew Tingen
fa65a9de65 fix: Fix console command responses in Simpl runtime environment 2022-07-06 15:40:48 -04:00
Drew Tingen
e4b292f145 fix: Crestron apps aren't considered interactive console 2022-07-06 15:37:24 -04:00
Drew Tingen
11a5533fcd chore: Update changelog, increment assembly patch version 2022-07-01 12:02:45 -04:00
Drew Tingen
e0d0763306 chore: Update Crestron SDK to 2.18.96 2022-07-01 11:58:28 -04:00
Drew Tingen
7d42158bc7 chore: sqlite reference only for netstandard 2022-06-30 18:49:21 -04:00
Drew Tingen
92a28813e0 fix: Use Crestron SQLite for 4 series 2022-06-30 17:58:01 -04:00
Drew Tingen
3b313a442c fix: Fix preprocessors for netstandard vs simplsharp 2022-06-30 11:01:12 -04:00
Drew Tingen
16fb3683dd chore: Update changelog, increment assembly patch version 2022-06-23 15:07:50 -04:00
Drew Tingen
bb9bcf6cdf feat: TimeZone getting an invalid time zone now throws exception with time zone name 2022-06-23 15:05:45 -04:00
Drew Tingen
0490ffd572 chore: Update changelog, increment assembly patch version 2022-05-23 11:32:09 -04:00
Chris Cameron
3bf0aff0a0 feat: Added KeyedLock for async semaphore locking by some arbitrary key 2022-02-02 14:13:29 -05:00
Chris Cameron
e5b10a96f7 chore: Moved threading classes into Threading subdirectory 2022-02-02 13:49:03 -05:00
Chris Cameron
0c3c87308c chore: Updating AssemblyInfo year 2022-01-05 16:27:18 -05:00
Chris Cameron
68d1f1033a fix: AbstractGenericJsonConverter handles existing values properly 2021-12-21 16:11:17 -05:00
Chris Cameron
af66147648 Merge remote-tracking branch 'origin/Krang_v1.9' into Krang_v1.10 2021-10-28 11:15:02 -04:00
Chris Cameron
16067bca20 chore: Updating changelog, incrementing patch version 2021-10-28 11:13:30 -04:00
Austin Noska
57e64788c4 fix: Change sqlite connection strings for IcdCultureInfo & IcdTimeZoneInfo to work with SimplSharp 2021-10-27 18:06:29 -04:00
Chris Cameron
bc605bf8ae chore: Updating crestron nuget packages 2021-10-25 14:30:25 -04:00
Chris Cameron
be50aa3314 docs: Fixed typo 2021-10-20 10:11:50 -04:00
Drew Tingen
1b79bd21df Merge remote-tracking branch 'origin/Krang_v1.9' into Krang_v1.10 2021-10-05 10:17:58 -04:00
Drew Tingen
7d5ec0e636 chore: Update changelog, increment assembly major version 2021-10-04 12:21:41 -04:00
Drew Tingen
79c1c60d79 chore: changelog 2021-09-30 10:47:18 -04:00
Drew Tingen
033008616f remove: SafeCriticalSection - remove TryEnter method 2021-09-30 10:45:12 -04:00
Drew Tingen
84b5b636f6 remove: Removing RateLimitedEventQueue - Use ThreadedWorkerQueue instead 2021-09-30 10:45:12 -04:00
Drew Tingen
451cf08c0f fix: Fixing ThreadedWorkerQueue to not use TryEnter, and track the processing state with a bool 2021-09-30 10:45:12 -04:00
Drew Tingen
63d76d8cef feat: Adding IcdAutoResetEvent and IcdMaunalResetEvent 2021-09-30 10:44:30 -04:00
Drew Tingen
c08a6283b8 fix: Fixed EnumUtils.GetValuesExceptNone to work for non-flag enums 2021-09-30 10:44:01 -04:00
Austin Noska
df42a6674f fix: Uri builder builds relative path into valid URI 2021-09-18 16:12:42 -04:00
Chris Cameron
ae53812a98 fix: Fixed plugin loading for .net framework 2021-08-31 11:00:48 -04:00
Chris Cameron
74ff651798 fix: Fixed IcdEnvironment.Framework for .net framework 2021-08-30 17:23:34 -04:00
Chris Cameron
29013a2bf5 fix: Fixed a bug where plugins were not unzipping in .net framework 2021-08-30 17:09:04 -04:00
Chris Cameron
baa00f7b00 chore: S#, net framework and net standard all build to /bin 2021-08-30 14:05:42 -04:00
Chris Cameron
67a2b11ee6 chore: Added SimplSharp nuget packages, fixed SIMPLSHARP preprocessors 2021-08-30 13:39:43 -04:00
Chris Cameron
8f5fee2401 chore: Added net472 target 2021-08-25 16:21:56 -04:00
84 changed files with 3281 additions and 857 deletions

View File

@@ -6,6 +6,68 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [17.2.0] - 2023-06-18
### Added
- Added BigEndianBitConverter
- Added EnumUtils.GetInverseFlags
- Added ReverseLookupDictionary collection
- Added NotifyFlagsChanged type
### Changed
- SafeMutex now implements IDisposable
## [17.1.0] - 2023-03-22
### Changed
- Improved methods for human formatted timespan, including without milliseconds.
- Removed Obfuscation
## [17.0.0] 2022-12-02
### Changed
- IcdEnvironment - Added CrestronDevicePlatform, removed from CrestronRuntimeEnvironment, to support SimplWindows on VC-4
- Fixed IcdEnvironment CrestronRuntimeEnvironment uses in IcdConsole, PathUtils, and ProgramUtils
- Fixed preprocessors in IcdDirectory
- Added exception handling to ThreadedWorkerQueue
## [16.0.5] 2022-07-11
### Changed
- Fixed console command responses in Simpl+ Runtime Environment
- Changed Crestron apps to not report as an interactive console
- Fixed NETSTANDARD vs SIMPLSHARP preprocessors in various files
- Fixed package reference conditions for NetStandard pakcages
## [16.0.4] 2022-07-01
### Changed
- Fixed PreProcessors for NETSTANDARD vs SIMPLSHARP for 4-series builds
- Updated Crestron SDK to 2.18.96
## [16.0.3] 2022-06-23
### Changed
- Throwing better exception when trying to get unknown timezones
## [16.0.2] 2022-05-23
### Changed
- Fixed an issue in IcdUriBuilder where relative pathes were not being built into a valid URI.
## [16.0.1] 2021-10-28
### Changed
- Changed sqlite connection strings in IcdCultureInfo & IcdTimeZoneInfo to work with SimplSharp.
## [16.0.0] 2021-10-04
### Added
- Added IcdAutoResetEvent and IcdManaualResetEvent
### Changed
- EnumUtils - Fixed bug where not all values were returned for GetValueExceptNone
- ThreadedWorkerQueue - Added BetweenTime property, to wait between process callbacks
- ThreadedWorkerQueue - Added RunProcess option to stop queue from processing items
- ThreadedWorkerQueue - Added WaitForFlush events to wait until the queue is empty
- ThreadedWorkerQueue - Added count property
- ThreadedWorkerQueue - Now implements IDisposable
### Removed
- Removed RateLimitedEventQueue and tests - features added to ThreadedWorkerQueue
- Removed TryEnter from SafeCriticalSection - incorrect behavior on Crestron systems
## [15.2.0] - 2021-08-18
### Added
- TryParse overload in StringUtils, attempts to parse a GUID from a string

View File

@@ -0,0 +1,332 @@
using System;
using NUnit.Framework;
// ReSharper disable AssignNullToNotNullAttribute
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public class BigEndianBitConverterTest
{
[TestCase(ushort.MaxValue, new byte[] { 0xFF, 0xFF }, 0)]
[TestCase(ushort.MinValue, new byte[] { 0x00, 0x00 }, 0)]
[TestCase((ushort)255, new byte[] { 0x00, 0xFF }, 0)]
[TestCase((ushort)65280, new byte[] { 0xFF, 0x00 }, 0)]
[TestCase(ushort.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
[TestCase(ushort.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0xFF, 0xFF }, 1)]
[TestCase((ushort)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
[TestCase((ushort)65280, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF }, 4)]
[TestCase((ushort)240, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0xFF }, 2)]
[TestCase((ushort)15, new byte[] { 0x00, 0x0F }, 0)]
public void ToUshortTest(ushort expectedResult, byte[] value, int startIndex)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUshort(value, startIndex));
}
[Test]
public void ToUshortExceptionTest()
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUshort(null, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUshort(values, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUshort(values, 9));
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUshort(values, 8));
}
[TestCase(short.MaxValue, new byte[] { 0x7F, 0xFF }, 0)]
[TestCase(short.MinValue, new byte[] { 0x80, 0x00 }, 0)]
[TestCase(0, new byte[] { 0x00, 0x00 }, 0)]
[TestCase(-1, new byte[] { 0xFF, 0xFF }, 0)]
[TestCase((short)255, new byte[] { 0x00, 0xFF }, 0)]
[TestCase((short)-256, new byte[] { 0xFF, 0x00 }, 0)]
[TestCase(short.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0x00, 0x00 }, 2)]
[TestCase(short.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0xFF, 0xFF }, 1)]
[TestCase((short)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
[TestCase((short)-256, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF }, 4)]
[TestCase((short)240, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0xFF }, 2)]
[TestCase((short)15, new byte[] { 0x00, 0x0F }, 0)]
public void ToShortTest(short expectedResult, byte[] value, int startIndex)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToShort(value, startIndex));
}
[Test]
public void ToShortExceptionTest()
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToShort(null, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToShort(values, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToShort(values, 9));
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToShort(values, 8));
}
[TestCase(uint.MaxValue, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
[TestCase(uint.MinValue, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase((uint)255, new byte[] { 0x00, 0x00, 0x00, 0xFF }, 0)]
[TestCase(4278190080, new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 0)]
[TestCase(uint.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
[TestCase(uint.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
[TestCase((uint)255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
[TestCase(4278190080, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
[TestCase((uint)15728895, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
[TestCase((uint)1044735, new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 0)]
public void ToUintTest(uint expectedResult, byte[] value, int startIndex)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUint(value, startIndex));
}
[Test]
public void ToUintExceptionTest()
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUint(null, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUint(values, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUint(values, 9));
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUint(values, 6));
}
[TestCase(int.MaxValue, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF }, 0)]
[TestCase(int.MinValue, new byte[] { 0x80, 0x00, 0x00, 0x00 }, 0)]
[TestCase(0, new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase(255, new byte[] { 0x00, 0x00, 0x00, 0xFF }, 0)]
[TestCase(-1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
[TestCase(-16777216, new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 0)]
[TestCase(int.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00 }, 2)]
[TestCase(int.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
[TestCase(255, new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
[TestCase(-16777216, new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
[TestCase(15728895, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
[TestCase(1044735, new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 0)]
public void ToIntTest(int expectedResult, byte[] value, int startIndex)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToInt(value, startIndex));
}
[Test]
public void ToIntExceptionTest()
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToInt(null, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToInt(values, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToInt(values, 9));
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToInt(values, 6));
}
[TestCase(ulong.MaxValue, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
[TestCase(ulong.MinValue, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase((ulong)255, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, 0)]
[TestCase(18374686479671623680, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase(ulong.MaxValue, new byte[] { 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 },
2)]
[TestCase(ulong.MinValue, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
[TestCase((ulong)255,
new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
[TestCase(18374686479671623680,
new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
[TestCase((ulong)67555089642946815,
new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF }, 2)]
[TestCase((ulong)4487102673719295, new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0xF0, 0x0F, 0xFF }, 0)]
public void ToUlongTest(ulong expectedResult, byte[] value, int startIndex)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToUlong(value, startIndex));
}
[Test]
public void ToUlongExceptionTest()
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToUlong(null, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUlong(values, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToUlong(values, 9));
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToUlong(values, 2));
}
[TestCase(long.MaxValue, new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
[TestCase(long.MinValue, new byte[] { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase(0, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase(255, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, 0)]
[TestCase(-1, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, 0)]
[TestCase(-72057594037927936, new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase(long.MaxValue, new byte[] { 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00 },
2)]
[TestCase(long.MinValue, new byte[] { 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 1)]
[TestCase(255,
new byte[] { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00 }, 3)]
[TestCase(-72057594037927936,
new byte[] { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }, 4)]
[TestCase(67555093906456320, new byte[] { 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0x0F, 0xFF, 0x00, 0xF0 },
2)]
[TestCase(4487102659035120, new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0x0F, 0xFF, 0xF0 }, 0)]
public void ToLongTest(long expectedResult, byte[] value, int startIndex)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
Assert.AreEqual(expectedResult, BigEndianBitConverter.ToLong(value, startIndex));
}
[Test]
public void ToLongExceptionTest()
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
byte[] values = { 0x00, 0xFF, 0x0F, 0xF0, 0xFF, 0xAA, 0x55, 0xF1, 0x1F };
Assert.Throws<ArgumentNullException>(() => BigEndianBitConverter.ToLong(null, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToLong(values, -1));
Assert.Throws<ArgumentOutOfRangeException>(() => BigEndianBitConverter.ToLong(values, 9));
Assert.Throws<ArgumentException>(() => BigEndianBitConverter.ToLong(values, 2));
}
[TestCase(new byte[] { 0x7F, 0xFF, 0xFF, 0xFF }, int.MaxValue)]
[TestCase(new byte[] { 0x80, 0x00, 0x00, 0x00 }, int.MinValue)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00 }, 0)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, 255)]
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, -1)]
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00 }, -16777216)]
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF }, 15728895)]
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, 1044735)]
public void GetBytesIntTest(byte[] expectedResult, int value)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
}
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }, uint.MaxValue)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00 }, uint.MinValue)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, (uint)255)]
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00 }, 4278190080)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0xFF }, (uint)255)]
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF }, (uint)15728895)]
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF }, (uint)1044735)]
public void GetBytesUintTest(byte[] expectedResult, uint value)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
}
[TestCase(new byte[] { 0x7F, 0xFF }, short.MaxValue)]
[TestCase(new byte[] { 0x80, 0x00 }, short.MinValue)]
[TestCase(new byte[] { 0x00, 0x00 }, (short)0)]
[TestCase(new byte[] { 0xFF, 0xFF }, (short)-1)]
[TestCase(new byte[] { 0x00, 0xFF }, (short)255)]
[TestCase(new byte[] { 0xFF, 0x00 }, (short)-256)]
[TestCase(new byte[] { 0x00, 0xF0 }, (short)240)]
[TestCase(new byte[] { 0x00, 0x0F }, (short)15)]
public void GetBytesShortTest(byte[] expectedResult, short value)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
}
[TestCase(new byte[] { 0xFF, 0xFF }, ushort.MaxValue)]
[TestCase(new byte[] { 0x00, 0x00 }, ushort.MinValue)]
[TestCase(new byte[] { 0x00, 0xFF }, (ushort)255)]
[TestCase(new byte[] { 0xFF, 0x00 }, (ushort)65280)]
[TestCase(new byte[] { 0x00, 0xF0 }, (ushort)240)]
[TestCase(new byte[] { 0x00, 0x0F }, (ushort)15)]
public void GetBytesUshortTest(byte[] expectedResult, ushort value)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
}
[TestCase(new byte[] { 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, long.MaxValue)]
[TestCase(new byte[] { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },long.MinValue)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, (long)0)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, (long)255)]
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, (long)-1)]
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, -72057594037927936)]
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF, 0xFF, 0x0F, 0xFF, 0x00 }, 67555093906456320)]
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0x0F, 0xFF, 0xF0 }, 4487102659035120)]
public void GetBytesLongTest(byte[] expectedResult, long value)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
}
[TestCase(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, ulong.MaxValue)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, ulong.MinValue)]
[TestCase(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF }, (ulong)255)]
[TestCase(new byte[] { 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 18374686479671623680)]
[TestCase(new byte[] { 0x00, 0xF0, 0x00, 0xFF, 0x00, 0xF0, 0x00, 0xFF }, (ulong)67555089642946815)]
[TestCase(new byte[] { 0x00, 0x0F, 0xF0, 0xFF, 0x00, 0xF0, 0x0F, 0xFF }, (ulong)4487102673719295)]
public void GetBytesUlongTest(byte[] expectedResult, ulong value)
{
// We use system BitConverter for systems that are already big-endian
if (!BitConverter.IsLittleEndian)
Assert.Inconclusive();
CollectionAssert.AreEqual(expectedResult, BigEndianBitConverter.GetBytes(value));
}
}
}

View File

@@ -1,100 +0,0 @@
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,300 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.Extensions;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public class ReverseLookupDictionaryTest
{
//todo: Finish!
#region Properties
[Test]
public void CountTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Set(1, "10");
dict.Set(2, "10");
dict.Set(3, "30");
Assert.AreEqual(2, dict.Count);
}
[Test]
public void KeysTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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));
Assert.IsFalse(dict.Keys.Contains(4));
}
[Test]
public void ValuesTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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"));
Assert.IsFalse(dict.Values.Contains("40"));
}
#endregion
#region Methods
[Test]
public void ClearTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
Assert.IsFalse(dict.ContainsKey(1));
dict.Set(1, "10");
Assert.IsTrue(dict.ContainsKey(1));
}
[Test]
public void ContainsValueTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
Assert.IsFalse(dict.ContainsValue("10"));
dict.Set(1, "10");
Assert.IsTrue(dict.ContainsValue("10"));
}
[Test]
public void AddTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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.DoesNotThrow(() => dict.Add(2, "10"));
Assert.DoesNotThrow(() => dict.Add(3, "20"));
Assert.AreEqual(3, dict.Count);
}
[Test]
public void SetTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "20");
dict.Set(3, "30");
dict.Set(4, "20");
Assert.AreEqual("20", dict.GetValue(1));
Assert.AreEqual("30", dict.GetValue(3));
Assert.AreEqual("40", dict.GetValue(4));
CollectionAssert.AreEquivalent(new[]{1,4}, dict.GetKeys("20"));
CollectionAssert.AreEquivalent(new[]{3}, dict.GetKeys("30"));
}
[Test]
public void GetKeysTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "Odd");
dict.Add(2, "Even");
dict.Set(3, "Odd");
dict.Add(4, "Value");
dict.Set(4, "Even");
dict.Add(5, "Odd");
dict.Add(6, "Even");
CollectionAssert.AreEquivalent(new[]{1,3,5}, dict.GetKeys("Odd"));
CollectionAssert.AreEquivalent(new[]{2,4,6}, dict.GetKeys("Even"));
}
[Test]
public void GetValueTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Set(1, "10");
dict.Set(1, "20");
dict.Set(3, "30");
dict.Set(2, "20");
Assert.AreEqual("20", dict.GetValue(1));
Assert.AreEqual("30", dict.GetValue(3));
Assert.AreEqual("20", dict.GetValue(2));
}
[Test]
public void RemoveKeyTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Add(1, "10");
Assert.AreEqual(1, dict.Count);
dict.RemoveKey(1);
Assert.AreEqual(0, dict.Count);
}
[Test]
public void RemoveValueSingleTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Add(1, "10");
Assert.AreEqual(1, dict.Count);
dict.RemoveValue("10");
Assert.AreEqual(0, dict.Count);
}
[Test]
public void RemoveValueMultipleTest1()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Add(1, "10");
dict.Add(2, "10");
dict.Add(3, "10");
Assert.AreEqual(3, dict.Count);
dict.RemoveValue("10");
Assert.AreEqual(0, dict.Count);
}
[Test]
public void RemoveValueWithOthersTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
dict.Add(1, "10");
dict.Add(2, "10");
dict.Add(3, "10");
dict.Add(4, "20");
dict.Add(5, "20");
dict.Add(6, "some other string");
Assert.AreEqual(6, dict.Count);
dict.RemoveValue("10");
Assert.AreEqual(3, dict.Count);
}
[Test]
public void TryGetValueTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<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 TryGetKeysMultipleTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
IEnumerable<int> value;
Assert.IsFalse(dict.TryGetKeys("10", out value));
dict.Add(1, "10");
dict.Add(11, "100");
Assert.IsTrue(dict.TryGetKeys("10", out value));
CollectionAssert.AreEquivalent(new[]{1}, value);
Assert.IsTrue(dict.TryGetKeys("100", out value));
CollectionAssert.AreEquivalent(new[]{11}, value);
dict.Add(2, "10");
dict.Add(3, "10");
dict.Add(12, "100");
dict.Add(13, "100");
Assert.IsTrue(dict.TryGetKeys("10", out value));
CollectionAssert.AreEquivalent(new[]{1,2,3}, value);
Assert.IsTrue(dict.TryGetKeys("100", out value));
CollectionAssert.AreEquivalent(new[]{11,12,13}, value);
Assert.IsFalse(dict.TryGetKeys("string", out value));
}
[Test]
public void TryGetKeysSingleTest()
{
ReverseLookupDictionary<int, string> dict = new ReverseLookupDictionary<int, string>();
IEnumerable<int> value;
Assert.IsFalse(dict.TryGetKeys("10", out value));
dict.Add(1, "10");
Assert.IsTrue(dict.TryGetKeys("10", out value));
CollectionAssert.AreEquivalent(new[]{1}, value);
}
#endregion
}
}

View File

@@ -22,7 +22,8 @@ namespace ICD.Common.Utils.Tests
A = 1,
B = 2,
C = 4,
D = 32
D = 32,
BandC = B | C
}
[Test]
@@ -185,6 +186,16 @@ namespace ICD.Common.Utils.Tests
value);
}
[TestCase(eTestFlagsEnum.A, eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D)]
[TestCase(eTestFlagsEnum.BandC, eTestFlagsEnum.A | eTestFlagsEnum.D)]
[TestCase(eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, eTestFlagsEnum.None)]
[TestCase(eTestFlagsEnum.None, eTestFlagsEnum.A | eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D)]
[TestCase(eTestFlagsEnum.D, eTestFlagsEnum.A | eTestFlagsEnum.BandC)]
public void GetInverseFlagsTest(eTestFlagsEnum expected, eTestFlagsEnum value)
{
Assert.AreEqual(expected, EnumUtils.GetInverseFlags(value));
}
[Test]
public void HasFlagGenericTest()
{

View File

@@ -1,10 +1,15 @@
using System.Collections.Generic;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO;
using Newtonsoft.Json;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Extensions

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
<RootNamespace>ICD.Common.Utils.Tests</RootNamespace>
<AssemblyName>ICD.Common.Utils.Tests</AssemblyName>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -11,21 +11,22 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;STANDARD</DefineConstants>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
<DefineConstants>TRACE;STANDARD</DefineConstants>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="binNetCoreApp\**" />
<EmbeddedResource Remove="binNetCoreApp\**" />
<None Remove="binNetCoreApp\**" />
<Compile Remove="bin\**" />
<EmbeddedResource Remove="bin\**" />
<None Remove="bin\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />

View File

@@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
class IcdAutoResetEventTest
{
[TestCase(true)]
[TestCase(false)]
public void InitialStateTest(bool initialState)
{
using (var waitHandleEvent = new IcdAutoResetEvent(initialState))
{
Assert.AreEqual(initialState, waitHandleEvent.WaitOne(1), "Initial state incorrect");
}
}
[Test]
public void DefaultInitialStateTest()
{
using (var waitHandleEvent = new IcdAutoResetEvent())
{
Assert.False(waitHandleEvent.WaitOne(1), "Initial state incorrect");
}
}
[TestCase(1)]
[TestCase(5)]
[TestCase(15)]
public void MultipleThreadSetTest(int count)
{
int releasedCount = 0;
SafeCriticalSection countSection = new SafeCriticalSection();
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
for (int i = 0; i < count; i++)
ThreadingUtils.SafeInvoke(() =>
{
waitHandleEvent.WaitOne();
countSection.Execute(() => releasedCount++);
;
});
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released early");
///Auto reset should only release one thread at a time
for (int i = 1; i <= count; i++)
{
waitHandleEvent.Set();
ThreadingUtils.Sleep(100);
Assert.AreEqual(i, countSection.Execute(() => releasedCount),
"Incorrect number of threads released");
}
waitHandleEvent.Dispose();
}
[TestCase(1)]
[TestCase(5)]
[TestCase(15)]
public void MultipleThreadResetTest(int count)
{
int releasedCount = 0;
SafeCriticalSection countSection = new SafeCriticalSection();
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(true);
Assert.True(waitHandleEvent.WaitOne(1), "Initial State Wrong");
waitHandleEvent.Reset();
for (int i = 0; i < count; i++)
ThreadingUtils.SafeInvoke(() =>
{
if (waitHandleEvent.WaitOne(100))
countSection.Execute(() => releasedCount++);
});
ThreadingUtils.Sleep(2000);
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
waitHandleEvent.Set();
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released after timeout");
waitHandleEvent.Dispose();
}
[TestCase(1)]
[TestCase(200)]
[TestCase(5000)]
public void WaitOneTest(int waitTime)
{
bool released = false;
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
ThreadingUtils.SafeInvoke(() =>
{
waitHandleEvent.WaitOne();
released = true;
});
ThreadingUtils.Sleep(waitTime);
Assert.False(released, "Thread released when it shouldn't have");
waitHandleEvent.Set();
ThreadingUtils.Sleep(100);
Assert.True(released, "Thread didn't release after set event");
waitHandleEvent.Dispose();
}
[TestCase(200)]
[TestCase(500)]
[TestCase(5000)]
public void WaitOneTimeoutTest(int waitTime)
{
bool released = false;
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
ThreadingUtils.SafeInvoke(() =>
{
waitHandleEvent.WaitOne(waitTime * 2);
released = true;
});
ThreadingUtils.Sleep(waitTime);
Assert.False(released, "Thread released when it shouldn't have");
waitHandleEvent.Set();
ThreadingUtils.Sleep(100);
Assert.True(released, "Thread didn't release after set event");
waitHandleEvent.Dispose();
}
[TestCase(200)]
[TestCase(500)]
[TestCase(5000)]
public void WaitOneTimedOutTest(int waitTime)
{
bool released = false;
bool? returned = null;
IcdAutoResetEvent waitHandleEvent = new IcdAutoResetEvent(false);
ThreadingUtils.SafeInvoke(() =>
{
returned = waitHandleEvent.WaitOne(waitTime);
released = true;
});
ThreadingUtils.Sleep(100);
Assert.True(returned == null, "WaitOne returned when it shouldn't have");
Assert.False(released, "Thread released when it shouldn't have");
ThreadingUtils.Sleep(waitTime * 2);
Assert.True(released, "Thread didn't release after timeout");
Assert.True(returned.HasValue && returned.Value == false, "WaitOne timeout didn't return false");
waitHandleEvent.Set();
waitHandleEvent.Dispose();
}
}
}

View File

@@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
class IcdManualResetEventTest
{
[TestCase(true)]
[TestCase(false)]
public void InitialStateTest(bool initialState)
{
using (var waitHandleEvent = new IcdManualResetEvent(initialState))
{
Assert.AreEqual(initialState, waitHandleEvent.WaitOne(1), "Initial state incorrect");
}
}
[Test]
public void DefaultInitialStateTest()
{
using (var waitHandleEvent = new IcdManualResetEvent())
{
Assert.False(waitHandleEvent.WaitOne(1), "Initial state incorrect");
}
}
[TestCase(1)]
[TestCase(5)]
[TestCase(15)]
public void MultipleThreadSetTest(int count)
{
int releasedCount = 0;
SafeCriticalSection countSection = new SafeCriticalSection();
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
for (int i = 0; i < count; i++)
ThreadingUtils.SafeInvoke(() =>
{
waitHandleEvent.WaitOne();
countSection.Execute(() => releasedCount++);
;
});
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released early");
waitHandleEvent.Set();
ThreadingUtils.Sleep(500);
Assert.AreEqual(count, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
waitHandleEvent.Dispose();
}
[TestCase(1)]
[TestCase(5)]
[TestCase(15)]
public void MultipleThreadResetTest(int count)
{
int releasedCount = 0;
SafeCriticalSection countSection = new SafeCriticalSection();
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(true);
Assert.True(waitHandleEvent.WaitOne(1), "Initial State Wrong");
waitHandleEvent.Reset();
for (int i = 0; i < count; i++)
ThreadingUtils.SafeInvoke(() =>
{
if (waitHandleEvent.WaitOne(100))
countSection.Execute(() => releasedCount++);
});
ThreadingUtils.Sleep(2000);
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Incorrect number of threads released");
waitHandleEvent.Set();
Assert.AreEqual(0, countSection.Execute(() => releasedCount), "Threads released after timeout");
waitHandleEvent.Dispose();
}
[TestCase(1)]
[TestCase(200)]
[TestCase(5000)]
public void WaitOneTest(int waitTime)
{
bool released = false;
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
ThreadingUtils.SafeInvoke(() =>
{
waitHandleEvent.WaitOne();
released = true;
});
ThreadingUtils.Sleep(waitTime);
Assert.False(released, "Thread released when it shouldn't have");
waitHandleEvent.Set();
ThreadingUtils.Sleep(100);
Assert.True(released, "Thread didn't release after set event");
waitHandleEvent.Dispose();
}
[TestCase(200)]
[TestCase(500)]
[TestCase(5000)]
public void WaitOneTimeoutTest(int waitTime)
{
bool released = false;
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
ThreadingUtils.SafeInvoke(() =>
{
waitHandleEvent.WaitOne(waitTime * 2);
released = true;
});
ThreadingUtils.Sleep(waitTime);
Assert.False(released, "Thread released when it shouldn't have");
waitHandleEvent.Set();
ThreadingUtils.Sleep(100);
Assert.True(released, "Thread didn't release after set event");
waitHandleEvent.Dispose();
}
[TestCase(200)]
[TestCase(500)]
[TestCase(5000)]
public void WaitOneTimedOutTest(int waitTime)
{
bool released = false;
bool? returned = null;
IcdManualResetEvent waitHandleEvent = new IcdManualResetEvent(false);
ThreadingUtils.SafeInvoke(() =>
{
returned = waitHandleEvent.WaitOne(waitTime);
released = true;
});
ThreadingUtils.Sleep(100);
Assert.True(returned == null, "WaitOne returned when it shouldn't have");
Assert.False(released, "Thread released when it shouldn't have");
ThreadingUtils.Sleep(waitTime * 2);
Assert.True(released, "Thread didn't release after timeout");
Assert.True(returned.HasValue && returned.Value == false, "WaitOne timeout didn't return false");
waitHandleEvent.Set();
waitHandleEvent.Dispose();
}
}
}

View File

@@ -1,6 +1,11 @@
using System;
using ICD.Common.Utils.Json;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using ICD.Common.Utils.Json;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Json

View File

@@ -1,7 +1,12 @@
using ICD.Common.Utils.Extensions;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO;
using ICD.Common.Utils.Json;
using Newtonsoft.Json;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Json

View File

@@ -37,30 +37,5 @@ namespace ICD.Common.Utils.Tests
Assert.Inconclusive();
}
[Test]
public void TryEnterTest()
{
int result = 0;
SafeCriticalSection section = new SafeCriticalSection();
section.Enter();
// ReSharper disable once NotAccessedVariable
ThreadingUtils.SafeInvoke(() => { result = section.TryEnter() ? 0 : 1; });
Assert.IsTrue(ThreadingUtils.Wait(() => result == 1, 1000));
section.Leave();
// ReSharper disable once RedundantAssignment
ThreadingUtils.SafeInvoke(() =>
{
result = section.TryEnter() ? 2 : 0;
section.Leave();
});
Assert.IsTrue(ThreadingUtils.Wait(() => result == 2, 1000));
}
}
}
}

View File

@@ -0,0 +1,205 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
{
[TestFixture]
public sealed class ThreadedWorkerQueueTest
{
[Test]
public void BetweenTimeTest()
{
List<int> callbacks = new List<int>();
using (ThreadedWorkerQueue<int> queue = new ThreadedWorkerQueue<int>((d) => callbacks.Add(d), true, 1000))
{
queue.Enqueue(10);
queue.Enqueue(20);
queue.Enqueue(30);
ThreadingUtils.Sleep(100);
Assert.AreEqual(1, callbacks.Count, "Initial enqueue did not trigger a dequeue");
ThreadingUtils.Sleep(1000);
Assert.AreEqual(2, callbacks.Count, "Second enqueue did not dequeue");
ThreadingUtils.Sleep(100);
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)]
[TestCase(0)]
[TestCase(long.MaxValue)]
public void BetweenMillisecondsTest(long milliseconds)
{
using (var queue = new ThreadedWorkerQueue<int>((d) => { }, true, milliseconds))
Assert.AreEqual(milliseconds, queue.BetweenTime);
}
[TestCase(5)]
[TestCase(0)]
[TestCase(30)]
public void CountTest(int count)
{
using (var queue = new ThreadedWorkerQueue<int>(d => { }, false))
{
for (int i = 0; i < count; i++)
queue.Enqueue(1);
Assert.AreEqual(count, queue.Count);
}
}
[Test]
public void ProcessBetweenTimeTest()
{
var processed = new List<int>();
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false, 1000))
{
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
ThreadingUtils.Sleep(100);
Assert.AreEqual(0, processed.Count, "Queue processed item early");
queue.SetRunProcess(true);
ThreadingUtils.Sleep(100);
Assert.AreEqual(1, processed.Count, "First item not processed");
ThreadingUtils.Sleep(1000);
Assert.AreEqual(2, processed.Count, "Second item not processed");
queue.SetRunProcess(false);
ThreadingUtils.Sleep(2000);
Assert.AreEqual(2, processed.Count, "Item processed after stopping run process");
Assert.AreEqual(1, queue.Count, "Incorrect number of items in queue");
// Queue lower priority item
queue.Enqueue(5, 1);
queue.SetRunProcess(true);
ThreadingUtils.Sleep(100);
Assert.AreEqual(3, processed.Count, "Third item not processed");
Assert.AreEqual(5, processed[2], "Dequeued incorrect priority item");
ThreadingUtils.Sleep(1000);
Assert.AreEqual(4, processed.Count, "Didn't process all items");
Assert.True(processed.SequenceEqual(new[] { 1, 2, 5, 3 }), "Processed sequence incorrect");
}
}
[Test]
public void FlushQueueBetweenTimeTest()
{
var processed = new List<int>();
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false, 1000))
{
Assert.True(queue.WaitForFlush(1), "WaitForFlush on empty queue failed");
queue.Enqueue(11);
queue.Enqueue(21);
queue.Enqueue(31);
queue.Enqueue(41);
queue.Enqueue(51);
queue.Enqueue(61);
queue.Enqueue(71);
queue.Enqueue(81);
queue.Enqueue(91);
queue.Enqueue(101);
queue.SetRunProcess(true);
Assert.False(queue.WaitForFlush(1250), "WaitForFlush didn't time out");
Assert.AreEqual(2, processed.Count, "Didn't process correct number of items in time frame");
Assert.True(queue.WaitForFlush(), "WaitForFlush failed");
Assert.AreEqual(10, processed.Count, "Not all items processed");
Assert.AreEqual(0, queue.Count, "Queue not empty");
}
}
#endregion
#region Methods
[TestCase(false)]
[TestCase(true)]
public void SetRunProcessTest(bool runProcess)
{
using (var queue = new ThreadedWorkerQueue<int>(d => { }, !runProcess))
{
Assert.AreEqual(!runProcess, queue.RunProcess, "Initial state wrong");
queue.SetRunProcess(runProcess);
Assert.AreEqual(runProcess, queue.RunProcess, "Didn't set to correct state 1st time");
queue.SetRunProcess(!runProcess);
Assert.AreEqual(!runProcess, queue.RunProcess, "Didn't set to correct state 2nd time");
}
}
[Test]
public void EnqueueTest()
{
var processed = new List<int>();
using (var queue = new ThreadedWorkerQueue<int>(d => processed.Add(d), false))
{
queue.Enqueue(10);
queue.Enqueue(20);
queue.Enqueue(30);
Assert.AreEqual(3, queue.Count, "First queue count wrong");
queue.Enqueue(40);
queue.Enqueue(50);
queue.Enqueue(60);
Assert.AreEqual(6, queue.Count, "Second queue count wrong");
queue.SetRunProcess(true);
Assert.True(queue.WaitForFlush(),"Queue didn't flush after processing");
Assert.True(processed.SequenceEqual(new[] { 10, 20, 30, 40, 50, 60 }), "Processed sequence wrong");
}
}
[Test]
public void ClearTest()
{
using (var queue = new ThreadedWorkerQueue<int>(d => { }, false))
{
queue.Enqueue(1);
queue.Enqueue(1);
queue.Enqueue(1);
queue.Clear();
Assert.AreEqual(0, queue.Count);
}
}
#endregion
}
}

View File

@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Types;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Types
{
[TestFixture]
public class GenericNotifyFlagsChangedTest
{
[Flags]
private enum eTestFlagsEnum
{
None = 0,
A = 1,
B = 2,
C = 4,
D = 32,
BandC = B | C
}
[Test]
public void TestSingleFlags()
{
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
// Initial State
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add No flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add Flag
genericNotify.Data = eTestFlagsEnum.B;
Assert.AreEqual(1, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B, addedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.B, genericNotify.Data);
// Add Another Flag
genericNotify.Data |= eTestFlagsEnum.C;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, genericNotify.Data);
// Remove a Flag
genericNotify.Data = genericNotify.Data & ~ eTestFlagsEnum.B;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B, removedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.C, genericNotify.Data);
// Add Already Existing Flags
genericNotify.Data |= eTestFlagsEnum.C;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
// Clear Flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(2, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.C, removedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
}
[Test]
public void TestMultipleFlags()
{
List<GenericEventArgs<eTestFlagsEnum>> addedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
List<GenericEventArgs<eTestFlagsEnum>> removedFlags = new List<GenericEventArgs<eTestFlagsEnum>>();
GenericNotifyFlagsChanged<eTestFlagsEnum> genericNotify = new GenericNotifyFlagsChanged<eTestFlagsEnum>();
genericNotify.OnFlagsSet += (sender, args) => addedFlags.Add(args);
genericNotify.OnFlagsUnset += (sender, args) => removedFlags.Add(args);
// Initial State
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add No flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(0, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
// Add Flag
genericNotify.Data = eTestFlagsEnum.B | eTestFlagsEnum.D;
Assert.AreEqual(1, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, addedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.D, genericNotify.Data);
// Add Another Flag
genericNotify.Data |= eTestFlagsEnum.C;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(0, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.C, addedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C | eTestFlagsEnum.D, genericNotify.Data);
// Remove a Flag
genericNotify.Data = eTestFlagsEnum.D;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.B | eTestFlagsEnum.C, removedFlags[0].Data);
Assert.AreEqual(eTestFlagsEnum.D, genericNotify.Data);
// Add Already Existing Flags
genericNotify.Data |= eTestFlagsEnum.D;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(1, removedFlags.Count);
// Clear Flags
genericNotify.Data = eTestFlagsEnum.None;
Assert.AreEqual(2, addedFlags.Count);
Assert.AreEqual(2, removedFlags.Count);
Assert.AreEqual(eTestFlagsEnum.D, removedFlags[1].Data);
Assert.AreEqual(eTestFlagsEnum.None, genericNotify.Data);
}
}
}

View File

@@ -70,7 +70,7 @@ namespace ICD.Common.Utils
/// </summary>
public static IDictionary<string, string> PuttyColors { get { return s_PuttyColors; } }
#if !SIMPLSHARP
#if NETSTANDARD
/// <summary>
/// Enables ANSI color in the console.
/// </summary>

View File

@@ -0,0 +1,153 @@
using System;
using ICD.Common.Properties;
namespace ICD.Common.Utils
{
public static class BigEndianBitConverter
{
private const byte FULL_BYTE = 0xFF;
public static ushort ToUshort([NotNull] byte[] value, int startIndex)
{
return unchecked((ushort)ToShort(value, startIndex));
}
public static short ToShort([NotNull] byte[] value, int startIndex)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.ToInt16(value, startIndex);
const int bytes = sizeof(short);
if (value == null)
throw new ArgumentNullException("value");
if (startIndex < 0 || startIndex >= value.Length)
throw new ArgumentOutOfRangeException("startIndex");
if (startIndex > value.Length - bytes)
throw new ArgumentException("Array plus start index too small");
short result = 0;
for (int i = 0; i < bytes; i++)
result |= (short)(value[i + startIndex] << GetBitShift(i, bytes));
return result;
}
public static uint ToUint([NotNull] byte[] value, int startIndex)
{
return unchecked((uint)ToInt(value, startIndex));
}
public static int ToInt([NotNull] byte[] value, int startIndex)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.ToInt32(value, startIndex);
const int bytes = sizeof(int);
if (value == null)
throw new ArgumentNullException("value");
if (startIndex < 0 || startIndex >= value.Length)
throw new ArgumentOutOfRangeException("startIndex");
if (startIndex > value.Length - bytes)
throw new ArgumentException("Array plus start index too small");
int result = 0;
for (int i = 0; i < bytes; i++)
result |= value[i + startIndex] << GetBitShift(i, bytes);
return result;
}
public static ulong ToUlong([NotNull] byte[] value, int startIndex)
{
return unchecked((ulong)ToLong(value, startIndex));
}
public static long ToLong([NotNull] byte[] value, int startIndex)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.ToInt64(value, startIndex);
const int bytes = sizeof(long);
if (value == null)
throw new ArgumentNullException("value");
if (startIndex < 0 || startIndex >= value.Length)
throw new ArgumentOutOfRangeException("startIndex");
if (startIndex > value.Length - bytes)
throw new ArgumentException("Array plus start index too small");
long result = 0;
for (int i = 0; i < bytes; i++)
result |= (long)(value[i + startIndex]) << GetBitShift(i, bytes);
return result;
}
public static byte[] GetBytes(int value)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.GetBytes(value);
const int total_bytes = sizeof(int);
byte[] response = new byte[total_bytes];
for (int i = 0; i < total_bytes; i++)
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
return response;
}
public static byte[] GetBytes(uint value)
{
return unchecked(GetBytes((int)value));
}
public static byte[] GetBytes(short value)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.GetBytes(value);
const int total_bytes = sizeof(short);
byte[] response = new byte[total_bytes];
for (int i = 0; i < total_bytes; i++)
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
return response;
}
public static byte[] GetBytes(ushort value)
{
return unchecked(GetBytes((short)value));
}
public static byte[] GetBytes(long value)
{
if (!BitConverter.IsLittleEndian)
return BitConverter.GetBytes(value);
const int total_bytes = sizeof(long);
byte[] response = new byte[total_bytes];
for (int i = 0; i < total_bytes; i++)
response[i] = (byte)(value >> GetBitShift(i,total_bytes) & FULL_BYTE);
return response;
}
public static byte[] GetBytes(ulong value)
{
return unchecked(GetBytes((long)value));
}
private static int GetBitShift(int byteNumber, int totalBytes)
{
return ((totalBytes - 1 - byteNumber) * 8);
}
}
}

View File

@@ -1,204 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.Timers;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
/// <summary>
/// RateLimitedEventQueue provides features for enqueing items to be raised via an event at a controlled interval.
/// </summary>
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
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; } }
[NotNull]
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_QueueSection.Enter();
try
{
m_DequeueTimer.Dispose();
}
finally
{
m_QueueSection.Leave();
}
}
/// <summary>
/// Enqueues the given item.
/// </summary>
/// <param name="item"></param>
public void Enqueue([CanBeNull] 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
[NotNull]
public IEnumerator<T> GetEnumerator()
{
return m_QueueSection.Execute(() => m_Queue.ToList(m_Queue.Count).GetEnumerator());
}
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection.CopyTo([NotNull] Array array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
m_QueueSection.Enter();
try
{
foreach (T item in this)
{
array.SetValue(item, index);
index++;
}
}
finally
{
m_QueueSection.Leave();
}
}
#endregion
}
}

View File

@@ -0,0 +1,299 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
#if !SIMPLSHARP
using System.Diagnostics;
#endif
namespace ICD.Common.Utils.Collections
{
#if !SIMPLSHARP
[DebuggerDisplay("Count = {Count}")]
#endif
public class ReverseLookupDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> m_KeyToValue;
private readonly Dictionary<TValue, IcdHashSet<TKey>> m_ValueToKeys;
#region Properties
public int Count { get { return m_KeyToValue.Count; } }
public bool IsReadOnly { get { return false; } }
[NotNull]
public ICollection<TKey> Keys { get { return m_KeyToValue.Keys; } }
[NotNull]
public ICollection<TValue> Values { get { return m_ValueToKeys.Keys; } }
#endregion
/// <summary>
/// Constructor.
/// </summary>
public ReverseLookupDictionary() :
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public ReverseLookupDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
{
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
m_ValueToKeys = new Dictionary<TValue, IcdHashSet<TKey>>(valueComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict, IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
: this(keyComparer, valueComparer)
{
if (dict == null)
throw new ArgumentNullException("dict");
foreach (KeyValuePair<TKey, TValue> kvp in dict)
Add(kvp.Key, kvp.Value);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
public ReverseLookupDictionary([NotNull] Dictionary<TKey, TValue> dict)
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
}
#region Methods
public void Clear()
{
m_KeyToValue.Clear();
m_ValueToKeys.Clear();
}
public bool ContainsKey([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_KeyToValue.ContainsKey(key);
}
public bool ContainsValue([NotNull] TValue value)
{
return m_ValueToKeys.ContainsKey(value);
}
public IEnumerable<KeyValuePair<TValue, IEnumerable<TKey>>> GetReverseDictionary()
{
// Cast stuff a layer deep cause weird
return m_ValueToKeys.Select(kvp => new KeyValuePair<TValue, IEnumerable<TKey>>(kvp.Key, kvp.Value.ToArray(kvp.Value.Count)));
}
public void Add([NotNull] TKey key, [NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
if (ContainsKey(key))
throw new ArgumentException("Key is already present in the collection", "key");
m_KeyToValue.Add(key, value);
m_ValueToKeys.GetOrAddNew(value, () => new IcdHashSet<TKey>()).Add(key);
}
public void Set([NotNull] TKey key, [NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
RemoveKey(key);
Add(key, value);
}
[NotNull]
public IEnumerable<TKey> GetKeys([NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
return m_ValueToKeys[value];
}
[NotNull]
public TValue GetValue([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_KeyToValue[key];
}
public bool RemoveKey([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (!ContainsKey(key))
return false;
TValue value = m_KeyToValue[key];
m_KeyToValue.Remove(key);
IcdHashSet<TKey> keys = m_ValueToKeys[value];
keys.Remove(key);
if (keys.Count == 0)
m_ValueToKeys.Remove(value);
return true;
}
/// <summary>
/// Removes the value from the collection, and any keys that were using it
/// </summary>
/// <param name="value"></param>
/// <returns>true if items were removed, false if not</returns>
/// <exception cref="ArgumentNullException"></exception>
public bool RemoveValue([NotNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
if (!ContainsValue(value))
return false;
IcdHashSet<TKey> keys = m_ValueToKeys[value];
m_ValueToKeys.Remove(value);
foreach (TKey key in keys)
{
m_KeyToValue.Remove(key);
}
return true;
}
public bool TryGetValue([NotNull] TKey key, out TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_KeyToValue.TryGetValue(key, out value);
}
public bool TryGetKeys([NotNull] TValue value, out IEnumerable<TKey> keys)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (value == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("value");
IcdHashSet<TKey> keysInternal;
if (m_ValueToKeys.TryGetValue(value, out keysInternal))
{
keys = keysInternal;
return true;
}
keys = Enumerable.Empty<TKey>();
return false;
}
#endregion
#region IDictionary
[NotNull]
TValue IDictionary<TKey, TValue>.this[[NotNull] TKey key] { get { return GetValue(key); } set { Set(key, value); } }
bool IDictionary<TKey, TValue>.Remove([NotNull] 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([NotNull] KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
(m_KeyToValue as IDictionary<TKey, TValue>).CopyTo(array, arrayIndex);
}
#endregion
#region IEnumerable
[NotNull]
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_KeyToValue.GetEnumerator();
}
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}

View File

@@ -1,9 +1,8 @@
using System;
#if STANDARD
using System.Net.Mail;
#endif
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.Net.Mail;
#endif
namespace ICD.Common.Utils.Email
@@ -126,7 +125,7 @@ namespace ICD.Common.Utils.Email
public static class MailErrorCodeUtils
{
#if STANDARD
#if !SIMPLSHARP
public static eMailErrorCode FromNetStandardMailCode(SmtpStatusCode code)
{
switch (code)
@@ -185,8 +184,7 @@ namespace ICD.Common.Utils.Email
throw new ArgumentOutOfRangeException(nameof(code), code, null);
}
}
#endif
#if SIMPLSHARP
#else
public static eMailErrorCode FromSimplMailCode(CrestronMailFunctions.SendMailErrorCodes code)
{
switch (code)

View File

@@ -198,7 +198,7 @@ namespace ICD.Common.Utils
if (type == null)
throw new ArgumentNullException("type");
return GetFlagsExceptNone(type);
return GetValues(type).Where(v => (int)v != 0);
}
/// <summary>
@@ -448,7 +448,20 @@ namespace ICD.Common.Utils
if (type == null)
throw new ArgumentNullException("type");
return GetValuesUncached(type).Aggregate(0, (current, value) => current | (int)value);
return GetValues(type).Aggregate(0, (current, value) => current | (int)value);
}
/// <summary>
/// Gets an enum value of the given type with the inverse of the flags set
/// </summary>
/// <param name="value"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T GetInverseFlags<T>(T value)
where T : struct, IConvertible
{
int output = GetFlagsAllValue(typeof(T)) & ~(int)(object)value;
return (T)Enum.ToObject(typeof(T), output);
}
/// <summary>

View File

@@ -29,7 +29,7 @@ namespace ICD.Common.Utils.Extensions
#endif
.CodeBase;
#if STANDARD
#if !SIMPLSHARP
if (string.IsNullOrEmpty(path))
path = extends.Location;
#endif

View File

@@ -1,7 +1,12 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using System.Text.RegularExpressions;
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions
{
@@ -27,24 +32,6 @@ namespace ICD.Common.Utils.Extensions
#else
JsonSerializer.CreateDefault();
#endif
return extends.ReadAsObject<T>(serializer);
}
/// <summary>
/// Reads the current token in the reader and deserializes to the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="serializer"></param>
/// <returns></returns>
public static T ReadAsObject<T>([NotNull] this JsonReader extends, [NotNull] JsonSerializer serializer)
{
if (extends == null)
throw new ArgumentNullException("extends");
if (serializer == null)
throw new ArgumentNullException("serializer");
return serializer.Deserialize<T>(extends);
}

View File

@@ -1,8 +1,13 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Extensions
{

View File

@@ -1,5 +1,10 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
namespace ICD.Common.Utils.Extensions
{

View File

@@ -1,6 +1,11 @@
using System;
using ICD.Common.Properties;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
{

View File

@@ -1,4 +1,4 @@
#if !SIMPLSHARP
#if NETSTANDARD
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;

View File

@@ -13,34 +13,61 @@ namespace ICD.Common.Utils.Extensions
/// <returns></returns>
public static string ToReadableString(this TimeSpan extends)
{
StringBuilder builder = new StringBuilder();
if (extends.Days == 1)
builder.AppendFormat("{0} day, ", extends.Days);
else if (extends.Days > 1)
builder.AppendFormat("{0} days, ", extends.Days);
if (extends.Hours == 1)
builder.AppendFormat("{0} hour, ", extends.Hours);
else if (extends.Hours > 1)
builder.AppendFormat("{0} hours, ", extends.Hours);
if (extends.Minutes == 1)
builder.AppendFormat("{0} minute, ", extends.Minutes);
else if (extends.Minutes > 1)
builder.AppendFormat("{0} minutes, ", extends.Minutes);
if (extends.Seconds == 1)
builder.AppendFormat("{0} second, ", extends.Seconds);
else if (extends.Seconds > 1)
builder.AppendFormat("{0} seconds, ", extends.Seconds);
if (extends.Milliseconds > 0)
builder.AppendFormat("{0} ms", extends.Milliseconds);
return builder.ToString().TrimEnd(',', ' ');
return extends.ToReadableString(false);
}
/// <summary>
/// Writes the TimeSpan to a string in the format "A day/s, B hour/s, C minute/s, D second/s, E ms"
/// Omits any items that are 0.
/// Optionally hides the miliseconds
/// </summary>
/// <param name="extends"></param>
/// <param name="hideMilliseconds"></param>
/// <returns></returns>
public static string ToReadableString(this TimeSpan extends, bool hideMilliseconds)
{
int zeroComparison = extends.CompareTo(TimeSpan.Zero);
if (zeroComparison == 0)
return "Zero Time";
StringBuilder builder = new StringBuilder();
// Handle negative time spans
if (zeroComparison < 0)
builder.Append("-");
// Get absolute value so negatives can be ignored
TimeSpan timeSpan = extends.Duration();
if (timeSpan.Days == 1)
builder.AppendFormat("{0} day, ", timeSpan.Days);
else if (timeSpan.Days > 1)
builder.AppendFormat("{0} days, ", timeSpan.Days);
if (timeSpan.Hours == 1)
builder.AppendFormat("{0} hour, ", timeSpan.Hours);
else if (timeSpan.Hours > 1)
builder.AppendFormat("{0} hours, ", timeSpan.Hours);
if (timeSpan.Minutes == 1)
builder.AppendFormat("{0} minute, ", timeSpan.Minutes);
else if (timeSpan.Minutes > 1)
builder.AppendFormat("{0} minutes, ", timeSpan.Minutes);
if (timeSpan.Seconds == 1)
builder.AppendFormat("{0} second, ", timeSpan.Seconds);
else if (timeSpan.Seconds > 1)
builder.AppendFormat("{0} seconds, ", timeSpan.Seconds);
else if (hideMilliseconds && (long)timeSpan.TotalSeconds == 0)
builder.AppendFormat("0 seconds");
if (!hideMilliseconds && timeSpan.Milliseconds > 0)
builder.AppendFormat("{0} ms", timeSpan.Milliseconds);
return builder.ToString().TrimEnd(',', ' ');
}
/// <summary>
/// Adds the given number of hours to the time, wrapping every 24 hours without modifying the day.
/// </summary>

View File

@@ -96,11 +96,8 @@ namespace ICD.Common.Utils.Globalization
{
private const string SQL_LOCAL_DATABASE_FILE = "CultureInfo.sqlite";
private const string SQL_CONNECTION_STRING_FORMAT =
#if SIMPLSHARP
"Data Source={0};Version=3;ReadOnly=True";
#else
"Data Source={0}";
#endif
private const string SQL_CMD_SELECT_BY_NAME = "select * from cultureinfo where name = @name collate nocase";
private const string SQL_CMD_SELECT_BY_LCID = "select * from cultureinfo where lcid = @lcid";
private const string SQL_CMD_SELECT_BY_ID = "select * from cultureinfo where id = @id";
@@ -466,8 +463,9 @@ namespace ICD.Common.Utils.Globalization
}
}
}
catch (Exception)
catch (Exception e)
{
IcdErrorLog.Exception(e, "Error populating IcdCultureInfo cache - {0}", e.Message);
s_IsDatabasePresent = false;
return;
}

View File

@@ -1,35 +1,26 @@
<?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>
<TargetFrameworks>netstandard2.0;net472</TargetFrameworks>
<AssemblyName>ICD.Common.Utils</AssemblyName>
<RootNamespace>$(AssemblyName)</RootNamespace>
<RootNamespace>ICD.Common.Utils</RootNamespace>
<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>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<OutputPath>binNetCoreApp\$(Configuration)\</OutputPath>
<DefineConstants>TRACE;STANDARD</DefineConstants>
<OutputPath>bin\$(Configuration)\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Remove="binNetCoreApp\**" />
<Compile Remove="bin\**" />
<Compile Remove="SIMPLSharpLogs\**" />
<EmbeddedResource Remove="binNetCoreApp\**" />
<EmbeddedResource Remove="bin\**" />
<EmbeddedResource Remove="SIMPLSharpLogs\**" />
<None Remove="binNetCoreApp\**" />
<None Remove="bin\**" />
<None Remove="SIMPLSharpLogs\**" />
</ItemGroup>
<ItemGroup>
@@ -39,15 +30,17 @@
<None Remove="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="5.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
<PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Pastel" Version="2.1.0" />
<PackageReference Include="System.Management" Version="5.0.0" />
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Include="System.Security.Principal.Windows" Version="5.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Crestron.SimplSharp.SDK.Library" Version="2.18.96" />
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Microsoft.Data.SQLite" Version="5.0.4" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Microsoft.Win32.Registry" Version="5.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="Pastel" Version="2.1.0" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Management" Version="5.0.0" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Runtime.Loader" Version="4.3.0" />
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Security.Principal.Windows" Version="5.0.0" />
</ItemGroup>
<ItemGroup>
<None Update="CultureInfo.sqlite">
@@ -57,5 +50,4 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@@ -25,7 +25,7 @@
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<OutputPath>bin\Debug\net3.5\</OutputPath>
<DefineConstants>DEBUG;TRACE;SIMPLSHARP</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
@@ -38,7 +38,7 @@
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<OutputPath>bin\Release\net3.5\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<FileAlignment>512</FileAlignment>
@@ -79,12 +79,13 @@
<Compile Include="Attributes\AbstractIcdAttribute.cs" />
<Compile Include="Attributes\IIcdAttribute.cs" />
<Compile Include="Attributes\RangeAttribute.cs" />
<Compile Include="BigEndianBitConverter.cs" />
<Compile Include="Collections\BiDictionary.cs" />
<Compile Include="Collections\IcdOrderedDictionary.cs" />
<Compile Include="Collections\IcdSortedDictionary.cs" />
<Compile Include="Collections\INotifyCollectionChanged.cs" />
<Compile Include="Collections\PriorityQueue.cs" />
<Compile Include="Collections\RateLimitedEventQueue.cs" />
<Compile Include="Collections\ReverseLookupDictionary.cs" />
<Compile Include="Collections\WeakKeyDictionary.cs" />
<Compile Include="Comparers\FileNameEqualityComparer.cs" />
<Compile Include="Comparers\PredicateComparer.cs" />
@@ -121,9 +122,12 @@
<Compile Include="Extensions\CollectionExtensions.cs" />
<Compile Include="Extensions\RegistryExtensions.cs" />
<Compile Include="Extensions\VersionExtensions.cs" />
<Compile Include="ThreadedWorkerQueue.cs" />
<Compile Include="Threading\AbstractIcdResetEvent.cs" />
<Compile Include="Threading\IcdAutoResetEvent.cs" />
<Compile Include="IcdManualResetEvent.cs" />
<Compile Include="Threading\KeyedLock.cs" />
<Compile Include="Threading\ThreadedWorkerQueue.cs" />
<Compile Include="TimeZoneInfo\IcdTimeZoneInfo.cs" />
<Compile Include="ObfuscationSettings.cs" />
<Compile Include="Extensions\BoolExtensions.cs" />
<Compile Include="Extensions\ByteExtensions.cs" />
<Compile Include="Extensions\CultureInfoExtensions.cs" />
@@ -214,10 +218,10 @@
<Compile Include="PrettyPrint.cs" />
<Compile Include="ProgramUtils.cs" />
<Compile Include="ReflectionUtils.cs" />
<Compile Include="SafeCriticalSection.cs" />
<Compile Include="SafeCriticalSection.SimplSharp.cs" />
<Compile Include="SafeCriticalSection.Standard.cs" />
<Compile Include="SafeMutex.cs" />
<Compile Include="Threading\SafeCriticalSection.cs" />
<Compile Include="Threading\SafeCriticalSection.SimplSharp.cs" />
<Compile Include="Threading\SafeCriticalSection.Standard.cs" />
<Compile Include="Threading\SafeMutex.cs" />
<Compile Include="SPlusUtils.cs" />
<Compile Include="Sqlite\eDbType.cs" />
<Compile Include="Sqlite\IcdDbDataReader.cs" />
@@ -230,7 +234,7 @@
<Compile Include="Sqlite\IIcdDataRecord.cs" />
<Compile Include="StringUtils.cs" />
<Compile Include="TableBuilder.cs" />
<Compile Include="ThreadingUtils.cs" />
<Compile Include="Threading\ThreadingUtils.cs" />
<Compile Include="Timers\IcdStopwatch.cs" />
<Compile Include="Timers\IcdTimer.cs" />
<Compile Include="Timers\Repeater.cs" />
@@ -238,6 +242,8 @@
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoAdjustmentRule.cs" />
<Compile Include="TimeZoneInfo\IcdTimeZoneInfoTransitionTime.cs" />
<Compile Include="TryUtils.cs" />
<Compile Include="Types\AbstractNotifyFlagsChanged.cs" />
<Compile Include="Types\GenericNotifyFlagsChanged.cs" />
<Compile Include="UriQueryBuilder.cs" />
<Compile Include="UriUtils.cs" />
<Compile Include="VersionSpan.cs" />
@@ -259,8 +265,4 @@
</None>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CompactFramework.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>if /I "$(ConfigurationName)" == "Release" Eazfuscator.NET.exe "$(TargetPath)" --msbuild-project-path "$(ProjectPath)" --msbuild-project-configuration "$(ConfigurationName)" --msbuild-project-platform "$(PlatformName)" --msbuild-solution-path "$(SolutionPath)" -n --newline-flush -v 5.2 --configuration-file="$(ProjectDir)ObfuscationSettings.cs"
rem S# Pro preparation will execute after these operations</PostBuildEvent>
</PropertyGroup>
</Project>

View File

@@ -299,7 +299,6 @@ namespace ICD.Common.Utils.IO.Compression
private Entry GetEntry(string fileName)
{
fileName = fileName.Replace("\\", "/").Trim().TrimStart('/');
var entry = Entries.FirstOrDefault(e => e.Name == fileName);
if (entry == null)

View File

@@ -1,5 +1,5 @@
using System;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp.CrestronIO;
#else
using ICD.Common.Utils.Extensions;
@@ -13,7 +13,7 @@ namespace ICD.Common.Utils.IO
{
public static string GetApplicationDirectory()
{
#if SIMPLSHARP
#if !NETSTANDARD
return Directory.GetApplicationDirectory();
#else
string pathToDll = Assembly.GetExecutingAssembly().GetPath();
@@ -27,10 +27,10 @@ namespace ICD.Common.Utils.IO
/// <returns></returns>
public static string GetApplicationRootDirectory()
{
#if SIMPLSHARP
#if !NETSTANDARD
return Directory.GetApplicationRootDirectory();
#else
return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().GetPath());
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetPath());
#endif
}

View File

@@ -127,7 +127,7 @@ namespace ICD.Common.Utils.IO
File.Move(sourceFileName, destFileName);
}
#if STANDARD
#if !SIMPLSHARP
public static void SetAttributes(string path, FileAttributes attributes, bool recursive)
{
File.SetAttributes(path, attributes);

View File

@@ -1,7 +1,7 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#elif STANDARD
#else
using System.IO;
#endif

View File

@@ -1,6 +1,6 @@
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#elif STANDARD
#else
using System.IO;
#endif

View File

@@ -4,7 +4,7 @@ using System.Text.RegularExpressions;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Extensions;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp;
#else
using System.Diagnostics;
@@ -40,8 +40,8 @@ namespace ICD.Common.Utils
{
if (s_IsConsoleApp == null)
{
#if SIMPLSHARP
s_IsConsoleApp = true;
#if !NETSTANDARD
s_IsConsoleApp = false;
#else
try
{
@@ -94,13 +94,17 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void ConsoleCommandResponse(string message, params object[] args)
{
// No console in Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return;
if (args != null && args.Any())
message = string.Format(message, args);
message = FixLineEndings(message);
#if SIMPLSHARP
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Appliance)
#if !NETSTANDARD
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
{
try
{
@@ -110,11 +114,10 @@ namespace ICD.Common.Utils
{
PrintLine(message);
}
return;
return;
}
#endif
PrintLine(message);
PrintLine(message);
}
public static void PrintLine(string message)
@@ -125,8 +128,8 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.Server)
#if !NETSTANDARD
if (IcdEnvironment.CrestronDevicePlatform != IcdEnvironment.eCrestronDevicePlatform.Server)
CrestronConsole.PrintLine(fixedMessage);
#else
Trace.WriteLine(AnsiUtils.StripAnsi(fixedMessage));
@@ -169,8 +172,8 @@ namespace ICD.Common.Utils
try
{
#if SIMPLSHARP
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.Server)
#if !NETSTANDARD
if (IcdEnvironment.CrestronDevicePlatform != IcdEnvironment.eCrestronDevicePlatform.Server)
CrestronConsole.Print(fixedMessage);
#else
Trace.Write(AnsiUtils.StripAnsi(fixedMessage));
@@ -207,9 +210,9 @@ namespace ICD.Common.Utils
public static bool SendControlSystemCommand(string command, ref string result)
{
#if SIMPLSHARP
// No console on VC4
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
#if !NETSTANDARD
// No console on Crestron Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return false;
return CrestronConsole.SendControlSystemCommand(command, ref result);
@@ -220,9 +223,13 @@ namespace ICD.Common.Utils
public static bool AddNewConsoleCommand(Action<string> callback, string command, string help, eAccessLevel accessLevel)
{
#if SIMPLSHARP
#if !NETSTANDARD
// Avoid crashing Simpl applications
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.Appliance)
if (IcdEnvironment.CrestronRuntimeEnvironment != IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro)
return false;
// No console in Crestron Server
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return false;
if (CrestronConsole.ConsoleRegistered)

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
@@ -194,14 +194,16 @@ namespace ICD.Common.Utils
{
// Cache the runtime environment
s_Framework = eFramework.Crestron;
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SIMPL)
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Simpl;
else if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Appliance;
else
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Server;
s_CrestronRuntimeEnvironment = CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SIMPL
? eCrestronRuntimeEnvironment.SimplPlus
: eCrestronRuntimeEnvironment.SimplSharpPro;
s_CrestronDevicePlatform = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance
? eCrestronDevicePlatform.Appliance
: eCrestronDevicePlatform.Server;
// todo: Make this check more robust
s_CrestronSeries = Type.GetType("Mono.Runtime") != null ? eCrestronSeries.FourSeries : eCrestronSeries.ThreeSeries;
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler;

View File

@@ -1,14 +1,15 @@
#if STANDARD
#if NETSTANDARD
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using ICD.Common.Properties;
using ICD.Common.Utils.EventArguments;
using ICD.Common.Utils.Timers;
using AddressFamily = System.Net.Sockets.AddressFamily;
using Dns = System.Net.Dns;
namespace ICD.Common.Utils
{
@@ -109,6 +110,7 @@ namespace ICD.Common.Utils
s_Framework = eFramework.Standard;
s_CrestronSeries = eCrestronSeries.Na;
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Na;
s_CrestronDevicePlatform = eCrestronDevicePlatform.Na;
}
/// <summary>
@@ -172,6 +174,7 @@ namespace ICD.Common.Utils
handler(sessionId, reasonCode);
}
/// <summary>
/// Call this method to raise the device added/removed event for an added device
/// Uses a timer to attempt to compress multiple events into a single event
/// </summary>

View File

@@ -12,6 +12,7 @@ namespace ICD.Common.Utils
public enum eFramework
{
Crestron,
Framework,
Standard
}
@@ -31,9 +32,15 @@ namespace ICD.Common.Utils
public enum eCrestronRuntimeEnvironment
{
Na, //Non-Crestron
Simpl, // Running in Simpl, Non-Pro
Appliance, // S#Pro running on a Crestron hardware appliance
Server // S#Pro running on a server (VC-4)
SimplPlus, // Running in Simpl+, Non-Pro
SimplSharpPro // Running in Simpl#Pro
}
public enum eCrestronDevicePlatform
{
Na, // Non-Crestron
Appliance, // Running on Crestron hardware appliance
Server // Running on VC-4 Server
}
/// <summary>
@@ -119,14 +126,24 @@ namespace ICD.Common.Utils
private static readonly eFramework s_Framework;
private static readonly eCrestronSeries s_CrestronSeries;
private static readonly eCrestronRuntimeEnvironment s_CrestronRuntimeEnvironment;
private static readonly eCrestronDevicePlatform s_CrestronDevicePlatform;
private static readonly SafeCriticalSection s_ProgramInitializationSection = new SafeCriticalSection();
private static bool s_ProgramInitializationComplete;
public static eFramework Framework {get { return s_Framework; }}
public static eCrestronSeries CrestronSeries {get { return s_CrestronSeries; }}
/// <summary>
/// Crestron environment being run in, SimplPlus or SimplSharpPro
/// </summary>
public static eCrestronRuntimeEnvironment CrestronRuntimeEnvironment {get { return s_CrestronRuntimeEnvironment; }}
/// <summary>
/// Crestron platform being run on, Appliance (crestron hardware) or Server (VC-4)
/// </summary>
public static eCrestronDevicePlatform CrestronDevicePlatform { get { return s_CrestronDevicePlatform; } }
/// <summary>
/// Returns true if the program has been flagged as completely initialized.
/// </summary>

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp;
#else
using System.Diagnostics;
@@ -32,7 +32,7 @@ namespace ICD.Common.Utils
private static readonly Dictionary<string, Action<string, Exception>> s_LogMethods =
new Dictionary<string, Action<string, Exception>>
{
#if SIMPLSHARP
#if !NETSTANDARD
{ERROR, (m, e) => ErrorLog.Error(m)},
{WARN, (m, e) => ErrorLog.Warn(m)},
{NOTICE, (m, e) => ErrorLog.Notice(m)},

View File

@@ -0,0 +1,40 @@
using ICD.Common.Properties;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.Threading;
#endif
namespace ICD.Common.Utils
{
/// <summary>
/// Notifies one or more waiting threads that an event has occurred.
/// </summary>
[PublicAPI]
public sealed class IcdManualResetEvent : AbstractIcdResetEvent
{
/// <summary>
/// Initializes a new instance of the IcdManualResetEvent class with the initial state to nonsignaled.
/// </summary>
[PublicAPI]
public IcdManualResetEvent() : this(false)
{
}
/// <summary>
/// Initializes a new instance of the IcdManualResetEvent class with a Boolean value indicating whether to set the initial state to signaled.
/// </summary>
/// <param name="initialState">true to set the initial state signaled; false to set the initial state to nonsignaled.</param>
[PublicAPI]
public IcdManualResetEvent(bool initialState) :
#if SIMPLSHARP
base(new CEvent(false, initialState))
#else
base(new ManualResetEvent(initialState))
#endif
{
}
}
}

View File

@@ -92,7 +92,7 @@ namespace ICD.Common.Utils
throw new ArgumentNullException("uri");
if (!uri.IsAbsoluteUri)
uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + uri);
uri = new Uri(Uri.UriSchemeHttp + Uri.SchemeDelimiter + "localhost" + uri);
Fragment = uri.Fragment;
Host = uri.Host;

View File

@@ -1,7 +1,12 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
{
@@ -128,23 +133,28 @@ namespace ICD.Common.Utils.Json
throw new ArgumentNullException("serializer");
if (reader.TokenType == JsonToken.Null)
return default(T);
return existingValue;
if (reader.TokenType != JsonToken.StartObject)
throw new FormatException(string.Format("Expected {0} got {1}", JsonToken.StartObject, reader.TokenType));
return ReadObject(reader, serializer);
return ReadObject(reader, existingValue, serializer);
}
/// <summary>
/// Override to handle deserialization of the current StartObject token.
/// </summary>
/// <param name="reader"></param>
/// <param name="existingValue"></param>
/// <param name="serializer"></param>
/// <returns></returns>
protected virtual T ReadObject(JsonReader reader, JsonSerializer serializer)
protected virtual T ReadObject(JsonReader reader, T existingValue, JsonSerializer serializer)
{
T output = Instantiate();
// ReSharper disable CompareNonConstrainedGenericWithNull
// ReSharper disable ConvertConditionalTernaryToNullCoalescing
T output = existingValue == null ? Instantiate() : existingValue;
// ReSharper restore ConvertConditionalTernaryToNullCoalescing
// ReSharper restore CompareNonConstrainedGenericWithNull
reader.ReadObject(serializer, (p, r, s) => ReadProperty(p, r, output, s));

View File

@@ -1,6 +1,11 @@
using System;
using ICD.Common.Utils.Extensions;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Json
{

View File

@@ -1,7 +1,12 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
{

View File

@@ -1,10 +1,15 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using System.Linq;
using System.Text;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;
using ICD.Common.Utils.IO;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
{

View File

@@ -1,6 +1,11 @@
using System;
using ICD.Common.Utils.Extensions;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using ICD.Common.Utils.Extensions;
namespace ICD.Common.Utils.Json
{

View File

@@ -1,4 +1,10 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
#else
using Newtonsoft.Json;
#endif
using System;
using System.Collections.Generic;
#if SIMPLSHARP
using Crestron.SimplSharp.Reflection;
@@ -7,7 +13,6 @@ using System.Reflection;
using System.Runtime.ExceptionServices;
#endif
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
{

View File

@@ -1,6 +0,0 @@
using System.Reflection;
[assembly: Obfuscation(Feature = "platform api: System.Threading.Thread, System.Reflection.*, System.IO.Stream, System.Windows.Forms.*", Exclude = true)]
[assembly: Obfuscation(Feature = "rename symbol names with printable characters", Exclude = false)]
[assembly: Obfuscation(Feature = "code control flow obfuscation", Exclude = false)]
[assembly: Obfuscation(Feature = "Apply to type * when class: renaming", Exclude = true, ApplyToMembers = false)]

View File

@@ -26,7 +26,7 @@ namespace ICD.Common.Utils
public static string RootPath {
get
{
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return IcdDirectory.GetApplicationRootDirectory();
return IcdDirectory.GetDirectoryRoot(IcdPath.DirectorySeparatorChar.ToString());
@@ -62,14 +62,15 @@ namespace ICD.Common.Utils
{
get
{
#if SIMPLSHARP
switch (IcdEnvironment.CrestronSeries)
{
case IcdEnvironment.eCrestronSeries.FourSeries:
return Join(RootPath, "user");
default:
return Join(RootPath, "User");
}
#if !NETSTANDARD
// Server uses "User" and rest of the e-seres uses "user" :facepalm:
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return Join(RootPath, "User");
// Rest of processors
// 3-series traditionally used "User", but isn't case sensitive
return Join(RootPath, "user");
#elif LINUX
return Join(RootPath, "opt", "ICD.Connect");
#else
@@ -94,7 +95,7 @@ namespace ICD.Common.Utils
get
{
// Crestron Server doesn't have meaningful program number
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return "ProgramConfig";
return string.Format("Program{0:D2}Config", ProgramUtils.ProgramNumber);
@@ -117,7 +118,7 @@ namespace ICD.Common.Utils
get
{
// Crestron Server doesn't have meaningful program number
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return "ProgramData";
return string.Format("Program{0:D2}Data", ProgramUtils.ProgramNumber);
@@ -157,7 +158,7 @@ namespace ICD.Common.Utils
string directoryName;
// Crestron Server doesn't have meaningful program number
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Server)
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
directoryName = "ProgramLogs";
else
directoryName = string.Format("Program{0:D2}Logs", ProgramUtils.ProgramNumber);
@@ -175,24 +176,25 @@ namespace ICD.Common.Utils
{
get
{
if (IcdEnvironment.Framework == IcdEnvironment.eFramework.Crestron)
if (IcdEnvironment.Framework != IcdEnvironment.eFramework.Standard)
{
// 3-series
if (IcdEnvironment.CrestronSeries == IcdEnvironment.eCrestronSeries.ThreeSeries)
return Join(RootPath, "HTML");
// 4-series non-server (because Crestron)
if (IcdEnvironment.CrestronRuntimeEnvironment == IcdEnvironment.eCrestronRuntimeEnvironment.Appliance)
return Join(RootPath, "html");
// 4-series server (because Crestron)
return Join(RootPath, "Html");
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return Join(RootPath, "Html");
// 4-series non-server (because Crestron)
return Join(RootPath, "html");
}
#if LINUX
return Join(RootPath, "var", "www", "html");
return Join(RootPath, "var", "www", "html");
#else
return "C:\\INetPub";
return "C:\\INetPub";
#endif
}
}

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using System.Globalization;
using Crestron.SimplSharp;
using ICD.Common.Utils.Services;

View File

@@ -1,4 +1,4 @@
#if !SIMPLSHARP
#if NETSTANDARD
using System;
using System.Diagnostics;
using System.Linq;

View File

@@ -2,7 +2,7 @@
using ICD.Common.Utils.IO;
using ICD.Common.Utils.Services;
using ICD.Common.Utils.Services.Logging;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp;
using ICD.Common.Properties;
using ICD.Common.Utils.Extensions;

View File

@@ -1,4 +1,4 @@
#if !SIMPLSHARP
#if NETSTANDARD
using ICD.Common.Utils.IO;
using System;
using System.Reflection;

View File

@@ -24,23 +24,25 @@ namespace ICD.Common.Utils
switch (IcdEnvironment.Framework)
{
case IcdEnvironment.eFramework.Crestron:
// No console in servers
if (IcdEnvironment.CrestronDevicePlatform == IcdEnvironment.eCrestronDevicePlatform.Server)
return;
switch (IcdEnvironment.CrestronRuntimeEnvironment)
{
case IcdEnvironment.eCrestronRuntimeEnvironment.Simpl:
case IcdEnvironment.eCrestronRuntimeEnvironment.SimplPlus:
int length = Math.Min(13, name.Length);
name = name.Substring(0, length).PadRight(13);
break;
case IcdEnvironment.eCrestronRuntimeEnvironment.Appliance:
case IcdEnvironment.eCrestronRuntimeEnvironment.SimplSharpPro:
int proLength = Math.Min(26 - 1, name.Length);
name = name.Substring(0, proLength).PadRight(26);
break;
case IcdEnvironment.eCrestronRuntimeEnvironment.Server:
// No console
return;
default:
throw new ArgumentOutOfRangeException();
}
break;
case IcdEnvironment.eFramework.Framework:
case IcdEnvironment.eFramework.Standard:
name += ' ';
break;

View File

@@ -3,5 +3,5 @@ using System.Reflection;
[assembly: AssemblyTitle("ICD.Common.Utils")]
[assembly: AssemblyCompany("ICD Systems")]
[assembly: AssemblyProduct("ICD.Common.Utils")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2021")]
[assembly: AssemblyVersion("15.2.0.0")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2023")]
[assembly: AssemblyVersion("17.2.0.0")]

View File

@@ -11,9 +11,11 @@ using Crestron.SimplSharp.Reflection;
using Activator = Crestron.SimplSharp.Reflection.Activator;
#else
using System.Reflection;
using Activator = System.Activator;
#endif
#if NETSTANDARD
using Microsoft.Extensions.DependencyModel;
using System.Runtime.Loader;
using Activator = System.Activator;
#endif
namespace ICD.Common.Utils
@@ -274,17 +276,12 @@ namespace ICD.Common.Utils
string fileNameWithOutExtension = IcdPath.GetFileNameWithoutExtension(path);
#if SIMPLSHARP
#if !NETSTANDARD
try
{
return Assembly.Load(new AssemblyName {Name = fileNameWithOutExtension});
}
catch (IOException)
{
return Assembly.LoadFrom(path);
}
catch (FileNotFoundException)
catch (Exception)
{
return Assembly.LoadFrom(path);
}

View File

@@ -37,7 +37,7 @@ namespace ICD.Common.Utils
}
/// <summary>
/// Adds the property with the given name and value to the builder without any additonal formatting.
/// Adds the property with the given name and value to the builder without any additional formatting.
/// </summary>
/// <param name="name"></param>
/// <param name="value"></param>

View File

@@ -12,7 +12,7 @@ namespace ICD.Common.Utils
/// Convert two ushort's to an int
/// </summary>
/// <param name="lowWord">ushort for the least significant 16 bits</param>
/// <param name="highWord">ushort for the most significant 1 bits</param>
/// <param name="highWord">ushort for the most significant 16 bits</param>
/// <returns></returns>
[PublicAPI("S+")]
public static int ConvertToInt(ushort lowWord, ushort highWord)
@@ -20,4 +20,4 @@ namespace ICD.Common.Utils
return (highWord << 16) + lowWord;
}
}
}
}

View File

@@ -1,75 +0,0 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.Threading;
#endif
namespace ICD.Common.Utils
{
/// <summary>
/// Like the CCriticalSection, the CMutex tends to get disposed before the parent is
/// done with it. This class is an attempt to gracefully handle the ObjectDisposedExceptions
/// we see on program termination, ocassionally causing the program to restart instead of stop.
/// </summary>
public sealed class SafeMutex
{
#if SIMPLSHARP
private readonly CMutex m_Mutex;
#else
private readonly Mutex m_Mutex;
#endif
/// <summary>
/// Constructor.
/// </summary>
public SafeMutex()
{
#if SIMPLSHARP
m_Mutex = new CMutex();
#else
m_Mutex = new Mutex();
#endif
}
#region Methods
/// <summary>
/// Waits the given number of milliseconds to aquire the mutex.
/// </summary>
/// <param name="timeout"></param>
/// <returns>True if the mutex was aquired.</returns>
public bool WaitForMutex(int timeout)
{
try
{
#if SIMPLSHARP
return m_Mutex.WaitForMutex(timeout);
#else
return m_Mutex.WaitOne(timeout);
#endif
}
catch (ObjectDisposedException)
{
return false;
}
}
/// <summary>
/// Releases the mutex.
/// </summary>
public void ReleaseMutex()
{
try
{
m_Mutex.ReleaseMutex();
}
catch (ObjectDisposedException)
{
// Releasing a disposed mutex in this case is valid behaviour
}
}
#endregion
}
}

View File

@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
#if STANDARD
#if !SIMPLSHARP
using System.Text;
#endif
using ICD.Common.Properties;
@@ -106,7 +106,7 @@ namespace ICD.Common.Utils.Services.Logging
if (e == null)
throw new ArgumentNullException("e");
#if STANDARD
#if !SIMPLSHARP
if (e is AggregateException)
{
extends.AddEntry(severity, e as AggregateException, message);
@@ -117,7 +117,7 @@ namespace ICD.Common.Utils.Services.Logging
IcdEnvironment.NewLine, e.Message, e.StackTrace));
}
#if STANDARD
#if !SIMPLSHARP
/// <summary>
/// Logs an aggregate exception as a formatted list of inner exceptions.
/// </summary>

View File

@@ -1,6 +1,12 @@
using System;
#if NETFRAMEWORK
extern alias RealNewtonsoft;
using RealNewtonsoft.Newtonsoft.Json;
using RealNewtonsoft.Newtonsoft.Json.Converters;
#else
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
#endif
using System;
namespace ICD.Common.Utils.Services.Logging
{

View File

@@ -1,5 +1,5 @@
using System;
#if SIMPLSHARP
#if !NETSTANDARD
using SqliteCommand = Crestron.SimplSharp.SQLite.SQLiteCommand;
#else
using Microsoft.Data.Sqlite;

View File

@@ -1,6 +1,6 @@
using System;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
#if !NETSTANDARD
using SqliteConnection = Crestron.SimplSharp.SQLite.SQLiteConnection;
#else
using Microsoft.Data.Sqlite;

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using SqliteDataReader = Crestron.SimplSharp.SQLite.SQLiteDataReader;
#else
using System;

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using SqliteParameter = Crestron.SimplSharp.SQLite.SQLiteParameter;
#else
using Microsoft.Data.Sqlite;

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using SqliteParameterCollection = Crestron.SimplSharp.SQLite.SQLiteParameterCollection;
#else
using Microsoft.Data.Sqlite;

View File

@@ -1,4 +1,4 @@
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp.CrestronData;
#else
using System;
@@ -41,14 +41,14 @@ namespace ICD.Common.Utils.Sqlite
public static class DbTypeExtensions
{
public static
#if SIMPLSHARP
#if !NETSTANDARD
DbType
#else
SqliteType
#endif
ToParamType(this eDbType extends)
{
#if SIMPLSHARP
#if !NETSTANDARD
return (DbType)extends;
#else
switch (extends)

View File

@@ -13,7 +13,7 @@ namespace ICD.Common.Utils
/// </summary>
public sealed class TableBuilder
{
#if SIMPLSHARP
#if !NETSTANDARD
private const char HORIZONTAL = '-';
private const char VERTICAL = '|';
private const char INTERSECT = '+';
@@ -230,7 +230,7 @@ namespace ICD.Common.Utils
private void AppendTopSeparator(StringBuilder builder, IList<int> columnWidths)
{
#if SIMPLSHARP
#if !NETSTANDARD
// Can't do fancy tables so don't bother drawing the top row
#else
builder.Append(DOWN_RIGHT).Append(HORIZONTAL);
@@ -256,7 +256,7 @@ namespace ICD.Common.Utils
private void AppendBottomSeparator(StringBuilder builder, IList<int> columnWidths)
{
#if SIMPLSHARP
#if !NETSTANDARD
AppendSeparator(builder, columnWidths);
#else
builder.Append(UP_RIGHT).Append(HORIZONTAL);
@@ -282,7 +282,7 @@ namespace ICD.Common.Utils
private static void AppendRow(StringBuilder builder, IList<string> row, IList<int> columnWidths)
{
#if !SIMPLSHARP
#if NETSTANDARD
builder.Append(VERTICAL).Append(' ');
#endif
@@ -298,7 +298,7 @@ namespace ICD.Common.Utils
builder.Append(VERTICAL);
}
#if !SIMPLSHARP
#if NETSTANDARD
builder.Append(VERTICAL);
#endif
@@ -307,7 +307,7 @@ namespace ICD.Common.Utils
private static void AppendSeparator(StringBuilder builder, IList<int> columnWidths)
{
#if !SIMPLSHARP
#if NETSTANDARD
builder.Append(VERTICAL_RIGHT).Append(HORIZONTAL);
#endif
@@ -324,7 +324,7 @@ namespace ICD.Common.Utils
builder.Append(INTERSECT);
}
#if !SIMPLSHARP
#if NETSTANDARD
builder.Append(VERTICAL_LEFT);
#endif

View File

@@ -1,162 +0,0 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Collections;
namespace ICD.Common.Utils
{
/// <summary>
/// Utilizes a priority queue to store items
/// Dequeues items in priority order and processes
/// them in a worker thread one at a time
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class ThreadedWorkerQueue<T>
{
private readonly PriorityQueue<T> m_Queue;
private readonly SafeCriticalSection m_QueueSection;
private readonly SafeCriticalSection m_ProcessSection;
private readonly Action<T> m_ProcessAction;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="processItemAction">Action to process the dequeued items</param>
public ThreadedWorkerQueue([NotNull] Action<T> processItemAction)
{
if (processItemAction == null)
throw new ArgumentNullException("processItemAction");
m_Queue = new PriorityQueue<T>();
m_QueueSection = new SafeCriticalSection();
m_ProcessSection = new SafeCriticalSection();
m_ProcessAction = processItemAction;
}
#region Queue Methods
/// <summary>
/// Clears the collection.
/// </summary>
[PublicAPI]
public void Clear()
{
m_QueueSection.Execute(() => m_Queue.Clear());
}
/// <summary>
/// Adds the item to the end of the queue.
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void Enqueue([CanBeNull] T item)
{
Enqueue(() => m_Queue.Enqueue(item));
}
/// <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([CanBeNull] T item, int priority)
{
Enqueue(() => m_Queue.Enqueue(item, priority));
}
/// <summary>
/// Adds the item to the queue with the given priority at the given index.
/// </summary>
/// <param name="item"></param>
/// <param name="priority"></param>
/// <param name="position"></param>
[PublicAPI]
public void Enqueue([CanBeNull] T item, int priority, int position)
{
Enqueue(() => m_Queue.Enqueue(item, priority, position));
}
/// <summary>
/// Enqueues the item at the beginning of the queue.
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void EnqueueFirst([CanBeNull] T item)
{
Enqueue(() => m_Queue.EnqueueFirst(item));
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
[PublicAPI]
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove)
{
Enqueue(() => m_Queue.EnqueueRemove(item, remove));
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
/// <param name="priority"></param>
[PublicAPI]
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority)
{
Enqueue(() => m_Queue.EnqueueRemove(item, remove, priority));
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
/// <param name="priority"></param>
/// <param name="deDuplicateToEndOfQueue"></param>
[PublicAPI]
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority, bool deDuplicateToEndOfQueue)
{
Enqueue(() => m_Queue.EnqueueRemove(item, remove, priority, deDuplicateToEndOfQueue));
}
#endregion
#region Private Methods
private void Enqueue(Action enqueueAction)
{
m_QueueSection.Execute(enqueueAction);
ThreadingUtils.SafeInvoke(ProcessQueue);
}
private void ProcessQueue()
{
if (!m_ProcessSection.TryEnter())
return;
try
{
T item = default(T);
while (m_QueueSection.Execute(() => m_Queue.TryDequeue(out item)))
m_ProcessAction(item);
}
finally
{
m_ProcessSection.Leave();
}
}
#endregion
}
}

View File

@@ -0,0 +1,106 @@
using System;
using ICD.Common.Properties;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.Threading;
#endif
namespace ICD.Common.Utils
{
public abstract class AbstractIcdResetEvent : IDisposable
{
#if SIMPLSHARP
private readonly CEvent m_Event;
#else
private readonly EventWaitHandle m_Event;
#endif
#if SIMPLSHARP
/// <summary>
/// Initializes a new instance of the IcdManualResetEvent class with the CEvent
/// </summary>
[PublicAPI]
protected AbstractIcdResetEvent(CEvent eventHandle)
{
m_Event = eventHandle;
}
#else
/// <summary>
/// Initializes a new instance of the IcdManualResetEvent class with the CEvent
/// </summary>
[PublicAPI]
protected AbstractIcdResetEvent(EventWaitHandle eventHandle)
{
m_Event = eventHandle;
}
#endif
/// <summary>
/// Sets the state of the event to signaled, allowing one or more waiting threads to proceed.
/// </summary>
/// <returns>true if the operation succeeds; otherwise, false.</returns>
[PublicAPI]
public bool Set()
{
return m_Event.Set();
}
/// <summary>
/// Sets the state of the event to nonsignaled, causing threads to block.
/// </summary>
/// <returns>true if the operation succeeds; otherwise, false.</returns>
[PublicAPI]
public bool Reset()
{
return m_Event.Reset();
}
/// <summary>
/// Function to wait for the event to be signaled. This will block indefinitely until the event is signaled.
/// </summary>
/// <returns>True if the current instance receives a signal otherwise false.</returns>
[PublicAPI]
public bool WaitOne()
{
#if SIMPLSHARP
return m_Event.Wait();
#else
return m_Event.WaitOne();
#endif
}
/// <summary>
/// Function to wait for the event to be signaled.
/// </summary>
/// <param name="timeout">Timeout in milliseconds or Timeout.Infinite to wait indefinitely.</param>
/// <returns>True if the current instance receives a signal otherwise false.</returns>
public bool WaitOne(int timeout)
{
#if SIMPLSHARP
return m_Event.Wait(timeout);
#else
return m_Event.WaitOne(timeout);
#endif
}
/// <summary>
/// Clean up of resources.
/// </summary>
public void Dispose()
{
m_Event.Dispose();
}
/// <summary>
/// Close the event to release all resources used by this instance.
/// </summary>
[PublicAPI]
public void Close()
{
m_Event.Close();
}
}
}

View File

@@ -0,0 +1,39 @@
using ICD.Common.Properties;
#if SIMPLSHARP
using Crestron.SimplSharp;
# else
using System.Threading;
#endif
namespace ICD.Common.Utils
{
/// <summary>
/// Notifies one or more waiting threads that an event has occurred. Every thread that passes WaitOne causes the event to be reset
/// </summary>
[PublicAPI]
public sealed class IcdAutoResetEvent : AbstractIcdResetEvent
{
/// <summary>
/// Initializes a new instance of the IcdAutoResetEvent class with the initial state to nonsignaled.
/// </summary>
[PublicAPI]
public IcdAutoResetEvent() : this(false)
{
}
/// <summary>
/// Initializes a new instance of the IcdAutoResetEvent class with a Boolean value indicating whether to set the initial state to signaled.
/// </summary>
/// <param name="initialState">true to set the initial state signaled; false to set the initial state to nonsignaled.</param>
[PublicAPI]
public IcdAutoResetEvent(bool initialState) :
#if SIMPLSHARP
base(new CEvent(true, initialState))
#else
base(new AutoResetEvent(initialState))
#endif
{
}
}
}

View File

@@ -0,0 +1,139 @@
#if !SIMPLSHARP
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Threading
{
public sealed class KeyedLock<TKey>
{
private readonly Dictionary<TKey, (SemaphoreSlim, int)> m_PerKey;
private readonly Queue<SemaphoreSlim> m_Pool;
private readonly int m_PoolCapacity;
public KeyedLock(IEqualityComparer<TKey> keyComparer = null, int poolCapacity = 10)
{
m_PerKey = new Dictionary<TKey, (SemaphoreSlim, int)>(keyComparer);
m_Pool = new Queue<SemaphoreSlim>(poolCapacity);
m_PoolCapacity = poolCapacity;
}
#region Methods
[PublicAPI]
public async Task<bool> WaitAsync(TKey key, int millisecondsTimeout,
CancellationToken cancellationToken = default)
{
var semaphore = GetSemaphore(key);
bool entered = false;
try
{
entered = await semaphore.WaitAsync(millisecondsTimeout,
cancellationToken).ConfigureAwait(false);
}
finally
{
if (!entered) ReleaseSemaphore(key, entered: false);
}
return entered;
}
[PublicAPI]
public Task WaitAsync(TKey key, CancellationToken cancellationToken = default)
=> WaitAsync(key, Timeout.Infinite, cancellationToken);
[PublicAPI]
public bool Wait(TKey key, int millisecondsTimeout,
CancellationToken cancellationToken = default)
{
var semaphore = GetSemaphore(key);
bool entered = false;
try
{
entered = semaphore.Wait(millisecondsTimeout, cancellationToken);
}
finally
{
if (!entered)
ReleaseSemaphore(key, entered: false);
}
return entered;
}
[PublicAPI]
public void Wait(TKey key, CancellationToken cancellationToken = default)
=> Wait(key, Timeout.Infinite, cancellationToken);
[PublicAPI]
public void Release(TKey key) => ReleaseSemaphore(key, entered: true);
#endregion
#region Private Methods
private SemaphoreSlim GetSemaphore(TKey key)
{
SemaphoreSlim semaphore;
lock (m_PerKey)
{
if (m_PerKey.TryGetValue(key, out var entry))
{
int counter;
(semaphore, counter) = entry;
m_PerKey[key] = (semaphore, ++counter);
}
else
{
lock (m_Pool) semaphore = m_Pool.Count > 0 ? m_Pool.Dequeue() : null;
if (semaphore == null) semaphore = new SemaphoreSlim(1, 1);
m_PerKey[key] = (semaphore, 1);
}
}
return semaphore;
}
private void ReleaseSemaphore(TKey key, bool entered)
{
SemaphoreSlim semaphore;
int counter;
lock (m_PerKey)
{
if (m_PerKey.TryGetValue(key, out var entry))
{
(semaphore, counter) = entry;
counter--;
if (counter == 0)
m_PerKey.Remove(key);
else
m_PerKey[key] = (semaphore, counter);
}
else
{
throw new InvalidOperationException("Key not found.");
}
}
if (entered)
semaphore.Release();
if (counter == 0)
{
Debug.Assert(semaphore.CurrentCount == 1);
lock (m_Pool)
{
if (m_Pool.Count < m_PoolCapacity)
m_Pool.Enqueue(semaphore);
}
}
}
#endregion
}
}
#endif

View File

@@ -90,31 +90,6 @@ namespace ICD.Common.Utils
}
}
/// <summary>
/// Attempt to enter the critical section without blocking.
/// </summary>
/// <returns>
/// True, calling thread has ownership of the critical section; otherwise, false.
/// </returns>
public bool TryEnter()
{
if (m_CriticalSection == null)
return false;
try
{
#if DEBUG
return m_CriticalSection.WaitForMutex(0);
#else
return m_CriticalSection.TryEnter();
#endif
}
catch (ObjectDisposedException)
{
return false;
}
}
#endregion
}
}

View File

@@ -24,17 +24,6 @@ namespace ICD.Common.Utils
Monitor.Exit(this);
}
/// <summary>
/// Attempt to enter the critical section without blocking.
/// </summary>
/// <returns>
/// True, calling thread has ownership of the critical section; otherwise, false.
/// </returns>
public bool TryEnter()
{
return Monitor.TryEnter(this);
}
#endregion
}
}

View File

@@ -0,0 +1,127 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
using System.Threading;
#endif
namespace ICD.Common.Utils
{
/// <summary>
/// Like the CCriticalSection, the CMutex tends to get disposed before the parent is
/// done with it. This class is an attempt to gracefully handle the ObjectDisposedExceptions
/// we see on program termination, ocassionally causing the program to restart instead of stop.
/// </summary>
public sealed class SafeMutex : IDisposable
{
private bool _disposedValue;
#if SIMPLSHARP
private readonly CMutex m_Mutex;
#else
private readonly Mutex m_Mutex;
#endif
/// <summary>
/// Initializes a new instance of the <see cref="SafeMutex"/> class.
/// </summary>
public SafeMutex()
{
#if SIMPLSHARP
m_Mutex = new CMutex();
#else
m_Mutex = new Mutex();
#endif
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeMutex"/> class.
/// </summary>
/// <inheritdoc cref="System.Threading.Mutex.Mutex(bool)"/>
public SafeMutex(bool initiallyOwned)
{
#if SIMPLSHARP
m_Mutex = new CMutex(initiallyOwned);
#else
m_Mutex = new Mutex(initiallyOwned);
#endif
}
#region Methods
/// <summary>
/// Waits the given number of milliseconds to aquire the mutex.
/// </summary>
/// <param name="timeout"></param>
/// <returns>True if the mutex was aquired.</returns>
public bool WaitForMutex(int timeout)
{
try
{
#if SIMPLSHARP
return m_Mutex.WaitForMutex(timeout);
#else
return m_Mutex.WaitOne(timeout);
#endif
}
catch (ObjectDisposedException)
{
return false;
}
}
/// <summary>
/// Releases the mutex.
/// </summary>
public void ReleaseMutex()
{
try
{
m_Mutex.ReleaseMutex();
}
catch (ObjectDisposedException)
{
// Releasing a disposed mutex in this case is valid behaviour
}
}
#endregion
#region IDisposable
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// dispose managed state (managed objects)
try
{
// disposing of a mutex automatically releases it. Match that behavior
m_mutex.ReleaseMutex();
m_Mutex.Dispose();
}
catch (ObjectDisposedException)
{
// Releasing a disposed mutex in this case is valid behaviour
}
}
// free unmanaged resources (unmanaged objects) and override finalizer
// set large fields to null
_disposedValue = true;
}
}
/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
}

View File

@@ -0,0 +1,466 @@
using System;
using ICD.Common.Properties;
using ICD.Common.Utils.Collections;
using ICD.Common.Utils.Services;
using ICD.Common.Utils.Services.Logging;
using ICD.Common.Utils.Timers;
namespace ICD.Common.Utils
{
/// <summary>
/// Utilizes a priority queue to store items
/// Dequeues items in priority order and processes
/// them in a worker thread one at a time
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class ThreadedWorkerQueue<T> : IDisposable
{
/// <summary>
/// Underlying Queue to hold items
/// </summary>
private readonly PriorityQueue<T> m_Queue;
/// <summary>
/// Bool to track if a thread is currently running to dequeue and process items
/// </summary>
private bool m_ProcessRunning;
/// <summary>
/// Critical section to lock the queue and the process running bool
/// </summary>
private readonly SafeCriticalSection m_QueueSection;
/// <summary>
/// Action that should be run for every item as it is dequeued
/// </summary>
private readonly Action<T> m_ProcessAction;
/// <summary>
/// Event used to block and wait for the queue to empty
/// </summary>
private readonly IcdManualResetEvent m_FlushEvent;
/// <summary>
/// If true, the queue will be processed.
/// </summary>
private bool m_RunProcess;
private long m_BetweenTime;
/// <summary>
/// Time to wait between processing items
/// If less than or equal to 0, items will be processed immediately
/// </summary>
[PublicAPI]
public long BetweenTime
{
get { return m_BetweenTime; }
set
{
if (value < 0)
throw new InvalidOperationException("BetweenTime can't be negative");
m_BetweenTime = value;
}
}
/// <summary>
/// Timer used to wait the BetweenTime
/// </summary>
private readonly SafeTimer m_BetweenTimeTimer;
/// <summary>
/// While true, the queue will be processed
/// When set to false, the queue will be be stopped after any current processing is finished
/// When set to true, the queue processing will be started in a new thread if there are any items in the queue
///
/// </summary>
[PublicAPI]
public bool RunProcess { get { return m_RunProcess; } }
/// <summary>
/// Gets the current count of the items in the queue
/// </summary>
public int Count
{
get { return m_QueueSection.Execute(() => m_Queue.Count); }
}
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
/// <param name="processItemAction">Action to process the dequeued items</param>
public ThreadedWorkerQueue([NotNull] Action<T> processItemAction)
: this(processItemAction, true, 0)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="processItemAction">Action to process the dequeued items</param>
/// <param name="runProcess">If true, queued items will be processed, if false, no processing will happen until runProcess is set</param>
public ThreadedWorkerQueue([NotNull] Action<T> processItemAction, bool runProcess)
: this(processItemAction, runProcess, 0)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="processItemAction">Action to process the dequeued items</param>
/// <param name="runProcess">If true, queued items will be processed, if false, no processing will happen until runProcess is set</param>
/// <param name="betweenTime">Time to wait between processing items</param>
public ThreadedWorkerQueue([NotNull] Action<T> processItemAction, bool runProcess, long betweenTime)
{
if (processItemAction == null)
throw new ArgumentNullException("processItemAction");
if (betweenTime < 0)
throw new ArgumentOutOfRangeException("betweenTime", "Between time can't be negative");
m_Queue = new PriorityQueue<T>();
m_QueueSection = new SafeCriticalSection();
m_FlushEvent = new IcdManualResetEvent(true);
m_RunProcess = runProcess;
m_ProcessAction = processItemAction;
m_BetweenTimeTimer = SafeTimer.Stopped(BetweenTimerCallback);
BetweenTime = betweenTime;
}
#endregion
#region Methods
/// <summary>
/// Clears the collection.
/// </summary>
[PublicAPI]
public void Clear()
{
m_QueueSection.Execute(() => m_Queue.Clear());
}
/// <summary>
/// Blocks until the queue is empty
/// </summary>
/// <returns>true if the queue empties</returns>
[PublicAPI]
public bool WaitForFlush()
{
return m_FlushEvent.WaitOne();
}
/// <summary>
/// Blocks until the queue is empty, or the timeout is reached
/// </summary>
/// <param name="timeout">Timeout in ms</param>
/// <returns>true if the queue empties, false if the timeout is reached</returns>
[PublicAPI]
public bool WaitForFlush(int timeout)
{
return m_FlushEvent.WaitOne(timeout);
}
public void SetRunProcess(bool runProcess)
{
m_QueueSection.Enter();
try
{
if (m_RunProcess == runProcess)
return;
m_RunProcess = runProcess;
if (runProcess)
EnableProcessQueue();
}
finally
{
m_QueueSection.Leave();
}
}
public void Dispose()
{
m_QueueSection.Enter();
try
{
SetRunProcess(false);
m_Queue.Clear();
m_BetweenTimeTimer.Stop();
m_BetweenTimeTimer.Dispose();
m_FlushEvent.Dispose();
}
finally
{
m_QueueSection.Leave();
}
}
#endregion
#region Enqueue Methods
/// <summary>
/// Adds the item to the end of the queue.
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void Enqueue([CanBeNull] T item)
{
Enqueue(item, () => m_Queue.Enqueue(item));
}
/// <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([CanBeNull] T item, int priority)
{
Enqueue(item, () => m_Queue.Enqueue(item, priority));
}
/// <summary>
/// Adds the item to the queue with the given priority at the given index.
/// </summary>
/// <param name="item"></param>
/// <param name="priority"></param>
/// <param name="position"></param>
[PublicAPI]
public void Enqueue([CanBeNull] T item, int priority, int position)
{
Enqueue(item, () => m_Queue.Enqueue(item, priority, position));
}
/// <summary>
/// Enqueues the item at the beginning of the queue.
/// </summary>
/// <param name="item"></param>
[PublicAPI]
public void EnqueueFirst([CanBeNull] T item)
{
Enqueue(item, () => m_Queue.EnqueueFirst(item));
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
[PublicAPI]
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove)
{
Enqueue(item, () => m_Queue.EnqueueRemove(item, remove));
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
/// <param name="priority"></param>
[PublicAPI]
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority)
{
Enqueue(item, () => m_Queue.EnqueueRemove(item, remove, priority));
}
/// <summary>
/// Removes any items in the queue matching the predicate.
/// Appends the given item at the end of the given priority level.
/// This is useful for reducing duplication, or replacing items with something more pertinent.
/// </summary>
/// <param name="item"></param>
/// <param name="remove"></param>
/// <param name="priority"></param>
/// <param name="deDuplicateToEndOfQueue"></param>
[PublicAPI]
public void EnqueueRemove([CanBeNull] T item, [NotNull] Func<T, bool> remove, int priority, bool deDuplicateToEndOfQueue)
{
Enqueue(item, () => m_Queue.EnqueueRemove(item, remove, priority, deDuplicateToEndOfQueue));
}
#endregion
#region Private Methods
/// <summary>
/// Enqueues an item using the given enqueueAction
/// Starts processing the queue if it's not already processing
/// </summary>
/// <param name="item"></param>
/// <param name="enqueueAction"></param>
private void Enqueue(T item, Action enqueueAction)
{
bool startWorkerThread;
T nextItem = default(T);
m_QueueSection.Enter();
try
{
// Reset the flush event, to block the flush thread while something is in the queue/being processed
m_FlushEvent.Reset();
// We'll start a worker thread if the process isn't running and should be
startWorkerThread = !m_ProcessRunning && RunProcess;
if (startWorkerThread)
m_ProcessRunning = true;
if (m_Queue.Count == 0 && startWorkerThread)
{
// If the queue is empty and we're starting the worker thread
// we won't bother adding an item just to dequeue it again
nextItem = item;
}
else if (startWorkerThread)
{
// If the queue isn't empty and we are starting the worker thread
// enqueue the item and dequeue the next item.
enqueueAction();
nextItem = m_Queue.Dequeue();
}
else
{
// If we're not starting the worker thread, just enqueue the item
enqueueAction();
}
}
finally
{
m_QueueSection.Leave();
}
if (startWorkerThread)
ProcessQueue(nextItem);
}
/// <summary>
/// Starts processing the queue by starting a thread
/// When running this, m_ProcessRunning MUST be set to true already,
/// and RunProcess should be checked before this
/// </summary>
/// <param name="item"></param>
private void ProcessQueue(T item)
{
ThreadingUtils.SafeInvoke(() => ProcessQueueThread(item));
}
/// <summary>
/// Processes the item, meant to be run in it's own thread
/// When running this, m_ProcessRunning MUST be set to true already,
/// and RunProcess should be checked before this
/// </summary>
/// <param name="item"></param>
private void ProcessQueueThread(T item)
{
while (true)
{
// Run the process action
// todo: Have exceptions raise an event in a new thread, maybe configurable exception handling
try
{
m_ProcessAction(item);
}
catch (Exception e)
{
ServiceProvider.GetService<ILoggerService>().AddEntry(eSeverity.Error, e, "Exception in ThreadedWorkingQueue process action:{0}", e.Message);
}
m_QueueSection.Enter();
try
{
bool runProcess = RunProcess;
bool hasItem = m_Queue.Count > 0;
long betweenTime = BetweenTime;
// Stop processing and return if we don't have another item, or we aren't to keep running the process
if (!runProcess || !hasItem)
{
m_ProcessRunning = false;
if (!hasItem)
m_FlushEvent.Set();
return;
}
// If a between time is set, start the timer (and leave m_ProcessRunning set)
if (betweenTime > 0)
{
m_BetweenTimeTimer.Reset(betweenTime);
return;
}
item = m_Queue.Dequeue();
}
finally
{
m_QueueSection.Leave();
}
}
}
/// <summary>
/// Starts processing an item if we should be
/// Run by RunProcess being set to true
/// </summary>
private void EnableProcessQueue()
{
T item;
m_QueueSection.Enter();
try
{
if (!RunProcess || m_ProcessRunning || !m_Queue.TryDequeue(out item))
return;
m_ProcessRunning = true;
}
finally
{
m_QueueSection.Leave();
}
ProcessQueue(item);
}
private void BetweenTimerCallback()
{
T item;
m_QueueSection.Enter();
try
{
// Check to make sure RunProcess is still true
// Try to dequeue item
if (!RunProcess || !m_Queue.TryDequeue(out item))
{
m_ProcessRunning = false;
return;
}
}
finally
{
m_QueueSection.Leave();
}
// Process the dequeued item
ProcessQueueThread(item);
}
#endregion
}
}

View File

@@ -18,11 +18,7 @@ namespace ICD.Common.Utils.TimeZoneInfo
private const string SQL_LOCAL_DATABASE_FILE = "TimeZones.sqlite";
private const string SQL_CONNECTION_STRING_FORMAT =
#if SIMPLSHARP
"Data Source={0};Version=3;ReadOnly=True";
#else
"Data Source={0}";
#endif
private static readonly Dictionary<string, IcdTimeZoneInfo> s_Cache;
@@ -65,7 +61,11 @@ namespace ICD.Common.Utils.TimeZoneInfo
public static IcdTimeZoneInfo FindSystemTimeZoneById(string timeZoneId)
{
return s_Cache[timeZoneId];
IcdTimeZoneInfo timeZone;
if (s_Cache.TryGetValue(timeZoneId, out timeZone))
return timeZone;
throw new KeyNotFoundException(string.Format("Unable to find timezone with id:{0}", timeZoneId));
}
public static bool TryFindSystemTimeZoneById(string timeZoneId, out IcdTimeZoneInfo output)

View File

@@ -0,0 +1,79 @@
using System;
using ICD.Common.Utils.EventArguments;
namespace ICD.Common.Utils.Types
{
public abstract class AbstractNotifyFlagsChanged<T> where T : struct, IConvertible
{
private T m_Data;
/// <summary>
/// Raised when flags are set on the data
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnFlagsSet;
/// <summary>
/// Raised when flags are unset on the data
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnFlagsUnset;
/// <summary>
/// Raised when the data changes
/// </summary>
public event EventHandler<GenericEventArgs<T>> OnChange;
/// <summary>
/// Data
/// </summary>
public T Data
{
get { return m_Data; }
set
{
if (DataIsEqual(m_Data, value))
return;
int intData = GetIntValue(m_Data);
int intValue = GetIntValue(value);
int setFlags = intValue & ~intData;
int unsetFlags = intData & ~intValue;
m_Data = value;
OnChange.Raise(this, value);
if (setFlags != 0)
OnFlagsSet.Raise(this, GetEnumValue(setFlags));
if (unsetFlags != 0)
OnFlagsUnset.Raise(this, GetEnumValue(unsetFlags));
}
}
/// <summary>
/// Converts the enum to the backing int value
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected abstract int GetIntValue(T value);
/// <summary>
/// Converts the backing int value to enum
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected abstract T GetEnumValue(int value);
/// <summary>
/// Checks enums for equality
/// Override for performance improvements
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
protected virtual bool DataIsEqual(T a, T b)
{
return GetIntValue(a) == GetIntValue(b);
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using ICD.Common.Utils.EventArguments;
namespace ICD.Common.Utils.Types
{
/// <summary>
/// Class to raise events when flags are set and unset on the data
/// </summary>
/// <typeparam name="T"></typeparam>
public sealed class GenericNotifyFlagsChanged<T> : AbstractNotifyFlagsChanged<T> where T : struct, IConvertible
{
/// <summary>
/// Converts the enum to the backing int value
/// Override for performance improvements
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected override int GetIntValue(T value)
{
return (int)(object)value;
}
/// <summary>
/// Converts the backing int value to enum
/// Override for performance improvements
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
protected override T GetEnumValue(int value)
{
return (T)(object)value;
}
}
}

View File

@@ -3,7 +3,6 @@ using System.Text;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronXml;
#else
using System.Xml;
#endif
@@ -147,7 +146,7 @@ namespace ICD.Common.Utils.Xml
#region Private Methods
#if STANDARD
#if !SIMPLSHARP
private static XmlWriterSettings GetSettings(Encoding encoding)
{
XmlWriterSettings output = GetSettings();

21
LICENSE.md Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 ICD Systems
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.