Compare commits

...

121 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
Drew Tingen
d00d2febf3 chore: Update Changelog, increment minor version 2021-08-18 10:45:49 -04:00
Austin Noska
af3d335079 feat: Add TryParse overload method to StringUtils, which tries to parse a GUID from a string 2021-08-17 16:04:52 -04:00
Drew Tingen
375e3154c6 chore: Update Changelog, increment minor version 2021-08-03 13:55:18 -04:00
Chris Cameron
37d799295c fix: Resolving warnings 2021-06-30 11:33:11 -04:00
Chris Cameron
06f40e5d22 fix: Resolving warning 2021-06-30 11:05:31 -04:00
Austin Noska
11c3e33fc1 fix: Use if statement instead of while when checking if SQL read was successful in IcdTimeZoneInfo 2021-06-30 10:09:58 -04:00
Chris Cameron
fc60e2b675 chore: Adding NotNull/CanBeNull attributes to StringUtils 2021-06-28 16:11:23 -04:00
Drew Tingen
531e9dfcf8 feat: Adding Equality Comparers to BiDictionary constructor 2021-06-24 11:48:52 -04:00
Drew Tingen
f601f9cda7 feat: SetFlags enum extension 2021-06-23 14:12:27 -04:00
Chris Cameron
841b692727 feat: Added Flush() method to ILoggerService 2021-06-23 14:11:37 -04:00
Chris Cameron
5f2c5e6dcb fix: Fixed a bug where EnumUtils.GetFlags, and overloads, would also return defined composites 2021-06-15 14:13:37 -04:00
Austin Noska
7ae9e86e1d fix: Fixed an issue where the SequenceComparer & the UndefinedVersionComparer were not handling null values properly 2021-06-07 14:43:53 -04:00
Chris Cameron
e6dec641f3 feat: Added ThreadingUtils overloads 2021-05-24 10:11:49 -04:00
Chris Cameron
8b848e5127 refactor: Tidying, resolving warnings 2021-05-20 17:09:12 -04:00
Chris Cameron
f328598f63 refactor: Tidying, resolving warnings 2021-05-20 14:45:49 -04:00
Chris Cameron
79db70211f fix: Removing "Microsoft" from windows model name 2021-05-20 09:55:05 -04:00
Chris Cameron
1390af967f feat: Logging when we intentionally restart the program or processor 2021-05-17 15:30:53 -04:00
Chris Cameron
28335ad99c chore: Updating changelog, incrementing major version 2021-05-14 10:59:54 -04:00
Chris Cameron
6967e9b013 fix: No longer automatically enabling ANSI color, must be enabled by the calling application 2021-05-14 10:17:01 -04:00
Chris Cameron
7784b99f75 fix: Logic for determining if there is a console output window, don't try to write to console if no console window 2021-05-14 10:17:01 -04:00
Chris Cameron
b3c1daaab5 fix: Fixed a bug where AbstractGenericXmlConverter was not reading out of empty elements 2021-05-04 17:26:01 -04:00
Austin Noska
ae10abd71e feat: Port open source CsvReader for CF 3.5 compatibility 2021-04-29 09:31:24 -04:00
Austin Noska
5aca963da0 feat: Expose wrapped stream for IcdStreamReader 2021-04-28 17:03:59 -04:00
Austin Noska
40330d0007 feat: Add method to IcdFile for getting the length in bytes of a specified file 2021-04-28 16:16:59 -04:00
Chris Cameron
a26783bd67 feat: IcdErrorLog traces to Visual Studio output 2021-04-22 14:26:54 -04:00
Chris Cameron
d58b8a9db9 feat: IcdConsole traces to Visual Studio output 2021-04-21 16:45:12 -04:00
Chris Cameron
5083f3d7ab fix: Fixing trailing whitespace in windows model name 2021-04-15 17:18:55 -04:00
Austin Noska
43fd348fae feat: Add enum extension method for cycling to next enum value 2021-04-13 15:59:37 -04:00
Chris Cameron
8cdddc94bc chore: Updating test projects to netcoreapp3.1 2021-04-13 11:46:10 -04:00
Chris Cameron
25d799fe7d fix: Fixed a bug where SafeTimer.Trigger would call the callback twice on NetStandard 2021-04-12 15:13:38 -04:00
Drew Tingen
f807db480e feat: added OnSystemDeviceAddedRemoved event to IcdEnviornment for NetStandard 2021-04-01 10:03:06 -04:00
Chris Cameron
985a81f961 refactor: Fixing warnings 2021-03-31 17:21:43 -04:00
Chris Cameron
0a9a382355 fix: GetProperty and CallMethod reflection utils use the FlattenHierarchy flag 2021-03-31 16:20:35 -04:00
Chris Cameron
86fabce6da feat: Initial commit of IcdOrderedDictionary 2021-03-30 13:43:59 -04:00
Chris Cameron
a7ab2ab3fe refactor: Renaming OrderedDictionary to SortedDictionary for consistency with .Net 2021-03-29 16:23:29 -04:00
Chris Cameron
370cadbaeb feat: Extension methods for joining enumerables of strings 2021-03-23 17:05:27 -04:00
Chris Cameron
e7bdcdfca5 feat: Adding methods for converting hex strings to byte arrays 2021-03-22 15:30:25 -04:00
Chris Cameron
cab1e237d5 chore: Updating nuget packages 2021-03-22 15:21:49 -04:00
Chris Cameron
da5e1d83a0 refactor: Tidying 2021-03-18 14:56:25 -04:00
Chris Cameron
63237b6fba feat: Added session change event to IcdEnvironment 2021-03-12 14:07:38 -05:00
Chris Cameron
a55480f8e5 fix: Preserve stack trace when rethrowing inner exceptions 2021-03-12 14:03:47 -05:00
Chris Cameron
d1f8096933 feat: Initial commit of RegistryExtensions 2021-03-03 16:45:19 -05:00
Chris Cameron
c41e61391e feat: Implementing ProcessorUtils for NetStandard 2021-03-03 13:50:28 -05:00
Chris Cameron
766e2da4e3 feat: Added property to ProgramUtils to determine if the application is running as admin 2021-03-02 11:44:36 -05:00
Chris Cameron
b83fba337f feat: Implementing RestartProgram and Reboot methods for windows 2021-03-01 09:46:10 -05:00
Austin Noska
766c265dac chore: Update changelog 2021-02-18 16:25:44 -05:00
Austin Noska
204d2a475d feat: Add MatchAny util method to RegexUtils that tries to match an input against a sequence of patterns. 2021-02-18 16:22:16 -05:00
Austin Noska
b5542fb0d2 feat: Add daylight time row entries & a new display name column to TimeZones.sqlite - Adjusted IcdTimeZoneInfo accordingly 2021-02-18 16:20:41 -05:00
Austin Noska
4ff4e9f3c6 feat: Add method to IcdEnvironment to get the name of the local time zone 2021-02-18 16:18:38 -05:00
Chris Cameron
456a8f74f9 feat: Adding shim for setting file attributes recursively 2021-02-11 11:43:18 -05:00
Chris Cameron
ffb00f960d feat: NetStandard krang plugins are archived to .kpz files 2021-02-10 14:21:31 -05:00
Chris Cameron
b44cbe0093 chore: Updating dependencies 2021-02-08 11:43:19 -05:00
Chris Cameron
cc79e88702 fix: Don't throw an ObjectDisposed exception when attempting to stop a disposed timer 2021-02-05 16:15:11 -05:00
Chris Cameron
1db7d4b45d Merge remote-tracking branch 'origin/ConnectPro_v1.8' into ConnectPro_v1.9
# Conflicts:
#	CHANGELOG.md
2021-02-04 11:34:06 -05:00
Chris Cameron
8427f2c0c6 Merge remote-tracking branch 'origin/ConnectPro_v1.8' into ConnectPro_v1.9 2021-01-25 12:21:07 -05:00
Chris Cameron
8411a27173 fix: Fixing 2008 build 2021-01-25 12:04:08 -05:00
Chris Cameron
e0287cfc7b Merge remote-tracking branch 'origin/ConnectPro_v1.8' into ConnectPro_v1.9 2021-01-21 10:42:51 -05:00
Chris Cameron
e044c97af7 refactor: IcdUriBuilder.AppendPath returns itself for chaining 2021-01-18 15:22:46 -05:00
Chris Cameron
c7e8f09eeb feat: Added GetParentUri method to UriExtensions 2021-01-18 15:22:28 -05:00
Chris Cameron
9b53d77d6b feat: Added GetOrAddService method for lazy-loading services 2021-01-15 11:35:04 -05:00
115 changed files with 5868 additions and 1258 deletions

View File

@@ -6,6 +6,109 @@ 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
## [15.1.0] - 2021-08-03
### Added
- Enum Extension "SetFlags", takes a bool to set or unset the given flags
- BiDictionay - Added constructors with TKey and TValue comparers
- ILoggerService - Added Flush() method
- Added log entries for intentional reboot and program restart
- Added ThreadingUtils TimeSpan overloads
### Changed
- IcdTimeZoneInfo - fixed issue when unable to read SQL
- WeakKeyDictionary - Fixe GetHashCode to handle null values
- ProcessorUtils - NetStandard - removed "Microsoft" from model name
- Fixed null handing in SequenceComparer and UndefinedVersionComparer
- EnumUtils - GetFlags no longer returns composite flags
## [15.0.0] - 2021-05-14
### Added
- Ported CsvReader for CF 3.5 compatibility from: https://github.com/tspence/csharp-csv-reader
- Added enum extension method for cycling to the next enum value
- Added GetLocalTimeZoneName method to IcdEnvironment
- Added MatchAny method to RegexUtils
- Added OnSystemDeviceAddedRemoved and associated raise methods to IcdEnvironment for NETSTANDARD
- Added GetParentUri method to UriExtensions
- Added RegistryExtensions for working with Windows registry
- Added session change event to IcdEnvironment for login/logout feedback
- Added OrderedDictionary collection
### Changed
- Updated TimeZones.sqlite to include daylight time zone info, added a new display name column.
- Implemented ProcessorUtils for Windows
- Renamed OrderedDictionary to SortedDictionary for consistency with .Net
- Fixed a bug where SafeTimer.Trigger() would run the callback twice on .Net Standard
- Fixed a bug where XML deserialization would fail to read out of empty elements
### Removed
- ANSI color is no longer enabled on .Net Standard by default - it must be enabled by the calling application
## [14.2.0] - 2021-02-04
### Changed
- ProcessorUtils Uptime methods changed to StartTime

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,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Utils.Collections;
using NUnit.Framework;
@@ -7,130 +8,85 @@ namespace ICD.Common.Utils.Tests.Collections
[TestFixture]
public sealed class IcdOrderedDictionaryTest
{
#region Properties
[Test]
public void CountTest()
public void OrderingTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
IcdOrderedDictionary<string, string> dict = new IcdOrderedDictionary<string, string>
{
{0, 0},
{1, 10},
{2, 20}
{"1", "a"},
{"2", "b"},
{"3", "c"}
};
Assert.AreEqual(3, dict.Count);
Assert.IsTrue(dict.Keys.SequenceEqual(new[] {"1", "2", "3"}));
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "a", "b", "c" }));
// Remove the key and add to the end of the dictionary
dict.Remove("2");
dict["2"] = "d";
Assert.IsTrue(dict.Keys.SequenceEqual(new[] { "1", "3", "2" }));
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "a", "c", "d" }));
// No key change
dict["1"] = "e";
Assert.IsTrue(dict.Keys.SequenceEqual(new[] { "1", "3", "2" }));
Assert.IsTrue(dict.Values.SequenceEqual(new[] { "e", "c", "d" }));
}
[Test]
public void IsReadOnlyTest()
public void GetTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
Assert.IsFalse(dict.IsReadOnly);
}
[Test]
public void KeysTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
IcdOrderedDictionary<string, string> dict = new IcdOrderedDictionary<string, string>
{
{0, 0},
{1, 10},
{-1, -10}
{"1", "a"},
{"2", "b"},
{"3", "c"}
};
int[] keys = dict.Keys.ToArray();
KeyValuePair<string, string> kvp = dict.Get(0);
Assert.AreEqual("1", kvp.Key);
Assert.AreEqual("a", kvp.Value);
Assert.AreEqual(3, keys.Length);
Assert.AreEqual(-1, keys[0]);
Assert.AreEqual(0, keys[1]);
Assert.AreEqual(1, keys[2]);
kvp = dict.Get(1);
Assert.AreEqual("2", kvp.Key);
Assert.AreEqual("b", kvp.Value);
kvp = dict.Get(2);
Assert.AreEqual("3", kvp.Key);
Assert.AreEqual("c", kvp.Value);
// Remove the key and add to the end of the dictionary
dict.Remove("2");
dict["2"] = "d";
kvp = dict.Get(0);
Assert.AreEqual("1", kvp.Key);
Assert.AreEqual("a", kvp.Value);
kvp = dict.Get(1);
Assert.AreEqual("3", kvp.Key);
Assert.AreEqual("c", kvp.Value);
kvp = dict.Get(2);
Assert.AreEqual("2", kvp.Key);
Assert.AreEqual("d", kvp.Value);
// No key change
dict["1"] = "e";
kvp = dict.Get(0);
Assert.AreEqual("1", kvp.Key);
Assert.AreEqual("e", kvp.Value);
kvp = dict.Get(1);
Assert.AreEqual("3", kvp.Key);
Assert.AreEqual("c", kvp.Value);
kvp = dict.Get(2);
Assert.AreEqual("2", kvp.Key);
Assert.AreEqual("d", kvp.Value);
}
[Test]
public void ValuesTest()
{
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>
{
{0, 0},
{1, 10},
{-1, -10}
};
int[] values = dict.Values.ToArray();
Assert.AreEqual(3, values.Length);
Assert.AreEqual(-10, values[0]);
Assert.AreEqual(0, values[1]);
Assert.AreEqual(10, values[2]);
}
[Test]
public void IndexerTest()
{
// ReSharper disable UseObjectOrCollectionInitializer
IcdOrderedDictionary<int, int> dict = new IcdOrderedDictionary<int, int>();
// ReSharper restore UseObjectOrCollectionInitializer
dict[0] = 0;
dict[1] = 10;
dict[-1] = -10;
dict[-1] = -11;
Assert.AreEqual(0, dict[0]);
Assert.AreEqual(10, dict[1]);
Assert.AreEqual(-11, dict[-1]);
Assert.AreEqual(3, dict.Count);
int[] expectedKeys = {-1, 0, 1 };
int[] expectedValues = {-11, 0, 10};
Assert.AreEqual(expectedKeys, dict.Keys.ToArray());
Assert.AreEqual(expectedValues, dict.Values.ToArray());
}
#endregion
#region Methods
[Test]
public void GetEnumeratorTest()
{
Assert.Inconclusive();
}
[Test]
public void AddTest()
{
Assert.Inconclusive();
}
[Test]
public void ClearTest()
{
Assert.Inconclusive();
}
[Test]
public void ContainsKeyTest()
{
Assert.Inconclusive();
}
[Test]
public void RemoveTest()
{
Assert.Inconclusive();
}
[Test]
public void TryGetValueTest()
{
Assert.Inconclusive();
}
#endregion
}
}

View File

@@ -0,0 +1,136 @@
using System.Linq;
using ICD.Common.Utils.Collections;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Collections
{
[TestFixture]
public sealed class IcdSortedDictionaryTest
{
#region Properties
[Test]
public void CountTest()
{
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
{
{0, 0},
{1, 10},
{2, 20}
};
Assert.AreEqual(3, dict.Count);
}
[Test]
public void IsReadOnlyTest()
{
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>();
Assert.IsFalse(dict.IsReadOnly);
}
[Test]
public void KeysTest()
{
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
{
{0, 0},
{1, 10},
{-1, -10}
};
int[] keys = dict.Keys.ToArray();
Assert.AreEqual(3, keys.Length);
Assert.AreEqual(-1, keys[0]);
Assert.AreEqual(0, keys[1]);
Assert.AreEqual(1, keys[2]);
}
[Test]
public void ValuesTest()
{
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>
{
{0, 0},
{1, 10},
{-1, -10}
};
int[] values = dict.Values.ToArray();
Assert.AreEqual(3, values.Length);
Assert.AreEqual(-10, values[0]);
Assert.AreEqual(0, values[1]);
Assert.AreEqual(10, values[2]);
}
[Test]
public void IndexerTest()
{
// ReSharper disable UseObjectOrCollectionInitializer
IcdSortedDictionary<int, int> dict = new IcdSortedDictionary<int, int>();
// ReSharper restore UseObjectOrCollectionInitializer
dict[0] = 0;
dict[1] = 10;
dict[-1] = -10;
dict[-1] = -11;
Assert.AreEqual(0, dict[0]);
Assert.AreEqual(10, dict[1]);
Assert.AreEqual(-11, dict[-1]);
Assert.AreEqual(3, dict.Count);
int[] expectedKeys = {-1, 0, 1 };
int[] expectedValues = {-11, 0, 10};
Assert.AreEqual(expectedKeys, dict.Keys.ToArray());
Assert.AreEqual(expectedValues, dict.Values.ToArray());
}
#endregion
#region Methods
[Test]
public void GetEnumeratorTest()
{
Assert.Inconclusive();
}
[Test]
public void AddTest()
{
Assert.Inconclusive();
}
[Test]
public void ClearTest()
{
Assert.Inconclusive();
}
[Test]
public void ContainsKeyTest()
{
Assert.Inconclusive();
}
[Test]
public void RemoveTest()
{
Assert.Inconclusive();
}
[Test]
public void TryGetValueTest()
{
Assert.Inconclusive();
}
#endregion
}
}

View File

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

@@ -32,5 +32,17 @@ namespace ICD.Common.Utils.Tests.Extensions
{
Assert.AreEqual(expected, value.HasFlags(flags));
}
[TestCase(eTestEnum.A, eTestEnum.B)]
[TestCase(eTestEnum.B, eTestEnum.C)]
[TestCase(eTestEnum.C, eTestEnum.A)]
[TestCase(eTestEnum.A | eTestEnum.B, null)]
public void CycleNextTest(eTestEnum value, eTestEnum? expected)
{
if (EnumUtils.HasMultipleFlags(value))
Assert.Catch(typeof(InvalidOperationException), () => value.CycleNext());
else
Assert.AreEqual(expected, value.CycleNext());
}
}
}

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

@@ -18,5 +18,13 @@ namespace ICD.Common.Utils.Tests.Extensions
{
Assert.AreEqual(expected, new Uri(uriString).GetPassword());
}
[TestCase("http://www.test.com/a/b/c", "http://www.test.com/a/b/")]
[TestCase("http://www.test.com/a/b/", "http://www.test.com/a/")]
[TestCase("http://www.test.com/", "http://www.test.com/")]
public void GetParentUri(string uriString, string expected)
{
Assert.AreEqual(expected, new Uri(uriString).GetParentUri().ToString());
}
}
}

View File

@@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<TargetFrameworks>netcoreapp3.1;net472</TargetFrameworks>
<RootNamespace>ICD.Common.Utils.Tests</RootNamespace>
<AssemblyName>ICD.Common.Utils.Tests</AssemblyName>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -11,24 +11,25 @@
</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 Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
<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" />
</ItemGroup>
<ItemGroup>

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

@@ -1,4 +1,5 @@
using ICD.Common.Properties;
using System.Linq;
using ICD.Common.Properties;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests
@@ -24,6 +25,21 @@ namespace ICD.Common.Utils.Tests
Assert.AreEqual("\x08\x22\x00\x00\x00\x02", output);
}
[TestCase("FF", new byte[] {0xFF})]
[TestCase("01FF", new byte[] { 0x01, 0xFF })]
public void HexToBytes(string value, byte[] expected)
{
byte[] bytes = StringUtils.HexToBytes(value);
Assert.IsTrue(bytes.SequenceEqual(expected));
}
[TestCase("1", 1)]
[TestCase("FF", 0xFF)]
public void HexToByte(string value, byte expected)
{
Assert.AreEqual(expected, StringUtils.HexToByte(value));
}
[TestCase("Test", "Test")]
[TestCase("test", "Test")]
[TestCase("TodayILiveInTheUSAWithSimon", "Today I Live In The USA With Simon")]

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

@@ -49,12 +49,14 @@ namespace ICD.Common.Utils.Tests.Timers
[Test]
public void TriggerTest()
{
bool called = false;
SafeTimer timer = SafeTimer.Stopped(() => called = true);
int called = 0;
SafeTimer timer = SafeTimer.Stopped(() => called++);
timer.Trigger();
Assert.IsTrue(called);
ThreadingUtils.Sleep(50);
Assert.AreEqual(1, called);
timer.Dispose();
}

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

@@ -0,0 +1,155 @@
using System.Xml;
using ICD.Common.Utils.Xml;
using NUnit.Framework;
namespace ICD.Common.Utils.Tests.Xml
{
[TestFixture]
public sealed class GenericXmlConverterTest
{
[XmlConverter(typeof(TestClassConverter))]
private sealed class TestClass
{
public string A { get; set; }
public int B { get; set; }
}
private sealed class TestClassConverter : AbstractGenericXmlConverter<TestClass>
{
private const string ELEMENT_A = "A";
private const string ATTRIBUTE_B = "b";
/// <summary>
/// Override to handle the current attribute.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected override void ReadAttribute(IcdXmlReader reader, TestClass instance)
{
switch (reader.Name)
{
case ATTRIBUTE_B:
instance.B = int.Parse(reader.Value);
break;
default:
base.ReadAttribute(reader, instance);
break;
}
}
/// <summary>
/// Override to handle the current element.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected override void ReadElement(IcdXmlReader reader, TestClass instance)
{
switch (reader.Name)
{
case ELEMENT_A:
instance.A = reader.ReadElementContentAsString();
break;
default:
base.ReadElement(reader, instance);
break;
}
}
/// <summary>
/// Override to write attributes to the root element.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected override void WriteAttributes(IcdXmlTextWriter writer, TestClass value)
{
base.WriteAttributes(writer, value);
writer.WriteAttributeString(ATTRIBUTE_B, value.B.ToString());
}
/// <summary>
/// Override to write elements to the writer.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected override void WriteElements(IcdXmlTextWriter writer, TestClass value)
{
base.WriteElements(writer, value);
writer.WriteElementString(ELEMENT_A, value.A);
}
}
[Test]
public void TestElement()
{
const string xml = @"<Instances>
<Instance b=""1"">
<A>Test</A>
</Instance>
</Instances>";
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
// Read into the Instances node
reader.Read();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
// Read into the Instance node
reader.Read();
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instance", reader.Name);
// Deserialize
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
Assert.IsNotNull(instance);
Assert.AreEqual("Test", instance.A);
Assert.AreEqual(1, instance.B);
// Deserialization should land on the following node
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
}
}
[Test]
public void TestEmptyElement()
{
const string xml = @"<Instances>
<Instance b=""1"" />
</Instances>";
using (IcdXmlReader reader = new IcdXmlReader(xml))
{
// Read into the Instances node
reader.Read();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
// Read into the Instance node
reader.Read();
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.Element, reader.NodeType);
Assert.AreEqual("Instance", reader.Name);
// Deserialize
TestClass instance = IcdXmlConvert.DeserializeObject<TestClass>(reader);
Assert.IsNotNull(instance);
Assert.IsNull(instance.A);
Assert.AreEqual(1, instance.B);
// Deserialization should land on the following node
reader.SkipInsignificantWhitespace();
Assert.AreEqual(XmlNodeType.EndElement, reader.NodeType);
Assert.AreEqual("Instances", reader.Name);
}
}
}
}

View File

@@ -70,15 +70,32 @@ namespace ICD.Common.Utils
/// </summary>
public static IDictionary<string, string> PuttyColors { get { return s_PuttyColors; } }
#if NETSTANDARD
/// <summary>
/// Constructor.
/// Enables ANSI color in the console.
/// </summary>
static AnsiUtils()
public static void EnableAnsiColor()
{
#if !SIMPLSHARP
// Enables ANSI color output in windows/linux console
Pastel.ConsoleExtensions.Enable();
}
/// <summary>
/// Disables ANSI color in the console.
/// </summary>
public static void DisableAnsiColor()
{
Pastel.ConsoleExtensions.Disable();
}
#endif
/// <summary>
/// Removes ANSI control sequences from the string.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static string StripAnsi(string data)
{
return Regex.Replace(data, ANSI_REGEX, string.Empty);
}
/// <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

@@ -38,10 +38,36 @@ namespace ICD.Common.Utils.Collections
/// <summary>
/// Constructor.
/// </summary>
public BiDictionary()
public BiDictionary() :
this(EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
m_KeyToValue = new Dictionary<TKey, TValue>();
m_ValueToKey = new Dictionary<TValue, TKey>();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public BiDictionary(IEqualityComparer<TKey> keyComparer, IEqualityComparer<TValue> valueComparer)
{
m_KeyToValue = new Dictionary<TKey, TValue>(keyComparer);
m_ValueToKey = new Dictionary<TValue, TKey>(valueComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dict"></param>
/// <param name="keyComparer"></param>
/// <param name="valueComparer"></param>
public BiDictionary([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>
@@ -49,13 +75,8 @@ namespace ICD.Common.Utils.Collections
/// </summary>
/// <param name="dict"></param>
public BiDictionary([NotNull] Dictionary<TKey, TValue> dict)
: this()
: this(dict, EqualityComparer<TKey>.Default, EqualityComparer<TValue>.Default)
{
if (dict == null)
throw new ArgumentNullException("dict");
foreach (KeyValuePair<TKey, TValue> kvp in dict)
Add(kvp.Key, kvp.Value);
}
#region Methods

View File

@@ -1,213 +1,651 @@
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 sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
public sealed class IcdOrderedDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary
{
private readonly List<TKey> m_OrderedKeys;
private readonly List<TValue> m_ValuesOrderedByKey;
private readonly Dictionary<TKey, TValue> m_Dictionary;
private readonly IComparer<TKey> m_Comparer;
private readonly IcdOrderedDictionary m_PrivateDictionary;
#region Properties
public int Count { get { return m_PrivateDictionary.Count; } }
public int Count { get { return m_Dictionary.Count; } }
int ICollection.Count { get { return m_PrivateDictionary.Count; } }
public bool IsReadOnly { get { return false; } }
[NotNull]
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
[NotNull]
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
[CanBeNull]
public TValue this[[NotNull] TKey key]
public TValue this[TKey key]
{
get { return m_Dictionary[key]; }
set
get
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
Remove(key);
Add(key, value);
if (m_PrivateDictionary.Contains(key))
return (TValue)m_PrivateDictionary[key];
throw new KeyNotFoundException();
}
set
{
if (key == null)
throw new ArgumentNullException("key");
m_PrivateDictionary[key] = value;
}
}
#endregion
object IDictionary.this[object key]
{
get { return m_PrivateDictionary[key]; }
set { m_PrivateDictionary[key] = value; }
}
public ICollection<TKey> Keys
{
get
{
List<TKey> keys = new List<TKey>(m_PrivateDictionary.Count);
foreach (TKey key in m_PrivateDictionary.Keys)
{
keys.Add(key);
}
// Keys should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return keys;
}
}
ICollection IDictionary.Keys { get { return m_PrivateDictionary.Keys; } }
public ICollection<TValue> Values
{
get
{
List<TValue> values = new List<TValue>(m_PrivateDictionary.Count);
foreach (TValue value in m_PrivateDictionary.Values)
values.Add(value);
// Values should be put in a ReadOnlyCollection,
// but since this is an internal class, for performance reasons,
// we choose to avoid creating yet another collection.
return values;
}
}
ICollection IDictionary.Values { get { return m_PrivateDictionary.Values; } }
bool IDictionary.IsFixedSize { get { return ((IDictionary)m_PrivateDictionary).IsFixedSize; } }
bool IDictionary.IsReadOnly { get { return m_PrivateDictionary.IsReadOnly; } }
bool ICollection.IsSynchronized { get { return ((ICollection)m_PrivateDictionary).IsSynchronized; } }
object ICollection.SyncRoot { get { return m_PrivateDictionary; } }
/// <summary>
/// Constructor.
/// </summary>
public IcdOrderedDictionary()
: this(Comparer<TKey>.Default)
{
m_PrivateDictionary = new IcdOrderedDictionary();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public IcdOrderedDictionary([NotNull] IComparer<TKey> comparer)
: this(comparer, EqualityComparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
/// <param name="equalityComparer"></param>
public IcdOrderedDictionary([NotNull] IComparer<TKey> comparer, [NotNull] IEqualityComparer<TKey> equalityComparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
m_Comparer = comparer;
m_OrderedKeys = new List<TKey>();
m_ValuesOrderedByKey = new List<TValue>();
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dictionary"></param>
public IcdOrderedDictionary([NotNull] IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
: this()
public IcdOrderedDictionary(IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
return;
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
Add(kvp.Key, kvp.Value);
m_PrivateDictionary = new IcdOrderedDictionary();
foreach (KeyValuePair<TKey, TValue> pair in dictionary)
{
m_PrivateDictionary.Add(pair.Key, pair.Value);
}
}
#region Methods
[NotNull]
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
public void Add(KeyValuePair<TKey, TValue> item)
{
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
.GetEnumerator();
Add(item.Key, item.Value);
}
public void Add([NotNull] TKey key, [CanBeNull] TValue value)
public void Add(TKey key, TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (m_Dictionary.ContainsKey(key))
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
m_PrivateDictionary.Add(key, value);
}
int index = m_OrderedKeys.InsertSorted(key, m_Comparer);
m_ValuesOrderedByKey.Insert(index, value);
m_Dictionary[key] = value;
public KeyValuePair<TKey, TValue> Get(int index)
{
DictionaryEntry entry = (DictionaryEntry)m_PrivateDictionary.ObjectsArray[index];
return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
public void Clear()
{
m_OrderedKeys.Clear();
m_ValuesOrderedByKey.Clear();
m_Dictionary.Clear();
m_PrivateDictionary.Clear();
}
public bool ContainsKey([NotNull] TKey key)
public bool Contains(KeyValuePair<TKey, TValue> item)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_Dictionary.ContainsKey(key);
}
public bool Remove([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (!m_Dictionary.Remove(key))
if (item.Key == null || !m_PrivateDictionary.Contains(item.Key))
return false;
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
m_OrderedKeys.RemoveAt(index);
m_ValuesOrderedByKey.RemoveAt(index);
return true;
return m_PrivateDictionary[item.Key].Equals(item.Value);
}
public bool TryGetValue([NotNull] TKey key, out TValue value)
public bool ContainsKey(TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_Dictionary.TryGetValue(key, out value);
return m_PrivateDictionary.Contains(key);
}
#endregion
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (array == null)
{
throw new ArgumentNullException("array");
}
#region Private Methods
if (arrayIndex < 0)
{
throw new ArgumentOutOfRangeException("arrayIndex");
}
if (array.Rank > 1 || arrayIndex >= array.Length || array.Length - arrayIndex < m_PrivateDictionary.Count)
{
throw new ArgumentException("array");
}
int index = arrayIndex;
foreach (DictionaryEntry entry in m_PrivateDictionary)
{
array[index] = new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
index++;
}
}
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
foreach (DictionaryEntry entry in m_PrivateDictionary)
yield return new KeyValuePair<TKey, TValue>((TKey)entry.Key, (TValue)entry.Value);
}
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
public bool Remove(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
TValue value;
return TryGetValue(item.Key, out value) &&
EqualityComparer<TValue>.Default.Equals(value, item.Value);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
foreach (KeyValuePair<TKey, TValue> kvp in this)
if (Contains(item))
{
array.SetValue(kvp, index);
index++;
m_PrivateDictionary.Remove(item.Key);
return true;
}
return false;
}
public bool Remove(TKey key)
{
if (key == null)
throw new ArgumentNullException("key");
if (m_PrivateDictionary.Contains(key))
{
m_PrivateDictionary.Remove(key);
return true;
}
return false;
}
public bool TryGetValue(TKey key, out TValue value)
{
if (key == null)
throw new ArgumentNullException("key");
bool keyExists = m_PrivateDictionary.Contains(key);
value = keyExists ? (TValue)m_PrivateDictionary[key] : default(TValue);
return keyExists;
}
void IDictionary.Add(object key, object value)
{
m_PrivateDictionary.Add(key, value);
}
void IDictionary.Clear()
{
m_PrivateDictionary.Clear();
}
bool IDictionary.Contains(object key)
{
return m_PrivateDictionary.Contains(key);
}
IDictionaryEnumerator IDictionary.GetEnumerator()
{
return m_PrivateDictionary.GetEnumerator();
}
void IDictionary.Remove(object key)
{
m_PrivateDictionary.Remove(key);
}
void ICollection.CopyTo(Array array, int index)
{
m_PrivateDictionary.CopyTo(array, index);
}
}
internal sealed class IcdOrderedDictionary : IDictionary
{
private ArrayList m_ObjectsArray;
private Hashtable m_ObjectsTable;
private readonly int m_InitialCapacity;
private readonly IEqualityComparer m_Comparer;
private readonly bool m_ReadOnly;
/// <summary>
/// Gets the size of the table.
/// </summary>
public int Count { get { return ObjectsArray.Count; } }
/// <summary>
/// Indicates that the collection can grow.
/// </summary>
bool IDictionary.IsFixedSize { get { return m_ReadOnly; } }
/// <summary>
/// Indicates that the collection is not read-only
/// </summary>
public bool IsReadOnly { get { return m_ReadOnly; } }
/// <summary>
/// Indicates that this class is not synchronized
/// </summary>
bool ICollection.IsSynchronized { get { return false; } }
/// <summary>
/// Gets the collection of keys in the table in order.
/// </summary>
public ICollection Keys { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, true); } }
/// <summary>
/// Returns an arrayList of the values in the table
/// </summary>
public ICollection Values { get { return new OrderedDictionaryKeyValueCollection(ObjectsArray, false); } }
public ArrayList ObjectsArray
{
get { return m_ObjectsArray ?? (m_ObjectsArray = new ArrayList(m_InitialCapacity)); }
}
private Hashtable ObjectsTable
{
get { return m_ObjectsTable ?? (m_ObjectsTable = new Hashtable(m_InitialCapacity, m_Comparer)); }
}
/// <summary>
/// The SyncRoot object. Not used because IsSynchronized is false
/// </summary>
object ICollection.SyncRoot { get { return this; } }
/// <summary>
/// Gets or sets the object at the specified index
/// </summary>
public object this[int index]
{
get { return ((DictionaryEntry)ObjectsArray[index]).Value; }
set
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (index < 0 || index >= ObjectsArray.Count)
{
throw new ArgumentOutOfRangeException("index");
}
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
ObjectsArray[index] = new DictionaryEntry(key, value);
ObjectsTable[key] = value;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
/// <summary>
/// Gets or sets the object with the specified key
/// </summary>
public object this[object key]
{
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
get { return ObjectsTable[key]; }
set
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (ObjectsTable.Contains(key))
{
ObjectsTable[key] = value;
ObjectsArray[IndexOfKey(key)] = new DictionaryEntry(key, value);
}
else
{
Add(key, value);
}
}
}
public IcdOrderedDictionary() : this(0)
{
}
public IcdOrderedDictionary(int capacity) : this(capacity, null)
{
}
public IcdOrderedDictionary(int capacity, IEqualityComparer comparer)
{
m_InitialCapacity = capacity;
m_Comparer = comparer;
}
private IcdOrderedDictionary(IcdOrderedDictionary dictionary)
{
if (dictionary == null)
{
throw new ArgumentNullException("dictionary");
}
m_ReadOnly = true;
m_ObjectsArray = dictionary.m_ObjectsArray;
m_ObjectsTable = dictionary.m_ObjectsTable;
m_Comparer = dictionary.m_Comparer;
m_InitialCapacity = dictionary.m_InitialCapacity;
}
/// <summary>
/// Adds a new entry to the table with the lowest-available index.
/// </summary>
public void Add(object key, object value)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
ObjectsTable.Add(key, value);
ObjectsArray.Add(new DictionaryEntry(key, value));
}
/// <summary>
/// Clears all elements in the table.
/// </summary>
public void Clear()
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
ObjectsTable.Clear();
ObjectsArray.Clear();
}
/// <summary>
/// Returns a readonly IcdOrderedDictionary for the given IcdOrderedDictionary.
/// </summary>
public IcdOrderedDictionary AsReadOnly()
{
return new IcdOrderedDictionary(this);
}
/// <summary>
/// Returns true if the key exists in the table, false otherwise.
/// </summary>
public bool Contains(object key)
{
return ObjectsTable.Contains(key);
}
/// <summary>
/// Copies the table to an array. This will not preserve order.
/// </summary>
public void CopyTo(Array array, int index)
{
ObjectsTable.CopyTo(array, index);
}
private int IndexOfKey(object key)
{
for (int i = 0; i < ObjectsArray.Count; i++)
{
object o = ((DictionaryEntry)ObjectsArray[i]).Key;
if (m_Comparer != null)
{
if (m_Comparer.Equals(o, key))
{
return i;
}
}
else
{
if (o.Equals(key))
{
return i;
}
}
}
return -1;
}
/// <summary>
/// Inserts a new object at the given index with the given key.
/// </summary>
public void Insert(int index, object key, object value)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (index > Count || index < 0)
{
throw new ArgumentOutOfRangeException("index");
}
ObjectsTable.Add(key, value);
ObjectsArray.Insert(index, new DictionaryEntry(key, value));
}
/// <summary>
/// Removes the entry at the given index.
/// </summary>
public void RemoveAt(int index)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (index >= Count || index < 0)
{
throw new ArgumentOutOfRangeException("index");
}
object key = ((DictionaryEntry)ObjectsArray[index]).Key;
ObjectsArray.RemoveAt(index);
ObjectsTable.Remove(key);
}
/// <summary>
/// Removes the entry with the given key.
/// </summary>
public void Remove(object key)
{
if (m_ReadOnly)
{
throw new NotSupportedException();
}
if (key == null)
{
throw new ArgumentNullException("key");
}
int index = IndexOfKey(key);
if (index < 0)
{
return;
}
ObjectsTable.Remove(key);
ObjectsArray.RemoveAt(index);
}
#region IDictionary implementation
/// <internalonly/>
public IDictionaryEnumerator GetEnumerator()
{
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
}
#endregion
#region IEnumerable implementation
/// <internalonly/>
IEnumerator IEnumerable.GetEnumerator()
{
return new OrderedDictionaryEnumerator(ObjectsArray, OrderedDictionaryEnumerator.DICTIONARY_ENTRY);
}
#endregion
/// <summary>
/// OrderedDictionaryEnumerator works just like any other IDictionaryEnumerator, but it retrieves DictionaryEntries
/// in the order by index.
/// </summary>
private sealed class OrderedDictionaryEnumerator : IDictionaryEnumerator
{
internal const int KEYS = 1;
internal const int VALUES = 2;
internal const int DICTIONARY_ENTRY = 3;
private readonly int m_ObjectReturnType;
private readonly IEnumerator m_ArrayEnumerator;
internal OrderedDictionaryEnumerator(ArrayList array, int objectReturnType)
{
m_ArrayEnumerator = array.GetEnumerator();
m_ObjectReturnType = objectReturnType;
}
/// <summary>
/// Retrieves the current DictionaryEntry. This is the same as Entry, but not strongly-typed.
/// </summary>
public object Current
{
get
{
if (m_ObjectReturnType == KEYS)
{
return ((DictionaryEntry)m_ArrayEnumerator.Current).Key;
}
if (m_ObjectReturnType == VALUES)
{
return ((DictionaryEntry)m_ArrayEnumerator.Current).Value;
}
return Entry;
}
}
/// <summary>
/// Retrieves the current DictionaryEntry
/// </summary>
public DictionaryEntry Entry
{
get
{
return new DictionaryEntry(((DictionaryEntry)m_ArrayEnumerator.Current).Key,
((DictionaryEntry)m_ArrayEnumerator.Current).Value);
}
}
/// <summary>
/// Retrieves the key of the current DictionaryEntry
/// </summary>
public object Key { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Key; } }
/// <summary>
/// Retrieves the value of the current DictionaryEntry
/// </summary>
public object Value { get { return ((DictionaryEntry)m_ArrayEnumerator.Current).Value; } }
/// <summary>
/// Moves the enumerator pointer to the next member
/// </summary>
public bool MoveNext()
{
return m_ArrayEnumerator.MoveNext();
}
/// <summary>
/// Resets the enumerator pointer to the beginning.
/// </summary>
public void Reset()
{
m_ArrayEnumerator.Reset();
}
}
/// <summary>
/// OrderedDictionaryKeyValueCollection implements a collection for the Values and Keys properties
/// that is "live"- it will reflect changes to the IcdOrderedDictionary on the collection made after the getter
/// was called.
/// </summary>
private sealed class OrderedDictionaryKeyValueCollection : ICollection
{
private readonly ArrayList m_Objects;
private readonly bool m_IsKeys;
public OrderedDictionaryKeyValueCollection(ArrayList array, bool isKeys)
{
m_Objects = array;
m_IsKeys = isKeys;
}
void ICollection.CopyTo(Array array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
if (index < 0)
throw new ArgumentOutOfRangeException("index");
foreach (object o in m_Objects)
{
array.SetValue(m_IsKeys ? ((DictionaryEntry)o).Key : ((DictionaryEntry)o).Value, index);
index++;
}
}
int ICollection.Count { get { return m_Objects.Count; } }
bool ICollection.IsSynchronized { get { return false; } }
object ICollection.SyncRoot { get { return m_Objects.SyncRoot; } }
IEnumerator IEnumerable.GetEnumerator()
{
return new OrderedDictionaryEnumerator(m_Objects,
m_IsKeys
? OrderedDictionaryEnumerator.KEYS
: OrderedDictionaryEnumerator.VALUES);
}
}
}
}

View File

@@ -0,0 +1,213 @@
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 sealed class IcdSortedDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
private readonly List<TKey> m_OrderedKeys;
private readonly List<TValue> m_ValuesOrderedByKey;
private readonly Dictionary<TKey, TValue> m_Dictionary;
private readonly IComparer<TKey> m_Comparer;
#region Properties
public int Count { get { return m_Dictionary.Count; } }
public bool IsReadOnly { get { return false; } }
[NotNull]
public ICollection<TKey> Keys { get { return m_OrderedKeys; } }
[NotNull]
public ICollection<TValue> Values { get { return m_ValuesOrderedByKey; } }
[CanBeNull]
public TValue this[[NotNull] TKey key]
{
get { return m_Dictionary[key]; }
set
{
// ReSharper disable once CompareNonConstrainedGenericWithNull
if (key == null)
throw new ArgumentNullException("key");
Remove(key);
Add(key, value);
}
}
#endregion
/// <summary>
/// Constructor.
/// </summary>
public IcdSortedDictionary()
: this(Comparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer)
: this(comparer, EqualityComparer<TKey>.Default)
{
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="comparer"></param>
/// <param name="equalityComparer"></param>
public IcdSortedDictionary([NotNull] IComparer<TKey> comparer, [NotNull] IEqualityComparer<TKey> equalityComparer)
{
if (comparer == null)
throw new ArgumentNullException("comparer");
if (equalityComparer == null)
throw new ArgumentNullException("equalityComparer");
m_Comparer = comparer;
m_OrderedKeys = new List<TKey>();
m_ValuesOrderedByKey = new List<TValue>();
m_Dictionary = new Dictionary<TKey, TValue>(equalityComparer);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="dictionary"></param>
public IcdSortedDictionary([NotNull] IEnumerable<KeyValuePair<TKey, TValue>> dictionary)
: this()
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
foreach (KeyValuePair<TKey, TValue> kvp in dictionary)
Add(kvp.Key, kvp.Value);
}
#region Methods
[NotNull]
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return m_OrderedKeys.Select(k => new KeyValuePair<TKey, TValue>(k, m_Dictionary[k]))
.GetEnumerator();
}
public void Add([NotNull] TKey key, [CanBeNull] TValue value)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (m_Dictionary.ContainsKey(key))
throw new ArgumentOutOfRangeException("key", "An item with the same key has already been added.");
int index = m_OrderedKeys.InsertSorted(key, m_Comparer);
m_ValuesOrderedByKey.Insert(index, value);
m_Dictionary[key] = value;
}
public void Clear()
{
m_OrderedKeys.Clear();
m_ValuesOrderedByKey.Clear();
m_Dictionary.Clear();
}
public bool ContainsKey([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
return m_Dictionary.ContainsKey(key);
}
public bool Remove([NotNull] TKey key)
{
// ReSharper disable CompareNonConstrainedGenericWithNull
if (key == null)
// ReSharper restore CompareNonConstrainedGenericWithNull
throw new ArgumentNullException("key");
if (!m_Dictionary.Remove(key))
return false;
int index = m_OrderedKeys.BinarySearch(key, m_Comparer);
m_OrderedKeys.RemoveAt(index);
m_ValuesOrderedByKey.RemoveAt(index);
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_Dictionary.TryGetValue(key, out value);
}
#endregion
#region Private Methods
[NotNull]
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Key, item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
TValue value;
return TryGetValue(item.Key, out value) &&
EqualityComparer<TValue>.Default.Equals(value, item.Value);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo([NotNull] KeyValuePair<TKey, TValue>[] array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
foreach (KeyValuePair<TKey, TValue> kvp in this)
{
array.SetValue(kvp, index);
index++;
}
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return (this as ICollection<KeyValuePair<TKey, TValue>>).Contains(item) && Remove(item.Key);
}
#endregion
}
}

View File

@@ -18,7 +18,7 @@ namespace ICD.Common.Utils.Collections
#endif
public sealed class PriorityQueue<T> : IEnumerable<T>, ICollection
{
private readonly IcdOrderedDictionary<int, List<T>> m_PriorityToQueue;
private readonly IcdSortedDictionary<int, List<T>> m_PriorityToQueue;
private int m_Count;
#region Properties
@@ -46,7 +46,7 @@ namespace ICD.Common.Utils.Collections
/// </summary>
public PriorityQueue()
{
m_PriorityToQueue = new IcdOrderedDictionary<int, List<T>>();
m_PriorityToQueue = new IcdSortedDictionary<int, List<T>>();
}
#region Methods

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

@@ -151,11 +151,11 @@ namespace ICD.Common.Utils.Collections
return m_Collection.GetEnumerator();
}
void ICollection.CopyTo(Array myArr, int index)
void ICollection.CopyTo(Array array, int index)
{
foreach (TContents item in m_Collection)
{
myArr.SetValue(item, index);
array.SetValue(item, index);
index++;
}
}

View File

@@ -61,9 +61,9 @@ namespace ICD.Common.Utils.Collections
m_Comparer = comparer;
}
public int GetHashCode(WeakKeyReference<T> weakKey)
public int GetHashCode(WeakKeyReference<T> obj)
{
return weakKey.HashCode;
return obj == null ? 0 : obj.HashCode;
}
// Note: There are actually 9 cases to handle here.

View File

@@ -29,11 +29,12 @@ namespace ICD.Common.Utils.Comparers
public int Compare(IEnumerable<T> x, IEnumerable<T> y)
{
if (x == null && y == null)
return 0;
if (x == null)
throw new ArgumentNullException("x");
return -1;
if (y == null)
throw new ArgumentNullException("y");
return 1;
using (IEnumerator<T> firstPos = x.GetEnumerator())
{

View File

@@ -19,6 +19,13 @@ namespace ICD.Common.Utils.Comparers
public int Compare(Version x, Version y)
{
if (x == null && y == null)
return 0;
if (x == null)
return -1;
if (y == null)
return 1;
return x.ClearUndefined()
.CompareTo(y.ClearUndefined());
}

310
ICD.Common.Utils/Csv/Csv.cs Normal file
View File

@@ -0,0 +1,310 @@
/*
* 2006 - 2018 Ted Spence, http://tedspence.com
* License: http://www.apache.org/licenses/LICENSE-2.0
* Home page: https://github.com/tspence/csharp-csv-reader
*/
using System;
using System.Collections.Generic;
using System.Text;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
using ICD.Common.Utils.Extensions;
#endif
namespace ICD.Common.Utils.Csv
{
/// <summary>
/// Root class that contains static functions for straightforward Csv parsing
/// </summary>
public static class Csv
{
/// <summary>
/// The default Csv field delimiter.
/// </summary>
public const char DEFAULT_CSV_DELIMITER = ',';
/// <summary>
/// The default Csv text qualifier. This is used to encode strings that contain the field delimiter.
/// </summary>
public const char DEFAULT_CSV_QUALIFIER = '"';
/// <summary>
/// The default TSV (tab delimited file) field delimiter.
/// </summary>
public const char DEFAULT_TSV_DELIMITER = '\t';
/// <summary>
/// The default TSV (tabe delimited file) text qualifier. This is used to encode strings that contain the field delimiter.
/// </summary>
public const char DEFAULT_TSV_QUALIFIER = '"';
#region Methods to read Csv data
/// <summary>
/// Parse a Csv stream into IEnumerable&lt;string[]&gt;, while permitting embedded newlines
/// </summary>
/// <param name="inStream">The stream to read</param>
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
/// <returns>An enumerable object that can be examined to retrieve rows from the stream.</returns>
public static IEnumerable<string[]> ParseStream(IcdStreamReader inStream, CsvReaderSettings settings)
{
string line = "";
int i = -1;
List<string> list = new List<string>();
var work = new StringBuilder();
// Ensure settings are non-null
if (settings == null) {
settings = CsvReaderSettings.CSV;
}
// Begin reading from the stream
while (i < line.Length || !inStream.EndOfStream)
{
// Consume the next character of data
i++;
if (i >= line.Length) {
var newLine = inStream.ReadLine();
line += newLine + settings.LineSeparator;
}
char c = line[i];
// Are we at a line separator? If so, yield our work and begin again
if (String.Equals(line.Substring(i, settings.LineSeparator.Length), settings.LineSeparator)) {
list.Add(work.ToString());
yield return list.ToArray();
list.Clear();
work.Clear();
if (inStream.EndOfStream)
{
break;
}
// Read in next line
if (i + settings.LineSeparator.Length >= line.Length)
{
line = inStream.ReadLine() + settings.LineSeparator;
}
else
{
line = line.Substring(i + settings.LineSeparator.Length);
}
i = -1;
// While starting a field, do we detect a text qualifier?
}
else if ((c == settings.TextQualifier) && (work.Length == 0))
{
// Our next task is to find the end of this qualified-text field
int p2 = -1;
while (p2 < 0) {
// If we don't see an end in sight, read more from the stream
p2 = line.IndexOf(settings.TextQualifier, i + 1);
if (p2 < 0) {
// No text qualifiers yet? Let's read more from the stream and continue
work.Append(line.Substring(i + 1));
i = -1;
var newLine = inStream.ReadLine();
if (String.IsNullOrEmpty(newLine) && inStream.EndOfStream)
{
break;
}
line = newLine + settings.LineSeparator;
continue;
}
// Append the text between the qualifiers
work.Append(line.Substring(i + 1, p2 - i - 1));
i = p2;
// If the user put in a doubled-up qualifier, e.g. `""`, insert a single one and continue
if (((p2 + 1) < line.Length) && (line[p2 + 1] == settings.TextQualifier))
{
work.Append(settings.TextQualifier);
i++;
p2 = -1;
}
}
// Does this start a new field?
}
else if (c == settings.FieldDelimiter)
{
// Is this a null token, and do we permit null tokens?
AddToken(list, work, settings);
// Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space
// Checks if the second parameter of the if statement will pass through successfully
// e.g. `"bob", "mary", "bill"`
if (i + 2 <= line.Length - 1)
{
if (line[i + 1].Equals(' ') && line[i + 2].Equals(settings.TextQualifier))
{
i++;
}
}
}
else
{
work.Append(c);
}
}
}
/// <summary>
/// Parse a single row of data from a Csv line into an array of objects, while permitting embedded newlines
/// DEPRECATED - Please use ParseStream instead.
/// </summary>
/// <param name="inStream">The stream to read</param>
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
/// <returns>An array containing all fields in the next row of data, or null if it could not be parsed.</returns>
public static string[] ParseMultiLine(IcdStreamReader inStream, CsvReaderSettings settings)
{
StringBuilder sb = new StringBuilder();
string[] array = null;
while (!inStream.EndOfStream)
{
// Read in a line
sb.Append(inStream.ReadLine());
// Does it parse?
string s = sb.ToString();
if (TryParseLine(s, out array, settings))
{
return array;
}
// We didn't succeed on the first try - our text must have an embedded newline in it.
// Let's assume that we were in the middle of parsing a field when we encountered a newline,
// and continue parsing.
sb.Append(settings.LineSeparator);
}
// Fails to parse - return the best array we were able to get
return array;
}
/// <summary>
/// Parse a line from a Csv file and return an array of fields, or null if
/// </summary>
/// <param name="line">One line of text from a Csv file</param>
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
/// <returns>An array containing all fields in the next row of data, or null if it could not be parsed.</returns>
public static string[] ParseLine(string line, CsvReaderSettings settings)
{
string[] row;
TryParseLine(line, out row, settings);
return row;
}
/// <summary>
/// Try to parse a line of Csv data. Can only return false if an unterminated text qualifier is encountered.
/// </summary>
/// <returns>False if there was an unterminated text qualifier in the <paramref name="line"/></returns>
/// <param name="line">The line of text to parse</param>
/// <param name="settings">The Csv settings to use for this parsing operation (Default: Csv)</param>
/// <param name="row">The array of fields found in the line</param>
public static bool TryParseLine(string line, out string[] row, CsvReaderSettings settings)
{
// Ensure settings are non-null
if (settings == null) settings = CsvReaderSettings.CSV;
// Okay, let's begin parsing
List<string> list = new List<string>();
var work = new StringBuilder();
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
// If we are starting a new field, is this field text qualified?
if ((c == settings.TextQualifier) && (work.Length == 0))
{
while (true)
{
int p2 = line.IndexOf(settings.TextQualifier, i + 1);
// If no closing qualifier is found, this string is broken; return failure.
if (p2 < 0)
{
work.Append(line.Substring(i + 1));
list.Add(work.ToString());
row = list.ToArray();
return false;
}
// Append this qualified string
work.Append(line.Substring(i + 1, p2 - i - 1));
i = p2;
// If this is a double quote, keep going!
if (((p2 + 1) < line.Length) && (line[p2 + 1] == settings.TextQualifier))
{
work.Append(settings.TextQualifier);
i++;
// otherwise, this is a single qualifier, we're done
}
else
{
break;
}
}
// Does this start a new field?
}
else if (c == settings.FieldDelimiter)
{
// Is this a null token, and do we permit null tokens?
AddToken(list, work, settings);
// Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space
// Checks if the second parameter of the if statement will pass through successfully
// e.g. "bob", "mary", "bill"
if (i + 2 <= line.Length - 1)
{
if (line[i + 1].Equals(' ') && line[i + 2].Equals(settings.TextQualifier))
{
i++;
}
}
}
else
{
work.Append(c);
}
}
// We always add the last work as an element. That means `alice,bob,charlie,` will be four items long.
AddToken(list, work, settings);
row = list.ToArray();
return true;
}
/// <summary>
/// Add a single token to the list
/// </summary>
/// <param name="list">List.</param>
/// <param name="work">Work.</param>
/// <param name="settings">Settings.</param>
private static void AddToken(List<string> list, StringBuilder work, CsvReaderSettings settings)
{
var s = work.ToString();
if (settings.AllowNull && String.Equals(s, settings.NullToken, StringComparison.Ordinal))
{
list.Add(null);
}
else
{
list.Add(s);
}
work.Length = 0;
}
#endregion
}
}

View File

@@ -0,0 +1,100 @@
/*
* 2006 - 2018 Ted Spence, http://tedspence.com
* License: http://www.apache.org/licenses/LICENSE-2.0
* Home page: https://github.com/tspence/csharp-csv-reader
*/
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
using ICD.Common.Utils.IO;
namespace ICD.Common.Utils.Csv
{
public sealed class CsvReader : IEnumerable<string[]>, IDisposable
{
private readonly CsvReaderSettings m_Settings;
private readonly IcdStreamReader m_Instream;
#region Public Variables
/// <summary>
/// If the first row in the file is a header row, this will be populated
/// </summary>
private string[] m_Headers;
#endregion
#region Constructors
/// <summary>
/// Construct a new Csv reader off a streamed source
/// </summary>
/// <param name="source">The stream source</param>
/// <param name="settings">The Csv settings to use for this reader (Default: Csv)</param>
public CsvReader(IcdStreamReader source, [CanBeNull] CsvReaderSettings settings)
{
m_Instream = source;
m_Settings = settings ?? CsvReaderSettings.CSV;
// Do we need to parse headers?
if (m_Settings.HeaderRowIncluded)
{
m_Headers = NextLine();
}
else
{
m_Headers = m_Settings.AssumedHeaders != null ? m_Settings.AssumedHeaders.ToArray() : null;
}
}
#endregion
#region Iterate through a Csv File
/// <summary>
/// Iterate through all lines in this Csv file
/// </summary>
/// <returns>An array of all data columns in the line</returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return Lines().GetEnumerator();
}
/// <summary>
/// Iterate through all lines in this Csv file
/// </summary>
/// <returns>An array of all data columns in the line</returns>
IEnumerator<string[]> IEnumerable<string[]>.GetEnumerator()
{
return Lines().GetEnumerator();
}
/// <summary>
/// Iterate through all lines in this Csv file
/// </summary>
/// <returns>An array of all data columns in the line</returns>
public IEnumerable<string[]> Lines()
{
return Csv.ParseStream(m_Instream, m_Settings);
}
/// <summary>
/// Retrieve the next line from the file.
/// DEPRECATED -
/// </summary>
/// <returns>One line from the file.</returns>
public string[] NextLine()
{
return Csv.ParseMultiLine(m_Instream, m_Settings);
}
#endregion
#region Disposal
/// <summary>
/// Close our resources - specifically, the stream reader
/// </summary>
public void Dispose()
{
m_Instream.Dispose();
}
#endregion
}
}

View File

@@ -0,0 +1,105 @@
/*
* 2006 - 2018 Ted Spence, http://tedspence.com
* License: http://www.apache.org/licenses/LICENSE-2.0
* Home page: https://github.com/tspence/csharp-csv-reader
*/
using System.Collections.Generic;
namespace ICD.Common.Utils.Csv
{
/// <summary>
/// Settings to configure how a Csv file is parsed
/// </summary>
public sealed class CsvReaderSettings
{
/// <summary>
/// Default constructor picks Csv as the default
/// </summary>
public CsvReaderSettings()
{
FieldDelimiter = ',';
TextQualifier = '"';
ForceQualifiers = false;
LineSeparator = IcdEnvironment.NewLine;
NullToken = null;
AllowNull = false;
IgnoreDimensionErrors = true;
AssumedHeaders = null;
HeaderRowIncluded = true;
}
/// <summary>
/// The character used to delimit individual fields in the Csv.
/// </summary>
public char FieldDelimiter { get; set; }
/// <summary>
/// The character used to enclose fields that contain the delimiter character.
/// </summary>
public char TextQualifier { get; set; }
/// <summary>
/// The separator used to indicate the end of a line in the Csv file.
/// </summary>
public string LineSeparator { get; set; }
/// <summary>
/// Set this value to true to enclose all fields in the text qualifier character.
/// </summary>
public bool ForceQualifiers { get; set; }
/// <summary>
/// Set this value to true to allow nulls to be rendered.
/// Csv files by default do not permit null fields. If this field is set to true, all non-null fields
/// will be enclosed by the text qualifier
/// </summary>
public bool AllowNull { get; set; }
/// <summary>
/// If AllowNull is set to true, this token will be used to represent NULL values.
/// </summary>
public string NullToken { get; set; }
/// <summary>
/// The first line of the Csv file will include the names of each field.
/// </summary>
public bool HeaderRowIncluded { get; set; }
/// <summary>
/// If HeaderRowIncluded is false, use these values for the headers
/// </summary>
public List<string> AssumedHeaders { get; set; }
/// <summary>
/// Set this value to true to allow parsing for files where each row has a different number of fields
/// </summary>
public bool IgnoreDimensionErrors { get; set; }
/// <summary>
/// Set this value to true to ignore header errors when deserializing
/// </summary>
public bool IgnoreHeaderErrors { get; set; }
/// <summary>
/// Standard comma-separated value (Csv) file settings
/// </summary>
public static readonly CsvReaderSettings CSV = new CsvReaderSettings();
/// <summary>
/// Standard comma-separated value (Csv) file settings that permit rendering of NULL values
/// </summary>
public static readonly CsvReaderSettings CSV_PERMIT_NULL = new CsvReaderSettings
{
AllowNull = true,
NullToken = "NULL"
};
/// <summary>
/// Standard tab-separated value (TSV) file settings
/// </summary>
public static readonly CsvReaderSettings TSV = new CsvReaderSettings
{
FieldDelimiter = '\t'
};
}
}

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>
@@ -352,10 +352,11 @@ namespace ICD.Common.Utils
/// <returns></returns>
public static IEnumerable<object> GetFlags(Type type, object value)
{
return s_EnumFlagsCacheSection.Execute(() => s_EnumFlagsCache.GetOrAddNew(type, () => new Dictionary<object, object[]>())
.GetOrAddNew(value, () => GetValues(type)
.Where(f => HasFlag(value, f))
.ToArray()));
return s_EnumFlagsCacheSection.Execute(() => s_EnumFlagsCache
.GetOrAddNew(type, () => new Dictionary<object, object[]>())
.GetOrAddNew(value, () => GetValues(type)
.Where(f => !HasMultipleFlags((int)f) && HasFlag(value, f))
.ToArray()));
}
/// <summary>
@@ -447,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,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
namespace ICD.Common.Utils.Extensions
@@ -59,6 +61,22 @@ namespace ICD.Common.Utils.Extensions
return EnumUtils.IncludeFlags(extends, other);
}
/// <summary>
/// Returns the enum, with the other flags set or unset
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="other"></param>
/// <param name="set"></param>
/// <returns></returns>
public static T SetFlags<T>(this T extends, T other, bool set)
where T : struct, IConvertible
{
return set ?
EnumUtils.IncludeFlags(extends, other) :
EnumUtils.ExcludeFlags(extends, other);
}
/// <summary>
/// Returns the enum value as a ushort.
/// </summary>
@@ -83,5 +101,25 @@ namespace ICD.Common.Utils.Extensions
{
return EnumUtils.ToStringUndefined(extends);
}
/// <summary>
/// Returns the next defined enum value for the enum type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <returns></returns>
public static T CycleNext<T>(this T extends)
where T : struct, IConvertible
{
if (EnumUtils.IsFlagsEnum(typeof(T)) && !EnumUtils.HasSingleFlag(extends))
throw new InvalidOperationException(string.Format("Cannot cycle enum with multiple flags - {0}", extends));
IEnumerable<T> values = EnumUtils.GetValues<T>();
IList<T> list = values as IList<T> ?? values.ToArray();
int index = list.BinarySearch(extends);
return index == list.Count - 1 ? list[0] : list[index + 1];
}
}
}

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

@@ -349,6 +349,22 @@ namespace ICD.Common.Utils.Extensions
#region Binary Search
/// <summary>
/// Returns the index of the item in the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="extends"></param>
/// <param name="item"></param>
/// <returns></returns>
[PublicAPI]
public static int BinarySearch<T>([NotNull] this IList<T> extends, T item)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.BinarySearch(item, Comparer<T>.Default);
}
/// <summary>
/// Returns the index of the item in the list.
/// </summary>

View File

@@ -218,7 +218,8 @@ namespace ICD.Common.Utils.Extensions
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic);
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (info == null)
{
instance = null;
@@ -238,7 +239,8 @@ namespace ICD.Common.Utils.Extensions
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic);
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (output == null)
instance = null;
@@ -309,7 +311,8 @@ namespace ICD.Common.Utils.Extensions
BindingFlags.Instance |
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.NonPublic);
BindingFlags.NonPublic |
BindingFlags.FlattenHierarchy);
if (method == null)
return false;

View File

@@ -0,0 +1,25 @@
#if NETSTANDARD
using System.Collections.Generic;
using System.Linq;
using ICD.Common.Properties;
using Microsoft.Win32;
namespace ICD.Common.Utils.Extensions
{
public static class RegistryExtensions
{
/// <summary>
/// Opens subkeys for the given registry key.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static IEnumerable<RegistryKey> OpenSubKeys([NotNull] this RegistryKey extends)
{
return extends.GetSubKeyNames()
.Select(extends.OpenSubKey)
.Where(k => k != null);
}
}
}
#endif

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICD.Common.Properties;
@@ -142,6 +143,46 @@ namespace ICD.Common.Utils.Extensions
Math.Min(chunkSize, extends.Length - (i * chunkSize))));
}
/// <summary>
/// Joins the strings in the enumerable.
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static string Join([NotNull] this IEnumerable<string> extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
return extends.Join(string.Empty);
}
/// <summary>
/// Joins the strings in the enumerable.
/// </summary>
/// <param name="extends"></param>
/// <param name="delimiter"></param>
/// <returns></returns>
public static string Join([NotNull] this IEnumerable<string> extends, string delimiter)
{
if (extends == null)
throw new ArgumentNullException("extends");
StringBuilder builder = new StringBuilder();
bool isFirst = true;
foreach (string item in extends)
{
if (!isFirst)
builder.Append(delimiter);
builder.Append(item);
isFirst = false;
}
return builder.ToString();
}
/// <summary>
/// Removes whitespace from the string.
/// </summary>

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

@@ -62,5 +62,23 @@ namespace ICD.Common.Utils.Extensions
return builder.ToString();
}
/// <summary>
/// Returns a new Uri representing the Uri for the parent path.
/// E.g.
/// www.test.com/A/B/C
/// Becomes
/// www.test.com/A/B/
/// </summary>
/// <param name="extends"></param>
/// <returns></returns>
public static Uri GetParentUri([NotNull] this Uri extends)
{
if (extends == null)
throw new ArgumentNullException("extends");
string parentUriString = extends.AbsoluteUri.Substring(0, extends.AbsoluteUri.Length - extends.Segments.Last().Length);
return new Uri(parentUriString, UriKind.Absolute);
}
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Text;
namespace ICD.Common.Utils.Extensions
{

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,36 +1,28 @@
<?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="bin\**" />
<Compile Remove="SIMPLSharpLogs\**" />
<EmbeddedResource Remove="bin\**" />
<EmbeddedResource Remove="SIMPLSharpLogs\**" />
<None Remove="bin\**" />
<None Remove="SIMPLSharpLogs\**" />
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<None Remove="ICD.Common.projectinfo" />
<None Remove="ICD.Common_SimplSharp.suo" />
@@ -38,12 +30,17 @@
<None Remove="Properties\ControlSystem.cfg" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.SQLite" Version="2.2.6" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="2.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="Pastel" Version="1.3.1" />
<PackageReference Include="System.Net.NetworkInformation" Version="4.3.0" />
<PackageReference Include="System.Runtime.Loader" Version="4.3.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">
@@ -53,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,17 +79,22 @@
<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" />
<Compile Include="Comparers\SequenceComparer.cs" />
<Compile Include="Comparers\UndefinedVersionComparer.cs" />
<Compile Include="Comparers\UndefinedVersionEqualityComparer.cs" />
<Compile Include="Csv\Csv.cs" />
<Compile Include="Csv\CsvReader.cs" />
<Compile Include="Csv\CsvReaderSettings.cs" />
<Compile Include="eConsoleColor.cs" />
<Compile Include="DateTimeUtils.cs" />
<Compile Include="eDaysOfWeek.cs" />
@@ -115,10 +120,14 @@
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<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" />
@@ -209,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" />
@@ -225,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" />
@@ -233,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" />
@@ -254,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

@@ -132,7 +132,7 @@ namespace ICD.Common.Utils.IO.Compression
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
private uint m_CrcValue = 0xffffffff;
@@ -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,10 +1,10 @@
using System;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp.CrestronIO;
#else
using ICD.Common.Utils.Extensions;
using System.IO;
using Microsoft.DotNet.PlatformAbstractions;
using System.Reflection;
#endif
namespace ICD.Common.Utils.IO
@@ -13,10 +13,11 @@ namespace ICD.Common.Utils.IO
{
public static string GetApplicationDirectory()
{
#if SIMPLSHARP
#if !NETSTANDARD
return Directory.GetApplicationDirectory();
#else
return ApplicationEnvironment.ApplicationBasePath;
string pathToDll = Assembly.GetExecutingAssembly().GetPath();
return pathToDll == null ? null : IcdPath.GetDirectoryName(pathToDll);
#endif
}
@@ -26,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

@@ -98,6 +98,12 @@ namespace ICD.Common.Utils.IO
return File.GetCreationTime(path);
}
[PublicAPI]
public static long GetLength(string path)
{
return new FileInfo(path).Length;
}
[PublicAPI]
public static IcdFileStream Create(string path)
{
@@ -120,5 +126,18 @@ namespace ICD.Common.Utils.IO
{
File.Move(sourceFileName, destFileName);
}
#if !SIMPLSHARP
public static void SetAttributes(string path, FileAttributes attributes, bool recursive)
{
File.SetAttributes(path, attributes);
if (!recursive || !Directory.Exists(path))
return;
foreach (string innerPath in Directory.GetFileSystemEntries(path))
SetAttributes(innerPath, attributes, true);
}
#endif
}
}

View File

@@ -1,7 +1,7 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronIO;
#elif STANDARD
#else
using System.IO;
#endif
@@ -11,6 +11,10 @@ namespace ICD.Common.Utils.IO
{
private readonly StreamReader m_StreamReader;
public StreamReader WrappedStreamReader { get { return m_StreamReader; } }
public bool EndOfStream { get { return m_StreamReader.EndOfStream; } }
/// <summary>
/// Constructor.
/// </summary>
@@ -23,6 +27,21 @@ namespace ICD.Common.Utils.IO
m_StreamReader = new StreamReader(memoryStream.WrappedMemoryStream);
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="path"></param>
public IcdStreamReader(string path)
{
if (path == null)
throw new ArgumentNullException("path");
if (!IcdFile.Exists(path))
throw new FileNotFoundException("Error creating stream reader, file not found");
m_StreamReader = new StreamReader(path);
}
~IcdStreamReader()
{
Dispose();
@@ -37,5 +56,10 @@ namespace ICD.Common.Utils.IO
{
m_StreamReader.Dispose();
}
public string ReadLine()
{
return m_StreamReader.ReadLine();
}
}
}

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;
@@ -25,9 +25,47 @@ namespace ICD.Common.Utils
public static event EventHandler<StringEventArgs> OnConsolePrint;
private static readonly SafeCriticalSection s_Section;
private static readonly Regex s_NewLineRegex;
private static bool? s_IsConsoleApp;
/// <summary>
/// Returns true if the application is being run from an interactive console,
/// false if the application is being run as a headless service.
/// </summary>
/// <value></value>
public static bool IsConsoleApp
{
get
{
if (s_IsConsoleApp == null)
{
#if !NETSTANDARD
s_IsConsoleApp = false;
#else
try
{
// Hack
int unused = Console.WindowHeight;
s_IsConsoleApp = true;
if (Console.Title.Length > 0)
s_IsConsoleApp = true;
if (!Environment.UserInteractive)
s_IsConsoleApp = false;
}
catch
{
s_IsConsoleApp = false;
}
#endif
}
return s_IsConsoleApp.Value;
}
}
/// <summary>
/// Static constructor.
/// </summary>
@@ -56,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
{
@@ -72,11 +114,10 @@ namespace ICD.Common.Utils
{
PrintLine(message);
}
return;
return;
}
#endif
PrintLine(message);
PrintLine(message);
}
public static void PrintLine(string message)
@@ -87,11 +128,14 @@ 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
Console.WriteLine(fixedMessage);
Trace.WriteLine(AnsiUtils.StripAnsi(fixedMessage));
if (IsConsoleApp)
Console.WriteLine(fixedMessage);
#endif
}
finally
@@ -128,11 +172,14 @@ 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
Console.Write(message);
Trace.Write(AnsiUtils.StripAnsi(fixedMessage));
if (IsConsoleApp)
Console.Write(fixedMessage);
#endif
}
finally
@@ -163,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);
@@ -176,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;
@@ -210,6 +212,15 @@ namespace ICD.Common.Utils
#region Methods
/// <summary>
/// Gets the name of the local time zone.
/// </summary>
/// <returns></returns>
public static string GetLocalTimeZoneName()
{
return CrestronEnvironment.GetTimeZone().Name;
}
public static DateTime GetLocalTime()
{
return CrestronEnvironment.GetLocalTime();

View File

@@ -1,17 +1,36 @@
#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
{
public static partial class IcdEnvironment
{
/// <summary>
/// We get several device added/removed events for a single device
/// To reduce noise, we use timers in an attempt to compress them to a single event
/// </summary>
private const long DEVICE_ADDED_REMOVED_TIME = 500;
private static readonly SafeTimer s_DeviceAddedTimer = SafeTimer.Stopped(DeviceAddedTimerCallback);
private static readonly SafeTimer s_DeviceRemovedTimer = SafeTimer.Stopped(DeviceRemovedTimerCallback);
/// <summary>
/// Raised when a system device (eg USB) is added
/// May be raised once for multiple device additions
/// </summary>
public static event EventHandler<BoolEventArgs> OnSystemDeviceAddedRemoved;
public static string NewLine { get { return Environment.NewLine; } }
/// <summary>
@@ -91,6 +110,18 @@ namespace ICD.Common.Utils
s_Framework = eFramework.Standard;
s_CrestronSeries = eCrestronSeries.Na;
s_CrestronRuntimeEnvironment = eCrestronRuntimeEnvironment.Na;
s_CrestronDevicePlatform = eCrestronDevicePlatform.Na;
}
/// <summary>
/// Gets the name of the local time zone.
/// </summary>
/// <returns></returns>
public static string GetLocalTimeZoneName()
{
return System.TimeZoneInfo.Local.IsDaylightSavingTime(GetLocalTime())
? System.TimeZoneInfo.Local.DaylightName
: System.TimeZoneInfo.Local.StandardName;
}
public static DateTime GetLocalTime()
@@ -119,6 +150,63 @@ namespace ICD.Common.Utils
const string replace = "$1:$2:$3:$4:$5:$6";
return Regex.Replace(address, regex, replace);
}
/// <summary>
/// Called by the application to raise the program status.
/// </summary>
/// <param name="status"></param>
public static void SetProgramStatus(eProgramStatusEventType status)
{
ProgramStatusCallback handler = OnProgramStatusEvent;
if (handler != null)
handler(status);
}
/// <summary>
/// Called by the application to raise session change events.
/// </summary>
/// <param name="sessionId"></param>
/// <param name="reasonCode"></param>
public static void HandleSessionChange(int sessionId, eSessionChangeEventType reasonCode)
{
SessionChangeEventCallback handler = OnSessionChangedEvent;
if (handler != null)
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>
public static void RaiseSystemDeviceAddedEvent()
{
s_DeviceAddedTimer.Reset(DEVICE_ADDED_REMOVED_TIME);
}
/// <summary>
/// Call this method to raise the device added/removed event for a removed device
/// Uses a timer to attempt to compress multiple events into a single event
/// </summary>
public static void RaiseSystemDeviceRemovedEvent()
{
s_DeviceRemovedTimer.Reset(DEVICE_ADDED_REMOVED_TIME);
}
/// <summary>
/// Actually fire the added event after the timer expires
/// </summary>
private static void DeviceAddedTimerCallback()
{
OnSystemDeviceAddedRemoved.Raise(null, true);
}
/// <summary>
/// Actually fire the removed event after the timer expires
/// </summary>
private static void DeviceRemovedTimerCallback()
{
OnSystemDeviceAddedRemoved.Raise(null, false);
}
}
}
#endif

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>
@@ -68,14 +75,44 @@ namespace ICD.Common.Utils
EthernetLan2Adapter = 8,
}
/// <summary>
/// Enum for session change events.
/// </summary>
public enum eSessionChangeEventType
{
None = 0,
ConsoleConnect = 1,
ConsoleDisconnect = 2,
RemoteConnect = 3,
RemoteDisconnect = 4,
SessionLogon = 5,
SessionLogoff = 6,
SessionLock = 7,
SessionUnlock = 8,
SessionRemoteControl = 9
}
public delegate void ProgramStatusCallback(eProgramStatusEventType type);
public delegate void EthernetEventCallback(eEthernetAdapterType adapter, eEthernetEventType type);
public delegate void SessionChangeEventCallback(int sessionId, eSessionChangeEventType type);
/// <summary>
/// Raised when the program status changes.
/// </summary>
public static event ProgramStatusCallback OnProgramStatusEvent;
/// <summary>
/// Raised when a network adapter connects/disconnects.
/// </summary>
public static event EthernetEventCallback OnEthernetEvent;
/// <summary>
/// Raised when a session changes, such as user logging in/out.
/// </summary>
public static event SessionChangeEventCallback OnSessionChangedEvent;
/// <summary>
/// Raised when the program has completed initialization.
/// </summary>
@@ -86,19 +123,27 @@ namespace ICD.Common.Utils
/// </summary>
public static event EventHandler OnSystemDateTimeChanged;
private static eFramework s_Framework;
private static eCrestronSeries s_CrestronSeries;
private static eCrestronRuntimeEnvironment s_CrestronRuntimeEnvironment;
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>
@@ -128,12 +173,12 @@ namespace ICD.Common.Utils
}
/// <summary>
/// Gets UTC time
/// Uses GetLocalTime so Crestron Env will have ms percision
/// Gets UTC time.
/// </summary>
/// <returns></returns>
public static DateTime GetUtcTime()
{
// Use GetLocalTime so Crestron Env will have ms precision
return GetLocalTime().ToUniversalTime();
}
}

View File

@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using ICD.Common.Properties;
#if SIMPLSHARP
#if !NETSTANDARD
using Crestron.SimplSharp;
#else
using System.Diagnostics;
#endif
namespace ICD.Common.Utils
@@ -30,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)},
@@ -38,12 +40,49 @@ namespace ICD.Common.Utils
{EXCEPTION, ErrorLog.Exception},
{INFO, (m, e) => ErrorLog.Info(m)}
#else
{ERROR, (m, e) => Console.Error.WriteLine(m)},
{WARN, (m, e) => Console.Error.WriteLine(m)},
{NOTICE, (m, e) => Console.Error.WriteLine(m)},
{OK, (m, e) => Console.Error.WriteLine(m)},
{EXCEPTION, (m, e) => Console.Error.WriteLine(m)},
{INFO, (m, e) => Console.Error.WriteLine(m)}
{
ERROR, (m, e) =>
{
Trace.WriteLine(AnsiUtils.StripAnsi(m));
Console.Error.WriteLine(m);
}
},
{
WARN, (m, e) =>
{
Trace.WriteLine(AnsiUtils.StripAnsi(m));
Console.Error.WriteLine(m);
}
},
{
NOTICE, (m, e) =>
{
Trace.WriteLine(AnsiUtils.StripAnsi(m));
Console.Error.WriteLine(m);
}
},
{
OK, (m, e) =>
{
Trace.WriteLine(AnsiUtils.StripAnsi(m));
Console.Error.WriteLine(m);
}
},
{
EXCEPTION, (m, e) =>
{
Trace.WriteLine(AnsiUtils.StripAnsi(m));
Console.Error.WriteLine(m);
}
},
{
INFO, (m, e) =>
{
Trace.WriteLine(AnsiUtils.StripAnsi(m));
Console.Error.WriteLine(m);
}
}
#endif
};

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;
@@ -169,10 +169,11 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="parts"></param>
/// <returns></returns>
public void AppendPath(params string[] parts)
public IcdUriBuilder AppendPath(params string[] parts)
{
parts = parts.Prepend(Path).ToArray(parts.Length + 1);
Path = Combine(parts);
return this;
}
#region Flurl

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,12 +1,18 @@
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;
#else
using System.Reflection;
using System.Runtime.ExceptionServices;
#endif
using ICD.Common.Properties;
using Newtonsoft.Json;
namespace ICD.Common.Utils.Json
{
@@ -54,7 +60,12 @@ namespace ICD.Common.Utils.Json
}
catch (TargetInvocationException e)
{
throw e.InnerException;
#if SIMPLSHARP
throw e.InnerException ?? e;
#else
ExceptionDispatchInfo.Capture(e.InnerException ?? e).Throw();
throw;
#endif
}
}

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;
@@ -213,6 +213,10 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void RestartProgram()
{
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
if (logger != null)
logger.AddEntry(eSeverity.Informational, "Intentional Restart of Program");
string consoleResult = string.Empty;
string command = string.Format("progreset -p:{0:D2}", ProgramUtils.ProgramNumber);
IcdConsole.SendControlSystemCommand(command, ref consoleResult);
@@ -224,6 +228,10 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void Reboot()
{
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
if (logger != null)
logger.AddEntry(eSeverity.Informational, "Intentional Reboot of Processor");
string consoleResult = string.Empty;
IcdConsole.SendControlSystemCommand("reboot", ref consoleResult);
}

View File

@@ -1,6 +1,13 @@
#if !SIMPLSHARP
#if NETSTANDARD
using System;
using System.Diagnostics;
using System.Linq;
using System.Management;
using ICD.Common.Properties;
using ICD.Common.Utils.IO;
using ICD.Common.Utils.Services;
using ICD.Common.Utils.Services.Logging;
using Microsoft.Win32;
namespace ICD.Common.Utils
{
@@ -14,7 +21,15 @@ namespace ICD.Common.Utils
/// Gets the model name of the processor.
/// </summary>
[PublicAPI]
public static string ModelName { get { return Environment.MachineName; } }
public static string ModelName
{
get
{
string productName = RegistryLocalMachineGetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName");
string csdVersion = RegistryLocalMachineGetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CSDVersion");
return string.Format("{0} {1}", productName, csdVersion).Trim();
}
}
/// <summary>
/// Gets the processor firmware version.
@@ -24,8 +39,7 @@ namespace ICD.Common.Utils
{
get
{
// TODO
return null;
return RegistryLocalMachineGetString(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "CurrentBuild");
}
}
@@ -37,8 +51,8 @@ namespace ICD.Common.Utils
{
get
{
// TODO
return DateTime.MinValue;
long time = RegistryLocalMachineGetLong(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallTime");
return time == 0 ? DateTime.MinValue : DateTime.FromFileTime(132448642489109028).ToUniversalTime();
}
}
@@ -50,8 +64,8 @@ namespace ICD.Common.Utils
{
get
{
// TODO
return null;
ManagementObject os = new ManagementObject("Win32_OperatingSystem=@");
return (string)os["SerialNumber"];
}
}
@@ -62,8 +76,22 @@ namespace ICD.Common.Utils
{
get
{
// TODO
return 0.0f;
ManagementObjectSearcher wmiObject = new ManagementObjectSearcher("select * from Win32_OperatingSystem");
var memoryValues =
wmiObject.Get()
.Cast<ManagementObject>()
.Select(mo => new
{
FreePhysicalMemory = double.Parse(mo["FreePhysicalMemory"].ToString()),
TotalVisibleMemorySize = double.Parse(mo["TotalVisibleMemorySize"].ToString())
})
.FirstOrDefault();
return memoryValues == null
? 0
: (float)((memoryValues.TotalVisibleMemorySize - memoryValues.FreePhysicalMemory) /
memoryValues.TotalVisibleMemorySize);
}
}
@@ -125,7 +153,16 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void RestartProgram()
{
throw new NotSupportedException();
string filename = Process.GetCurrentProcess().MainModule?.FileName;
if (string.IsNullOrEmpty(filename) || !IcdFile.Exists(filename))
throw new InvalidOperationException("Failed to find program filename");
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
if (logger != null)
logger.AddEntry(eSeverity.Informational, "Intentional Restart of Program");
Process.Start(filename);
Environment.Exit(0);
}
/// <summary>
@@ -134,7 +171,19 @@ namespace ICD.Common.Utils
[PublicAPI]
public static void Reboot()
{
throw new NotSupportedException();
ILoggerService logger = ServiceProvider.TryGetService<ILoggerService>();
if (logger != null)
logger.AddEntry(eSeverity.Informational, "Intentional Reboot of Processor");
// TODO - Linux
ProcessStartInfo psi =
new ProcessStartInfo("shutdown", "/r /t 0")
{
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(psi);
}
/// <summary>
@@ -145,10 +194,7 @@ namespace ICD.Common.Utils
[PublicAPI]
public static DateTime? GetSystemStartTime()
{
if (s_SystemStartTime == null)
s_SystemStartTime = IcdEnvironment.GetUtcTime() - TimeSpan.FromMilliseconds(Environment.TickCount);
return s_SystemStartTime;
return s_SystemStartTime ?? (s_SystemStartTime = Process.GetCurrentProcess().StartTime.ToUniversalTime());
}
/// <summary>
@@ -163,6 +209,36 @@ namespace ICD.Common.Utils
}
#endregion
#region Private Methods
private static string RegistryLocalMachineGetString(string path, string key)
{
try
{
RegistryKey rk = Registry.LocalMachine.OpenSubKey(path);
return rk == null ? string.Empty : (string)rk.GetValue(key);
}
catch
{
return string.Empty;
}
}
private static long RegistryLocalMachineGetLong(string path, string key)
{
try
{
RegistryKey rk = Registry.LocalMachine.OpenSubKey(path);
return rk == null ? 0 : (long)rk.GetValue(key);
}
catch
{
return 0;
}
}
#endregion
}
}
#endif

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,7 +1,8 @@
#if !SIMPLSHARP
#if NETSTANDARD
using ICD.Common.Utils.IO;
using System;
using System.Reflection;
using System.Security.Principal;
using ICD.Common.Properties;
namespace ICD.Common.Utils
@@ -64,6 +65,19 @@ namespace ICD.Common.Utils
{
get { return IcdFile.GetCreationTime(PathUtils.Join(PathUtils.ProgramPath, ProgramFile)); }
}
}
/// <summary>
/// Returns true if the current executing user is an admin.
/// </summary>
[PublicAPI]
public static bool IsElevated
{
get
{
WindowsIdentity current = WindowsIdentity.GetCurrent();
return new WindowsPrincipal(current).IsInRole(WindowsBuiltInRole.Administrator);
}
}
}
}
#endif

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("14.2.0.0")]
[assembly: AssemblyCopyright("Copyright © ICD Systems 2023")]
[assembly: AssemblyVersion("17.2.0.0")]

View File

@@ -14,10 +14,10 @@
//
// =============================================================================
// ReSharper disable once RedundantUsingDirective
using System;
// ReSharper disable once CheckNamespace
namespace System.Reflection
{
/// <summary>

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
@@ -178,7 +180,7 @@ namespace ICD.Common.Utils
if (type == null)
throw new ArgumentNullException("type");
return (T)CreateInstance(type);
return (T)CreateInstance(type, new object[0]);
}
/// <summary>
@@ -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

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICD.Common.Properties;
namespace ICD.Common.Utils
{
@@ -111,5 +113,34 @@ namespace ICD.Common.Utils
return Regex.Replace(input, pattern, evaluator, options);
}
/// <summary>
/// Returns the first successful match or the last failure.
/// </summary>
/// <param name="input"></param>
/// <param name="patterns"></param>
/// <returns></returns>
[NotNull]
public static Match MatchAny([NotNull] string input, [NotNull] IEnumerable<string> patterns)
{
if (input == null)
throw new ArgumentNullException("input");
if (patterns == null)
throw new ArgumentNullException("patterns");
Match last = null;
foreach (string pattern in patterns)
{
last = Regex.Match(input, pattern);
if (last.Success)
break;
}
if (last == null)
throw new InvalidOperationException("No patterns provided");
return last;
}
}
}

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;
@@ -53,6 +53,11 @@ namespace ICD.Common.Utils.Services.Logging
[PublicAPI]
[NotNull]
IEnumerable<KeyValuePair<int, LogItem>> GetHistory();
/// <summary>
/// Writes all enqueued logs.
/// </summary>
void Flush();
}
/// <summary>
@@ -101,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);
@@ -112,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

@@ -25,6 +25,31 @@ namespace ICD.Common.Utils.Services
#region Methods
/// <summary>
/// Retrieves the registered service of the given type.
/// Creates a new instance using the given function if it does not exist.
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <param name="constructor"></param>
/// <returns></returns>
[PublicAPI]
[NotNull]
public static TService GetOrAddService<TService>([NotNull] Func<TService> constructor)
{
if (constructor == null)
throw new ArgumentNullException("constructor");
TService output = TryGetService<TService>();
if (output == null)
{
output = constructor();
AddService(output);
}
return output;
}
/// <summary>
/// Retrieves the registered service of the given type. Use this for required dependencies.
/// </summary>

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

@@ -19,7 +19,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="numeric"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ToHexLiteral(int numeric)
{
return string.Format("\\x{0:X2}", numeric);
@@ -30,7 +30,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ToHexLiteral(char c)
{
return ToHexLiteral(Convert.ToInt32(c));
@@ -41,7 +41,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ToHexLiteral([NotNull] string input)
{
if (input == null)
@@ -78,7 +78,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string FromHexLiteral([NotNull] string data)
{
if (data == null)
@@ -98,7 +98,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ToMixedReadableHexLiteral(char c)
{
int numeric = Convert.ToInt32(c);
@@ -115,7 +115,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ToMixedReadableHexLiteral([NotNull] string input)
{
if (input == null)
@@ -134,7 +134,8 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToString(object value)
[NotNull]
public static string ToString([CanBeNull] object value)
{
return string.Format("{0}", value);
}
@@ -144,7 +145,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ToString([NotNull] IEnumerable<byte> bytes)
{
if (bytes == null)
@@ -160,7 +161,7 @@ namespace ICD.Common.Utils
/// <param name="bytes"></param>
/// <param name="length"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ToString([NotNull] IEnumerable<byte> bytes, int length)
{
if (bytes == null)
@@ -175,6 +176,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[NotNull]
[PublicAPI]
public static byte[] ToBytes([NotNull] string input)
{
@@ -184,6 +186,43 @@ namespace ICD.Common.Utils
return Encoding.GetEncoding(28591).GetBytes(input);
}
/// <summary>
/// Converts the hex string ("01AB23") to a byte array ({1, 171, 23}).
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NotNull]
[PublicAPI]
public static byte[] HexToBytes([NotNull] string value)
{
if (value == null)
throw new ArgumentNullException("value");
if (value.Length % 2 != 0)
throw new ArgumentException("The binary key cannot have an odd number of digits");
return value.Split(2)
.Select(s => HexToByte(s))
.ToArray();
}
/// <summary>
/// Converts the hex string ("AB") to a byte (171).
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[PublicAPI]
public static byte HexToByte([NotNull] string value)
{
if (value == null)
throw new ArgumentNullException("value");
if (value.Length == 0 || value.Length > 2)
throw new ArgumentOutOfRangeException("value");
return Convert.ToByte(value, 16);
}
/// <summary>
/// Attempts to parse the string as an integer.
/// </summary>
@@ -191,7 +230,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out int result)
public static bool TryParse([NotNull] string value, out int result)
{
return TryConvert(Convert.ToInt32, value, out result);
}
@@ -203,7 +242,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out uint result)
public static bool TryParse([NotNull] string value, out uint result)
{
return TryConvert(Convert.ToUInt32, value, out result);
}
@@ -215,7 +254,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out short result)
public static bool TryParse([NotNull] string value, out short result)
{
return TryConvert(Convert.ToInt16, value, out result);
}
@@ -227,7 +266,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out ushort result)
public static bool TryParse([NotNull] string value, out ushort result)
{
return TryConvert(Convert.ToUInt16, value, out result);
}
@@ -239,7 +278,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out long result)
public static bool TryParse([NotNull] string value, out long result)
{
return TryConvert(Convert.ToInt64, value, out result);
}
@@ -251,7 +290,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out ulong result)
public static bool TryParse([NotNull] string value, out ulong result)
{
return TryConvert(Convert.ToUInt64, value, out result);
}
@@ -263,7 +302,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out float result)
public static bool TryParse([NotNull] string value, out float result)
{
return TryConvert(Convert.ToSingle, value, out result);
}
@@ -275,7 +314,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out double result)
public static bool TryParse([NotNull] string value, out double result)
{
return TryConvert(Convert.ToDouble, value, out result);
}
@@ -287,7 +326,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out byte result)
public static bool TryParse([NotNull] string value, out byte result)
{
return TryConvert(Convert.ToByte, value, out result);
}
@@ -299,7 +338,7 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out char result)
public static bool TryParse([NotNull] string value, out char result)
{
return TryConvert(Convert.ToChar, value, out result);
}
@@ -311,11 +350,32 @@ namespace ICD.Common.Utils
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse(string value, out bool result)
public static bool TryParse([NotNull] string value, out bool result)
{
return TryConvert(Convert.ToBoolean, value, out result);
}
/// <summary>
/// Attempts to parse the string as a guid.
/// </summary>
/// <param name="value"></param>
/// <param name="result"></param>
/// <returns></returns>
[PublicAPI]
public static bool TryParse([NotNull] string value, out Guid result)
{
try
{
result = new Guid(value);
return true;
}
catch (Exception)
{
result = Guid.Empty;
return false;
}
}
/// <summary>
/// Attempts to parse the string via the given conversion function.
/// </summary>
@@ -350,6 +410,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
[NotNull]
public static string NiceName([NotNull] object obj)
{
if (obj == null)
@@ -363,6 +424,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
[NotNull]
public static string NiceName([NotNull] string name)
{
if (name == null)
@@ -399,6 +461,7 @@ namespace ICD.Common.Utils
/// <param name="phoneFormat"></param>
/// <param name="number"></param>
/// <returns></returns>
[NotNull]
public static string SafeNumericFormat([NotNull] string phoneFormat, [NotNull] string number)
{
if (phoneFormat == null)
@@ -441,7 +504,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string Reverse([NotNull] string input)
{
if (input == null)
@@ -458,6 +521,7 @@ namespace ICD.Common.Utils
/// <param name="input"></param>
/// <param name="count"></param>
/// <returns></returns>
[NotNull]
public static string Repeat(char input, int count)
{
return Repeat(input.ToString(), count);
@@ -469,7 +533,7 @@ namespace ICD.Common.Utils
/// <param name="input"></param>
/// <param name="count"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string Repeat(string input, int count)
{
if (count < 0)
@@ -485,7 +549,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ArrayFormat<T>([NotNull] IEnumerable<T> items)
{
if (items == null)
@@ -516,7 +580,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ArrayRangeFormat([NotNull] IEnumerable<int> items)
{
if (items == null)
@@ -537,7 +601,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string ArrayRangeFormat([NotNull] IEnumerable<ushort> items)
{
if (items == null)
@@ -552,7 +616,7 @@ namespace ICD.Common.Utils
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
[PublicAPI]
[PublicAPI, NotNull]
public static string RangeFormat(object a, object b)
{
return string.Format("[{0} - {1}]", a, b);
@@ -563,8 +627,8 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[PublicAPI]
public static string UppercaseFirst(string input)
[PublicAPI, CanBeNull]
public static string UppercaseFirst([CanBeNull] string input)
{
if (string.IsNullOrEmpty(input))
return input;
@@ -577,8 +641,8 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
[PublicAPI]
public static string ToTitleCase(string input)
[PublicAPI, NotNull]
public static string ToTitleCase([NotNull] string input)
{
return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input);
}
@@ -588,6 +652,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="ipid"></param>
/// <returns></returns>
[NotNull]
public static string ToIpIdString(byte ipid)
{
return string.Format("0x{0:X2}", ipid);
@@ -642,7 +707,8 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string RemoveWhitespace(string text)
[CanBeNull]
public static string RemoveWhitespace([CanBeNull] string text)
{
return text == null ? null : text.RemoveWhitespace();
}
@@ -652,7 +718,8 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string RemoveDuplicateWhitespace(string text)
[CanBeNull]
public static string RemoveDuplicateWhitespace([CanBeNull] string text)
{
return text == null ? null : text.RemoveDuplicateWhitespace();
}
@@ -662,7 +729,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static bool IsNullOrWhitespace(string text)
public static bool IsNullOrWhitespace([CanBeNull] string text)
{
if (string.IsNullOrEmpty(text))
return true;
@@ -676,7 +743,8 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="password"></param>
/// <returns></returns>
public static string PasswordFormat(string password)
[CanBeNull]
public static string PasswordFormat([CanBeNull] string password)
{
return password == null ? null : Repeat('*', password.Length);
}
@@ -686,7 +754,8 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string ToRepresentation(string value)
[NotNull]
public static string ToRepresentation([CanBeNull] string value)
{
return value == null ? "NULL" : string.Format("\"{0}\"", value);
}
@@ -696,6 +765,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="items"></param>
/// <returns></returns>
[NotNull]
public static string SerialComma([NotNull] IEnumerable<string> items)
{
if (items == null)
@@ -729,7 +799,7 @@ namespace ICD.Common.Utils
/// <param name="value"></param>
/// <returns></returns>
[PublicAPI, CanBeNull]
public static string Trim(string value)
public static string Trim([CanBeNull] string value)
{
return value == null ? null : value.ToUpper();
}
@@ -768,6 +838,7 @@ namespace ICD.Common.Utils
/// <param name="ignoreCase"></param>
/// <returns></returns>
[PublicAPI]
[CanBeNull]
public static string GetLongestCommonIntersectionFromStart([NotNull] IEnumerable<string> items, bool ignoreCase)
{
if (items == null)
@@ -811,6 +882,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NotNull]
public static string Enquote([NotNull] string value)
{
if (value == null)
@@ -830,6 +902,7 @@ namespace ICD.Common.Utils
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NotNull]
public static string UnEnquote([NotNull] string value)
{
if (value == null)

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,9 +230,8 @@ 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
return;
#else
builder.Append(DOWN_RIGHT).Append(HORIZONTAL);
@@ -257,9 +256,8 @@ namespace ICD.Common.Utils
private void AppendBottomSeparator(StringBuilder builder, IList<int> columnWidths)
{
#if SIMPLSHARP
#if !NETSTANDARD
AppendSeparator(builder, columnWidths);
return;
#else
builder.Append(UP_RIGHT).Append(HORIZONTAL);
@@ -284,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
@@ -300,7 +298,7 @@ namespace ICD.Common.Utils
builder.Append(VERTICAL);
}
#if !SIMPLSHARP
#if NETSTANDARD
builder.Append(VERTICAL);
#endif
@@ -309,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
@@ -326,7 +324,7 @@ namespace ICD.Common.Utils
builder.Append(INTERSECT);
}
#if !SIMPLSHARP
#if NETSTANDARD
builder.Append(VERTICAL_LEFT);
#endif

View File

@@ -1,166 +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>
///
/// </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
{
}
}
}

Some files were not shown because too many files have changed in this diff Show More