Compare commits

...

12 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
14 changed files with 1427 additions and 10 deletions

View File

@@ -6,6 +6,16 @@ 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.

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

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

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

@@ -26,7 +26,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft"/>
<PackageReference Condition="'$(TargetFramework)' == 'net472'" Include="Newtonsoft.Json" Version="13.0.1" Aliases="RealNewtonsoft" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />

View File

@@ -0,0 +1,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,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

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

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

View File

@@ -79,11 +79,13 @@
<Compile Include="Attributes\AbstractIcdAttribute.cs" />
<Compile Include="Attributes\IIcdAttribute.cs" />
<Compile Include="Attributes\RangeAttribute.cs" />
<Compile Include="BigEndianBitConverter.cs" />
<Compile Include="Collections\BiDictionary.cs" />
<Compile Include="Collections\IcdOrderedDictionary.cs" />
<Compile Include="Collections\IcdSortedDictionary.cs" />
<Compile Include="Collections\INotifyCollectionChanged.cs" />
<Compile Include="Collections\PriorityQueue.cs" />
<Compile Include="Collections\ReverseLookupDictionary.cs" />
<Compile Include="Collections\WeakKeyDictionary.cs" />
<Compile Include="Comparers\FileNameEqualityComparer.cs" />
<Compile Include="Comparers\PredicateComparer.cs" />
@@ -240,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" />

View File

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

View File

@@ -1,4 +1,4 @@
using System;
using System;
#if SIMPLSHARP
using Crestron.SimplSharp;
#else
@@ -12,8 +12,10 @@ namespace ICD.Common.Utils
/// 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
public sealed class SafeMutex : IDisposable
{
private bool _disposedValue;
#if SIMPLSHARP
private readonly CMutex m_Mutex;
#else
@@ -21,8 +23,8 @@ namespace ICD.Common.Utils
#endif
/// <summary>
/// Constructor.
/// </summary>
/// Initializes a new instance of the <see cref="SafeMutex"/> class.
/// </summary>
public SafeMutex()
{
#if SIMPLSHARP
@@ -32,6 +34,19 @@ namespace ICD.Common.Utils
#endif
}
/// <summary>
/// Initializes a new instance of the <see cref="SafeMutex"/> class.
/// </summary>
/// <inheritdoc cref="System.Threading.Mutex.Mutex(bool)"/>
public SafeMutex(bool initiallyOwned)
{
#if SIMPLSHARP
m_Mutex = new CMutex(initiallyOwned);
#else
m_Mutex = new Mutex(initiallyOwned);
#endif
}
#region Methods
/// <summary>
@@ -46,7 +61,7 @@ namespace ICD.Common.Utils
#if SIMPLSHARP
return m_Mutex.WaitForMutex(timeout);
#else
return m_Mutex.WaitOne(timeout);
return m_Mutex.WaitOne(timeout);
#endif
}
catch (ObjectDisposedException)
@@ -71,5 +86,42 @@ namespace ICD.Common.Utils
}
#endregion
#region IDisposable
private void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// dispose managed state (managed objects)
try
{
// disposing of a mutex automatically releases it. Match that behavior
m_mutex.ReleaseMutex();
m_Mutex.Dispose();
}
catch (ObjectDisposedException)
{
// Releasing a disposed mutex in this case is valid behaviour
}
}
// free unmanaged resources (unmanaged objects) and override finalizer
// set large fields to null
_disposedValue = true;
}
}
/// <inheritdoc/>
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
#endregion
}
}

View File

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

View File

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