diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..721442b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "sonarlint.connectedMode.project": { + "connectionId": "dgmjr-io", + "projectKey": "dgmjr-io_Primitives" + } +} diff --git a/Tests/Dgmjr.Primitives.Tests.csproj b/Tests/Dgmjr.Primitives.Tests.csproj index 1e675b1..04c4e48 100644 --- a/Tests/Dgmjr.Primitives.Tests.csproj +++ b/Tests/Dgmjr.Primitives.Tests.csproj @@ -22,7 +22,7 @@ - + diff --git a/Tests/Dgmjr.Primitives.Tests.sln b/Tests/Dgmjr.Primitives.Tests.sln index 6cba4f4..3088077 100644 --- a/Tests/Dgmjr.Primitives.Tests.sln +++ b/Tests/Dgmjr.Primitives.Tests.sln @@ -8,9 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\..\..\..\Packages\Versions.Local.props = ..\..\..\..\Packages\Versions.Local.props EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dgmjr.Primitives", "..\src\Dgmjr.Primitives.csproj", "{4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dgmjr.Primitives.Tests", "Dgmjr.Primitives.Tests.csproj", "{366CE85C-81D5-438D-B208-FC32FF29AB5A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dgmjr.Primitives.Tests", "Dgmjr.Primitives.Tests.csproj", "{7B428690-5BC1-48AE-BE92-795E8E67A8E5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -22,30 +20,18 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Local|Any CPU.ActiveCfg = Local|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Local|Any CPU.Build.0 = Local|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Testing|Any CPU.ActiveCfg = Testing|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Testing|Any CPU.Build.0 = Testing|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Staging|Any CPU.ActiveCfg = Staging|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Staging|Any CPU.Build.0 = Staging|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Production|Any CPU.ActiveCfg = Local|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Production|Any CPU.Build.0 = Local|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4E1F4A52-7F97-43E1-AAA6-2EEEE07326E6}.Release|Any CPU.Build.0 = Release|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Local|Any CPU.ActiveCfg = Local|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Local|Any CPU.Build.0 = Local|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Testing|Any CPU.ActiveCfg = Testing|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Testing|Any CPU.Build.0 = Testing|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Staging|Any CPU.ActiveCfg = Staging|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Staging|Any CPU.Build.0 = Staging|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Production|Any CPU.ActiveCfg = Local|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Production|Any CPU.Build.0 = Local|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {366CE85C-81D5-438D-B208-FC32FF29AB5A}.Release|Any CPU.Build.0 = Release|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Local|Any CPU.ActiveCfg = Local|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Local|Any CPU.Build.0 = Local|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Testing|Any CPU.ActiveCfg = Testing|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Testing|Any CPU.Build.0 = Testing|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Staging|Any CPU.ActiveCfg = Staging|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Staging|Any CPU.Build.0 = Staging|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Production|Any CPU.ActiveCfg = Local|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Production|Any CPU.Build.0 = Local|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B428690-5BC1-48AE-BE92-795E8E67A8E5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Int24.cs b/src/Int24.cs index 539522d..335e21a 100644 --- a/src/Int24.cs +++ b/src/Int24.cs @@ -1,393 +1,1539 @@ +//****************************************************************************************************** +// Int24.cs - Gbtc +// +// Copyright © 2012, Grid Protection Alliance. All Rights Reserved. +// +// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See +// the NOTICE file distributed with this work for additional information regarding copyright ownership. +// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may +// not use this file except in compliance with the License. You may obtain a copy of the License at: +// +// http://www.opensource.org/licenses/MIT +// +// Unless agreed to in writing, the subject software distributed under the License is distributed on an +// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the +// License for the specific language governing permissions and limitations. +// +// Code Modification History: +// ---------------------------------------------------------------------------------------------------- +// 11/12/2004 - J. Ritchie Carroll +// Initial version of source generated. +// 08/3/2009 - Josh L. Patterson +// Updated comments. +// 08/11/2009 - Josh L. Patterson +// Updated comments. +// 09/14/2009 - Stephen C. Wills +// Added new header and license agreement. +// 12/14/2012 - Starlynn Danyelle Gilliam +// Modified Header. +// +//****************************************************************************************************** + +#region [ Contributor License Agreements ] + +/**************************************************************************\ + Copyright © 2009 - J. Ritchie Carroll + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\**************************************************************************/ + +#endregion + using System; +using System.ComponentModel; +using System.Globalization; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace System { - /// - /// Represents a 24-bit integer. - /// - [StructLayout(LayoutKind.Explicit, Size = 3)] - public readonly struct Int24 : IEquatable, IComparable, IFormattable, IConvertible -#if NE7_0_OR_GREATER - , - IAdditionOperators, - ISubtractionOperators, - IMultiplicationOperators, - IDivisionOperators, - IUnaryNegationOperators, - IBinaryIntegerOperators, - IComparisonOperators, - IMinMaxValue, - IIncrementOperators, - IDecrementOperators -#endif + /// Represents a 3-byte, 24-bit signed integer. + /// + /// + /// This class behaves like most other intrinsic signed integers but allows a 3-byte, 24-bit integer implementation + /// that is often found in many digital-signal processing arenas and different kinds of protocol parsing. A signed + /// 24-bit integer is typically used to save storage space on disk where its value range of -8388608 to 8388607 is + /// sufficient, but the signed Int16 value range of -32768 to 32767 is too small. + /// + /// + /// This structure uses an Int32 internally for storage and most other common expected integer functionality, so using + /// a 24-bit integer will not save memory. However, if the 24-bit signed integer range (-8388608 to 8388607) suits your + /// data needs you can save disk space by only storing the three bytes that this integer actually consumes. You can do + /// this by calling the Int24.GetBytes function to return a three byte binary array that can be serialized to the desired + /// destination and then calling the Int24.GetValue function to restore the Int24 value from those three bytes. + /// + /// + /// All the standard operators for the Int24 have been fully defined for use with both Int24 and Int32 signed integers; + /// you should find that without the exception Int24 can be compared and numerically calculated with an Int24 or Int32. + /// Necessary casting should be minimal and typical use should be very simple - just as if you are using any other native + /// signed integer. + /// + /// + [Serializable] + public struct Int24 + : IComparable, + IFormattable, + IConvertible, + IComparable, + IComparable, + IEquatable, + IEquatable { + #region [ Members ] + + // Constants + private const int MaxValue32 = 8388607; // Represents the largest possible value of an Int24 as an Int32. + private const int MinValue32 = -8388608; // Represents the smallest possible value of an Int24 as an Int32. + + /// High byte bit-mask used when a 24-bit integer is stored within a 32-bit integer. This field is constant. + public const int BitMask = -16777216; + + // Fields + private readonly int m_value; // We internally store the Int24 value in a 4-byte integer for convenience + #endregion + + #region [ Constructors ] + + /// Creates 24-bit signed integer from an existing 24-bit signed integer. + /// 24-but signed integer to create new Int24 from. + public Int24(Int24 value) + { + m_value = ApplyBitMask((int)value); + } + + /// Creates 24-bit signed integer from a 32-bit signed integer. + /// 32-bit signed integer to use as new 24-bit signed integer value. + /// Source values outside 24-bit min/max range will cause an overflow exception. + public Int24(int value) + { + ValidateNumericRange(value); + m_value = ApplyBitMask(value); + } + + /// Creates 24-bit signed integer from three bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within . + /// + /// You can use this constructor in-lieu of a System.BitConverter.ToInt24 function. + /// Bytes endian order assumed to match that of currently executing process architecture (little-endian on Intel platforms). + /// + /// cannot be null. + /// is greater than length. + /// length from is too small to represent a . + public Int24(byte[] value, int startIndex) + { + m_value = GetValue(value, startIndex).m_value; + } + + #endregion + + #region [ Methods ] + + /// Returns the Int24 value as an array of three bytes. + /// An array of bytes with length 3. + /// + /// You can use this function in-lieu of a System.BitConverter.GetBytes function. + /// Bytes will be returned in endian order of currently executing process architecture (little-endian on Intel platforms). + /// + public byte[] GetBytes() + { + // Return serialized 3-byte representation of Int24 + return GetBytes(this); + } + /// - /// The bit offset of the integer. + /// Compares this instance to a specified object and returns an indication of their relative values. /// - private const int BitOffset = 8; + /// An object to compare, or null. + /// + /// A signed number indicating the relative values of this instance and value. Returns less than zero + /// if this instance is less than value, zero if this instance is equal to value, or greater than zero + /// if this instance is greater than value. + /// + /// value is not an Int32 or Int24. + public int CompareTo(object value) + { + if ((object)value == null) + return 1; + + if (!(value is int) && !(value is Int24)) + throw new ArgumentException("Argument must be an Int32 or an Int24"); + + int num = (int)value; + return (m_value < num ? -1 : (m_value > num ? 1 : 0)); + } /// - /// The size of the bits in the integer. + /// Compares this instance to a specified 24-bit signed integer and returns an indication of their + /// relative values. /// - public const int Size = 24; + /// An integer to compare. + /// + /// A signed number indicating the relative values of this instance and value. Returns less than zero + /// if this instance is less than value, zero if this instance is equal to value, or greater than zero + /// if this instance is greater than value. + /// + public int CompareTo(Int24 value) + { + return CompareTo((int)value); + } /// - /// The zero mask for the integer. + /// Compares this instance to a specified 32-bit signed integer and returns an indication of their + /// relative values. /// - public const uint Zero = 0x00800000; + /// An integer to compare. + /// + /// A signed number indicating the relative values of this instance and value. Returns less than zero + /// if this instance is less than value, zero if this instance is equal to value, or greater than zero + /// if this instance is greater than value. + /// + public int CompareTo(int value) + { + return (m_value < value ? -1 : (m_value > value ? 1 : 0)); + } /// - /// The mask for negative sign. + /// Returns a value indicating whether this instance is equal to a specified object. /// - private const uint NegativeSignMask = 0xFF000000; + /// An object to compare, or null. + /// + /// True if obj is an instance of Int32 or Int24 and equals the value of this instance; + /// otherwise, False. + /// + public override bool Equals(object obj) + { + if (obj is int || obj is Int24) + return Equals((int)obj); + return false; + } /// - /// The mask for positive sign. - /// - private const uint PositiveSignMask = 0x00FFFFFF; - - [FieldOffset(0)] - private readonly byte _b0; - - [FieldOffset(1)] - private readonly byte _b1; + /// Returns a value indicating whether this instance is equal to a specified Int24 value. + /// + /// An Int24 value to compare to this instance. + /// + /// True if obj has the same value as this instance; otherwise, False. + /// + public bool Equals(Int24 obj) + { + return Equals((int)obj); + } - [FieldOffset(2)] - private readonly byte _b2; + /// + /// Returns a value indicating whether this instance is equal to a specified Int32 value. + /// + /// An Int32 value to compare to this instance. + /// + /// True if obj has the same value as this instance; otherwise, False. + /// + public bool Equals(int obj) + { + return (m_value == obj); + } - private readonly byte[] Bytes => [_b0, _b1, _b2]; + /// + /// Returns the hash code for this instance. + /// + /// + /// A 32-bit signed integer hash code. + /// + public override int GetHashCode() + { + return m_value; + } - public static implicit operator uint(i24 value) => - (uint)value._b0 | (uint)(value._b1 >> BitOffset) | (uint)(value._b2 >> BitOffset * 2); + /// + /// Converts the numeric value of this instance to its equivalent string representation. + /// + /// + /// The string representation of the value of this instance, consisting of a minus sign if + /// the value is negative, and a sequence of digits ranging from 0 to 9 with no leading zeroes. + /// + public override string ToString() + { + return m_value.ToString(); + } - public static implicit operator int(i24 value) => - (int)value._b0 | ((int)value._b1 >> BitOffset) | ((int)value._b2 >> BitOffset * 2); + /// + /// Converts the numeric value of this instance to its equivalent string representation, using + /// the specified format. + /// + /// A format string. + /// + /// The string representation of the value of this instance as specified by format. + /// + public string ToString(string format) + { + return m_value.ToString(format); + } - // /// - // /// Gets the value of the integer. - // /// - // public i24 Value => this; + /// + /// Converts the numeric value of this instance to its equivalent string representation using the + /// specified culture-specific format information. + /// + /// + /// A that supplies culture-specific formatting information. + /// + /// + /// The string representation of the value of this instance as specified by provider. + /// + public string ToString(IFormatProvider provider) + { + return m_value.ToString(provider); + } /// - /// Initializes a new instance of the i24 struct with three bytes. + /// Converts the numeric value of this instance to its equivalent string representation using the + /// specified format and culture-specific format information. /// - /// The first byte. - /// The second byte. - /// The third byte. - public Int24(byte b0, byte b1, byte b2) + /// A format specification. + /// + /// A that supplies culture-specific formatting information. + /// + /// + /// The string representation of the value of this instance as specified by format and provider. + /// + public string ToString(string format, IFormatProvider provider) { - _b0 = b0; - _b1 = b1; - _b2 = b2; + return m_value.ToString(format, provider); } + /// - /// Initializes a new instance of the i24 struct with three bytes. + /// Converts the string representation of a number to its 24-bit signed integer equivalent. /// - /// The array of three bytes holding the int24 value. - public Int24(byte[] bytes) : this(bytes[0], bytes[1], bytes[2]) { } + /// A string containing a number to convert. + /// + /// A 24-bit signed integer equivalent to the number contained in s. + /// + /// s is null. + /// + /// s represents a number less than Int24.MinValue or greater than Int24.MaxValue. + /// + /// s is not in the correct format. + public static Int24 Parse(string s) + { + return (Int24)int.Parse(s); + } /// - /// Initializes a new instance of the i24 struct with a signed byte value. + /// Converts the string representation of a number in a specified style to its 24-bit signed integer equivalent. /// - /// The signed byte value. - public Int24(sbyte value) : this((byte)value, 0x0, 0x0) { } + /// A string containing a number to convert. + /// + /// A bitwise combination of System.Globalization.NumberStyles values that indicates the permitted format of s. + /// A typical value to specify is System.Globalization.NumberStyles.Integer. + /// + /// + /// A 24-bit signed integer equivalent to the number contained in s. + /// + /// + /// style is not a System.Globalization.NumberStyles value. -or- style is not a combination of + /// System.Globalization.NumberStyles.AllowHexSpecifier and System.Globalization.NumberStyles.HexNumber values. + /// + /// s is null. + /// + /// s represents a number less than Int24.MinValue or greater than Int24.MaxValue. + /// + /// s is not in a format compliant with style. + public static Int24 Parse(string s, NumberStyles style) + { + return (Int24)int.Parse(s, style); + } /// - /// Initializes a new instance of the i24 struct with a short integer value. + /// Converts the string representation of a number in a specified culture-specific format to its 24-bit + /// signed integer equivalent. /// - ///The short integer value. - public Int24(short value) : this((byte)(value & 0xFF), (byte)(value >> BitOffset), 0x0) { } + /// A string containing a number to convert. + /// + /// A that supplies culture-specific formatting information about s. + /// + /// + /// A 24-bit signed integer equivalent to the number contained in s. + /// + /// s is null. + /// + /// s represents a number less than Int24.MinValue or greater than Int24.MaxValue. + /// + /// s is not in the correct format. + public static Int24 Parse(string s, IFormatProvider provider) + { + return (Int24)int.Parse(s, provider); + } /// - /// Initializes a new instance of the i24 struct with an integer value. + /// Converts the string representation of a number in a specified style and culture-specific format to its 24-bit + /// signed integer equivalent. /// - ///The integer value. - public Int24(int value) : this((byte)(value & 0xFF), (byte)(value >> BitOffset), (byte)(value >> BitOffset * 2)) { } + /// A string containing a number to convert. + /// + /// A bitwise combination of System.Globalization.NumberStyles values that indicates the permitted format of s. + /// A typical value to specify is System.Globalization.NumberStyles.Integer. + /// + /// + /// A that supplies culture-specific formatting information about s. + /// + /// + /// A 24-bit signed integer equivalent to the number contained in s. + /// + /// + /// style is not a System.Globalization.NumberStyles value. -or- style is not a combination of + /// System.Globalization.NumberStyles.AllowHexSpecifier and System.Globalization.NumberStyles.HexNumber values. + /// + /// s is null. + /// + /// s represents a number less than Int24.MinValue or greater than Int24.MaxValue. + /// + /// s is not in a format compliant with style. + public static Int24 Parse(string s, NumberStyles style, IFormatProvider provider) + { + return (Int24)int.Parse(s, style, provider); + } /// - /// Initializes a new instance of the i24 struct with an unsigned integer value. + /// Converts the string representation of a number to its 24-bit signed integer equivalent. A return value + /// indicates whether the conversion succeeded or failed. /// - /// The unsigned integer value. - public Int24(uint value) + /// A string containing a number to convert. + /// + /// When this method returns, contains the 24-bit signed integer value equivalent to the number contained in s, + /// if the conversion succeeded, or zero if the conversion failed. The conversion fails if the s parameter is null, + /// is not of the correct format, or represents a number less than Int24.MinValue or greater than Int24.MaxValue. + /// This parameter is passed uninitialized. + /// + /// true if s was converted successfully; otherwise, false. + public static bool TryParse(string s, out Int24 result) { - if (value > (1 << Size - 1) - 1) + int parseResult; + bool parseResponse; + + parseResponse = int.TryParse(s, out parseResult); + + try + { + result = (Int24)parseResult; + } + catch { - throw new ArgumentOutOfRangeException(nameof(value), "Value too large for i24"); + result = (Int24)0; + parseResponse = false; } - _b0 = (byte)(value & 0xFF); - _b1 = (byte)(value >> BitOffset); - _b2 = (byte)(value >> BitOffset * 2); + return parseResponse; } /// - /// Initializes a new instance of the i24 struct with a read-only span of bytes. + /// Converts the string representation of a number in a specified style and culture-specific format to its + /// 24-bit signed integer equivalent. A return value indicates whether the conversion succeeded or failed. /// - /// The read-only span of bytes. - public Int24(ReadOnlySpan bytes) + /// A string containing a number to convert. + /// + /// A bitwise combination of System.Globalization.NumberStyles values that indicates the permitted format of s. + /// A typical value to specify is System.Globalization.NumberStyles.Integer. + /// + /// + /// When this method returns, contains the 24-bit signed integer value equivalent to the number contained in s, + /// if the conversion succeeded, or zero if the conversion failed. The conversion fails if the s parameter is null, + /// is not in a format compliant with style, or represents a number less than Int24.MinValue or greater than + /// Int24.MaxValue. This parameter is passed uninitialized. + /// + /// + /// A object that supplies culture-specific formatting information about s. + /// + /// true if s was converted successfully; otherwise, false. + /// + /// style is not a System.Globalization.NumberStyles value. -or- style is not a combination of + /// System.Globalization.NumberStyles.AllowHexSpecifier and System.Globalization.NumberStyles.HexNumber values. + /// + public static bool TryParse( + string s, + NumberStyles style, + IFormatProvider provider, + out Int24 result + ) { - if (bytes.Length != 3) + int parseResult; + bool parseResponse; + + parseResponse = int.TryParse(s, style, provider, out parseResult); + + try { - throw new ArgumentException( - "Bytes span must be exactly 3 bytes long", - nameof(bytes) - ); + result = (Int24)parseResult; + } + catch + { + result = (Int24)0; + parseResponse = false; } - _b0 = bytes[0]; - _b1 = bytes[1]; - _b2 = bytes[2]; + return parseResponse; } /// - /// Gets or sets the byte at the specified index. + /// Returns the System.TypeCode for value type System.Int32 (there is no defined type code for an Int24). /// - /// The zero-based index of the byte to get or set. - public byte this[int index] + /// The enumerated constant, System.TypeCode.Int32. + /// + /// There is no defined Int24 type code and since an Int24 will easily fit inside an Int32, the + /// Int32 type code is returned. + /// + public TypeCode GetTypeCode() { - get - { - return index switch - { - 0 => _b0, - 1 => _b1, - 2 => _b2, - _ => throw new IndexOutOfRangeException() - }; - } + return TypeCode.Int32; + } + + #region [ Explicit IConvertible Implementation ] + + // These are explicitly implemented on the native integer implementations, so we do the same... + + bool IConvertible.ToBoolean(IFormatProvider provider) + { + return Convert.ToBoolean(m_value, provider); + } + + char IConvertible.ToChar(IFormatProvider provider) + { + return Convert.ToChar(m_value, provider); + } + + sbyte IConvertible.ToSByte(IFormatProvider provider) + { + return Convert.ToSByte(m_value, provider); + } + + byte IConvertible.ToByte(IFormatProvider provider) + { + return Convert.ToByte(m_value, provider); + } + + short IConvertible.ToInt16(IFormatProvider provider) + { + return Convert.ToInt16(m_value, provider); + } + + ushort IConvertible.ToUInt16(IFormatProvider provider) + { + return Convert.ToUInt16(m_value, provider); + } + + int IConvertible.ToInt32(IFormatProvider provider) + { + return m_value; + } + + uint IConvertible.ToUInt32(IFormatProvider provider) + { + return Convert.ToUInt32(m_value, provider); + } + + long IConvertible.ToInt64(IFormatProvider provider) + { + return Convert.ToInt64(m_value, provider); + } + + ulong IConvertible.ToUInt64(IFormatProvider provider) + { + return Convert.ToUInt64(m_value, provider); + } + + float IConvertible.ToSingle(IFormatProvider provider) + { + return Convert.ToSingle(m_value, provider); + } + + double IConvertible.ToDouble(IFormatProvider provider) + { + return Convert.ToDouble(m_value, provider); } + decimal IConvertible.ToDecimal(IFormatProvider provider) + { + return Convert.ToDecimal(m_value, provider); + } + + DateTime IConvertible.ToDateTime(IFormatProvider provider) + { + return Convert.ToDateTime(m_value, provider); + } + + object IConvertible.ToType(Type type, IFormatProvider provider) + { + return Convert.ChangeType(m_value, type, provider); + } + + #endregion + + #endregion + + #region [ Operators ] + + // Every effort has been made to make Int24 as cleanly interoperable with Int32 as possible... + + #region [ Comparison Operators ] + /// - /// Compares this instance to another i24 instance and returns an indication of their relative values. + /// Compares the two values for equality. /// - /// An object to compare with this instance. - public int CompareTo(i24 other) + /// Left hand operand. + /// Right hand operand. + /// Boolean value indicating equality. + public static bool operator ==(Int24 value1, Int24 value2) { - int thisValue = SignExtend(); - int otherValue = other.SignExtend(); - return thisValue.CompareTo(otherValue); + return value1.Equals(value2); } /// - /// Determines whether this instance and another specified i24 object have the same value. + /// Compares the two values for equality. /// - /// The i24 to compare to this instance. - /// true if the value of the other parameter is the same as the value of this instance; otherwise, false. - public bool Equals(i24 other) + /// Left hand operand. + /// Right hand operand. + /// Boolean value indicating equality. + public static bool operator ==(int value1, Int24 value2) { - return SignExtend() == other.SignExtend(); + return value1.Equals((int)value2); } /// - /// Determines whether this instance and a specified object, which must also be an i24 object, have the same value. + /// Compares the two values for equality. /// - /// The object to compare to this instance. - /// true if obj is an i24 and its value is the same as this instance; otherwise, false. If obj is null, the method returns false. - public override bool Equals(object? obj) + /// Left hand operand. + /// Right hand operand. + /// Boolean value indicating equality. + public static bool operator ==(Int24 value1, int value2) { - return obj is i24 other && Equals(other); + return ((int)value1).Equals(value2); } /// - /// Sign extends the current i24 struct. + /// Compares the two values for inequality. /// - /// A new i24 struct with sign extension applied. - public i24 SignExtend() + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating the result of the inequality. + public static bool operator !=(Int24 value1, Int24 value2) { - if ((this & Zero) != 0) - { - // Negative number: fill upper 8 bits with 1's - return unchecked ((i24)(int)(this | NegativeSignMask)); - } - // Positive number: fill upper 8 bits with 0's - return (i24)(int)(this & PositiveSignMask); + return !value1.Equals(value2); } /// - /// Returns a hash code for this instance. + /// Compares the two values for inequality. /// - ///A hash code for the current object. - public override int GetHashCode() + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating the result of the inequality. + public static bool operator !=(int value1, Int24 value2) { - return SignExtend().GetHashCode(); + return !value1.Equals((int)value2); } /// - /// Converts the numeric value of this instance to its equivalent string representation. + /// Compares the two values for inequality. /// - ///The string representation of the value of this instance. - public override string ToString() + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating the result of the inequality. + public static bool operator !=(Int24 value1, int value2) { - return SignExtend().ToString(); + return !((int)value1).Equals(value2); } /// - /// Converts the numeric value of this instance to its equivalent string representation using specified format and culture-specific format information. - /// - ///A standard or custom numeric format string. - ///An object that supplies culture-specific formatting information. - ///The string representation of the value of this instance as specified by format and provider parameters. - public string ToString(string? format, IFormatProvider? formatProvider) + /// Returns true if left value is less than right value. + /// + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was less than the right value. + public static bool operator <(Int24 value1, Int24 value2) { - return SignExtend().ToString(format, formatProvider); + return (value1.CompareTo(value2) < 0); } - private const int Int32MinValue = -8388608; - private const int Int32MaxValue = 8388607; + /// + /// Returns true if left value is less than right value. + /// + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was less than the right value. + public static bool operator <(int value1, Int24 value2) + { + return (value1.CompareTo((int)value2) < 0); + } - // Implementations of IComparable, IComparable, IEquatable, and IConvertible - // ... + /// + /// Returns true if left value is less than right value. + /// + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was less than the right value. + public static bool operator <(Int24 value1, int value2) + { + return (value1.CompareTo(value2) < 0); + } /// - /// Converts the value of this instance to a 32-bit signed integer. + /// Returns true if left value is less or equal to than right value. /// - ///A 32-bit signed integer equal to the value of this instance. - public int ToInt32() + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was less than the right value. + public static bool operator <=(Int24 value1, Int24 value2) { - int result = _b0; - result |= _b1 << 8; - result |= (_b2 & 0x7F) << 16; + return (value1.CompareTo(value2) <= 0); + } - if ((_b2 & 0x80) == 0x80) - { - result |= 0xFF << 23; - } + /// + /// Returns true if left value is less or equal to than right value. + /// + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was less than the right value. + public static bool operator <=(int value1, Int24 value2) + { + return (value1.CompareTo((int)value2) <= 0); + } - return result; + /// + /// Returns true if left value is less or equal to than right value. + /// + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was less than the right value. + public static bool operator <=(Int24 value1, int value2) + { + return (value1.CompareTo(value2) <= 0); } - private const int Bits = 24; - private const int SignBit = 1 << (Bits - 1); - private const int MaxValueWithoutSignBit = SignBit - 1; + /// + /// Returns true if left value is greater than right value. + /// + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was greater than the right value. + public static bool operator >(Int24 value1, Int24 value2) + { + return (value1.CompareTo(value2) > 0); + } /// - /// Implicitly converts an i24 struct to an integer. + /// Returns true if left value is greater than right value. /// - ///The i24 struct to convert. - public static implicit operator i24(int value) => new(value); + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was greater than the right value. + public static bool operator >(int value1, Int24 value2) + { + return (value1.CompareTo((int)value2) > 0); + } /// - /// Determines whether two specified i24 objects have the same value. + /// Returns true if left value is greater than right value. /// - /// The first i24 to compare. - /// The second i24 to compare. - /// true if the value of left is the same as the value of right; otherwise, false. - public static bool operator ==(i24 left, i24 right) => left.Equals(right); + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was greater than the right value. + public static bool operator >(Int24 value1, int value2) + { + return (value1.CompareTo(value2) > 0); + } /// - /// Determines whether two specified i24 objects have different values. + /// Returns true if left value is greater than or equal to right value. /// - /// The first i24 to compare. - /// The second i24 to compare. - /// true if the value of left is different from the value of right; otherwise, false. - public static bool operator !=(i24 left, i24 right) => !left.Equals(right); + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was greater than or equal to the right value. + public static bool operator >=(Int24 value1, Int24 value2) + { + return (value1.CompareTo(value2) >= 0); + } /// - /// Determines whether one specified i24 is less than another specified i24. + /// Returns true if left value is greater than or equal to right value. /// - ///The first object to compare. - ///The second object to compare. - ///true if left is less than right; otherwise, false. - public static bool operator <(i24 left, i24 right) => left.CompareTo(right) < 0; + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was greater than or equal to the right value. + public static bool operator >=(int value1, Int24 value2) + { + return (value1.CompareTo((int)value2) >= 0); + } /// - /// Determines whether one specified i24 is greater than another specified i24. + /// Returns true if left value is greater than or equal to right value. /// - ///The first object to compare. - ///The second object to compare. - ///true if left is greater than right; otherwise, false. - public static bool operator >(i24 left, i24 right) => left.CompareTo(right) > 0; + /// Left hand operand. + /// Right hand operand. + /// Boolean indicating whether the left value was greater than or equal to the right value. + public static bool operator >=(Int24 value1, int value2) + { + return (value1.CompareTo(value2) >= 0); + } + + #endregion + + #region [ Type Conversion Operators ] + + #region [ Explicit Narrowing Conversions ] /// - /// Determines whether one specified i24 is less than or equal to another specified int. - /// - ///The first object to compare. - ///The second object to compare. - ///true if left is less than or equal to right; otherwise, false. - public static bool operator <=(i24 left, i24? right) + /// Explicitly converts value to an . + /// + /// Enum value that is converted. + /// Int24 + public static explicit operator Int24(Enum value) { - return !(left > right); + return new Int24(Convert.ToInt32(value)); } /// - /// Determines whether one specified i24 is greater than or equal to another specified int. - /// - ///The first object to compare. - ///The second object to compare. - ///true if left is greater than or equal to right; otherwise, false. - public static bool operator >=(i24 left, i24? right) + /// Explicitly converts value to an . + /// + /// String value that is converted. + /// Int24 + public static explicit operator Int24(string value) { - return !(left < right); + return new Int24(Convert.ToInt32(value)); } /// - /// Returns the TypeCode for this instance. + /// Explicitly converts value to an . /// - ///The enumerated constant that is the TypeCode of the class or value type that implements this interface. - public TypeCode GetTypeCode() => TypeCode.Int32; + /// Decimal value that is converted. + /// Int24 + public static explicit operator Int24(decimal value) + { + return new Int24(Convert.ToInt32(value)); + } - /// The maximum value that can be assigned to an instance of this type. - /// A constant equal to 8388607. - /// 8388607 - public static readonly i24 MaxValue = new(Int32MinValue); + /// + /// Explicitly converts value to an . + /// + /// Double value that is converted. + /// Int24 + public static explicit operator Int24(double value) + { + return new Int24(Convert.ToInt32(value)); + } /// - /// The minimum value that can be assigned to an instance of this type. + /// Explicitly converts value to an . /// - /// A constant equal to -8388608. - /// -8388608 - public static readonly i24 MinValue = new(Int32MaxValue); + /// Float value that is converted. + /// Int24 + public static explicit operator Int24(float value) + { + return new Int24(Convert.ToInt32(value)); + } - public static explicit operator i24(sbyte value) => new(value); + /// + /// Explicitly converts value to an . + /// + /// Long value that is converted. + /// Int24 + public static explicit operator Int24(long value) + { + return new Int24(Convert.ToInt32(value)); + } - public int ToInt32(IFormatProvider? formatProvider) => ToInt32(); + /// + /// Explicitly converts value to an . + /// + /// Integer value that is converted. + /// Int24 + public static explicit operator Int24(int value) + { + return new Int24(value); + } - public uint ToUInt32(IFormatProvider? formatProvider) => (uint)ToInt32(); + /// + /// Explicitly converts to . + /// + /// Int24 value that is converted. + /// Short + public static explicit operator short(Int24 value) + { + return (short)((int)value); + } - public long ToInt64(IFormatProvider? formatProvider) => (long)ToInt32(); + /// + /// Explicitly converts to . + /// + /// Int24 value that is converted. + /// Unsigned Short + public static explicit operator ushort(Int24 value) + { + return (ushort)((uint)value); + } - public ulong ToUInt64(IFormatProvider? formatProvider) => (ulong)ToInt32(); + /// + /// Explicitly converts to . + /// + /// Int24 value that is converted. + /// Byte + public static explicit operator byte(Int24 value) + { + return (byte)((int)value); + } - public short ToInt16(IFormatProvider? formatProvider) => (short)ToInt32(); + #endregion - public ushort ToUInt16(IFormatProvider? formatProvider) => (ushort)ToInt32(); + #region [ Implicit Widening Conversions ] -#if NET7_0_OR_GREATER - public Int128 ToInt128(IFormatProvider? formatProvider) => (Int128)ToInt32(); + /// + /// Implicitly converts value to an . + /// + /// Byte value that is converted to an . + /// An value. + public static implicit operator Int24(byte value) + { + return new Int24((int)value); + } - public UInt128 ToUInt128(IFormatProvider? formatProvider) => (UInt128)ToInt32(); -#endif + /// + /// Implicitly converts value to an . + /// + /// Char value that is converted to an . + /// An value. + public static implicit operator Int24(char value) + { + return new Int24((int)value); + } - public byte ToByte(IFormatProvider? formatProvider) => (byte)ToInt32(); + /// + /// Implicitly converts value to an . + /// + /// Short value that is converted to an . + /// An value. + public static implicit operator Int24(short value) + { + return new Int24((int)value); + } - public sbyte ToSByte(IFormatProvider? formatProvider) => (sbyte)ToInt32(); + /// + /// Implicitly converts to . + /// + /// value that is converted to an . + /// An value. + public static implicit operator int(Int24 value) + { + return ((IConvertible)value).ToInt32(null); + } - public Single ToSingle(IFormatProvider? formatProvider) => (Single)ToInt32(); + /// + /// Implicitly converts to . + /// + /// value that is converted to an unsigned integer. + /// Unsigned integer + public static implicit operator uint(Int24 value) + { + return ((IConvertible)value).ToUInt32(null); + } - public double ToDouble(IFormatProvider? formatProvider) => (double)ToInt32(); + /// + /// Implicitly converts to . + /// + /// value that is converted to an . + /// An value. + public static implicit operator long(Int24 value) + { + return ((IConvertible)value).ToInt64(null); + } - public decimal ToDecimal(IFormatProvider? formatProvider) => (decimal)ToInt32(); + /// + /// Implicitly converts to . + /// + /// value that is converted to an . + /// An value. + public static implicit operator ulong(Int24 value) + { + return ((IConvertible)value).ToUInt64(null); + } - public char ToChar(IFormatProvider? formatProvider) => (char)ToInt32(); + /// + /// Implicitly converts to . + /// + /// value that is converted to an . + /// A value. + public static implicit operator double(Int24 value) + { + return ((IConvertible)value).ToDouble(null); + } - public DateTime ToDateTime(IFormatProvider? formatProvider) => - DateTime.FromBinary((long)ToInt32()); + /// + /// Implicitly converts to . + /// + /// value that is converted to an . + /// A value. + public static implicit operator float(Int24 value) + { + return ((IConvertible)value).ToSingle(null); + } - public bool ToBoolean(IFormatProvider? formatProvider) => ToInt32() > 0; + /// + /// Implicitly converts to . + /// + /// value that is converted to an . + /// A value. + public static implicit operator decimal(Int24 value) + { + return ((IConvertible)value).ToDecimal(null); + } - public object ToType(Type conversionType, IFormatProvider? formatProvider) => - Convert.ChangeType(ToInt32(), conversionType, formatProvider); + /// + /// Implicitly converts to . + /// + /// value that is converted to an . + /// A value. + public static implicit operator string(Int24 value) + { + return value.ToString(); + } - public string ToString(IFormatProvider? formatProvider) => - ((int)ToInt32()).ToString(formatProvider); + #endregion - public static i24 Parse(string s, Globalization.NumberStyles style = 0) => - new(int.Parse(s, style)); - } + #endregion + + #region [ Boolean and Bitwise Operators ] + + /// + /// Returns true if value is not zero. + /// + /// Int24 value to test. + /// Boolean to indicate whether the value was not equal to zero. + public static bool operator true(Int24 value) + { + return (value != 0); + } + + /// + /// Returns true if value is equal to zero. + /// + /// Int24 value to test. + /// Boolean to indicate whether the value was equal to zero. + public static bool operator false(Int24 value) + { + return (value == 0); + } + + /// + /// Returns bitwise complement of value. + /// + /// value as operand. + /// as result. + public static Int24 operator ~(Int24 value) + { + return (Int24)ApplyBitMask(~(int)value); + } + + /// + /// Returns logical bitwise AND of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Int24 as result of operation. + public static Int24 operator &(Int24 value1, Int24 value2) + { + return (Int24)ApplyBitMask((int)value1 & (int)value2); + } + + /// + /// Returns logical bitwise AND of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer as result of operation. + public static int operator &(int value1, Int24 value2) + { + return (value1 & (int)value2); + } + + /// + /// Returns logical bitwise AND of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer as result of operation. + public static int operator &(Int24 value1, int value2) + { + return ((int)value1 & value2); + } + + /// + /// Returns logical bitwise OR of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Int24 as result of operation. + public static Int24 operator |(Int24 value1, Int24 value2) + { + return (Int24)ApplyBitMask((int)value1 | (int)value2); + } + + /// + /// Returns logical bitwise OR of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer as result of operation. + public static int operator |(int value1, Int24 value2) + { + return (value1 | (int)value2); + } + + /// + /// Returns logical bitwise OR of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer as result of operation. + public static int operator |(Int24 value1, int value2) + { + return ((int)value1 | value2); + } + + /// + /// Returns logical bitwise exclusive-OR of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer value of the resulting exclusive-OR operation. + public static Int24 operator ^(Int24 value1, Int24 value2) + { + return (Int24)ApplyBitMask((int)value1 ^ (int)value2); + } + + /// + /// Returns logical bitwise exclusive-OR of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer value of the resulting exclusive-OR operation. + public static int operator ^(int value1, Int24 value2) + { + return (value1 ^ (int)value2); + } + + /// + /// Returns logical bitwise exclusive-OR of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer value of the resulting exclusive-OR operation. + public static int operator ^(Int24 value1, int value2) + { + return ((int)value1 ^ value2); + } + + /// + /// Returns value after right shifts of first value by the number of bits specified by second value. + /// + /// value to shift. + /// shifts indicates how many places to shift. + /// An value. + public static Int24 operator >>(Int24 value, int shifts) + { + return (Int24)(ApplyBitMask((int)value >> shifts)); + } + + /// + /// Returns value after left shifts of first value by the number of bits specified by second value. + /// + /// value to shift. + /// shifts indicates how many places to shift. + /// An value. + public static Int24 operator <<(Int24 value, int shifts) + { + return (Int24)(ApplyBitMask((int)value << shifts)); + } + + #endregion + + #region [ Arithmetic Operators ] + + /// + /// Returns computed remainder after dividing first value by the second. + /// + /// value as numerator. + /// value as denominator. + /// as remainder + public static Int24 operator %(Int24 value1, Int24 value2) + { + return (Int24)((int)value1 % (int)value2); + } + + /// + /// Returns computed remainder after dividing first value by the second. + /// + /// value as numerator. + /// value as denominator. + /// as remainder + public static int operator %(int value1, Int24 value2) + { + return (value1 % (int)value2); + } + + /// + /// Returns computed remainder after dividing first value by the second. + /// + /// value as numerator. + /// value as denominator. + /// as remainder + public static int operator %(Int24 value1, int value2) + { + return ((int)value1 % value2); + } + + /// + /// Returns computed sum of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Int24 result of addition. + public static Int24 operator +(Int24 value1, Int24 value2) + { + return (Int24)((int)value1 + (int)value2); + } + + /// + /// Returns computed sum of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer result of addition. + public static int operator +(int value1, Int24 value2) + { + return (value1 + (int)value2); + } + + /// + /// Returns computed sum of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer result of addition. + public static int operator +(Int24 value1, int value2) + { + return ((int)value1 + value2); + } + + /// + /// Returns computed difference of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Int24 result of subtraction. + public static Int24 operator -(Int24 value1, Int24 value2) + { + return (Int24)((int)value1 - (int)value2); + } + + /// + /// Returns computed difference of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer result of subtraction. + public static int operator -(int value1, Int24 value2) + { + return (value1 - (int)value2); + } + + /// + /// Returns computed difference of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer result of subtraction. + public static int operator -(Int24 value1, int value2) + { + return ((int)value1 - value2); + } + + /// + /// Returns incremented value. + /// + /// The operand. + /// Int24 result of increment. + public static Int24 operator ++(Int24 value) + { + return (Int24)(value + 1); + } + + /// + /// Returns decremented value. + /// + /// The operand. + /// Int24 result of decrement. + public static Int24 operator --(Int24 value) + { + return (Int24)(value - 1); + } + + /// + /// Returns computed product of values. + /// + /// value as left hand operand. + /// value as right hand operand. + /// as result + public static Int24 operator *(Int24 value1, Int24 value2) + { + return (Int24)((int)value1 * (int)value2); + } + + /// + /// Returns computed product of values. + /// + /// value as left hand operand. + /// value as right hand operand. + /// as result + public static int operator *(int value1, Int24 value2) + { + return (value1 * (int)value2); + } + + /// + /// Returns computed product of values. + /// + /// value as left hand operand. + /// value as right hand operand. + /// as result + public static int operator *(Int24 value1, int value2) + { + return ((int)value1 * value2); + } + + // Integer division operators + + /// + /// Returns computed division of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Int24 result of operation. + public static Int24 operator /(Int24 value1, Int24 value2) + { + return (Int24)((int)value1 / (int)value2); + } + + /// + /// Returns computed division of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer result of operation. + public static int operator /(int value1, Int24 value2) + { + return (value1 / (int)value2); + } + + /// + /// Returns computed division of values. + /// + /// Left hand operand. + /// Right hand operand. + /// Integer result of operation. + public static int operator /(Int24 value1, int value2) + { + return ((int)value1 / value2); + } + + //// Standard division operators + //public static double operator /(Int24 value1, Int24 value2) + //{ + // return ((double)value1 / (double)value2); + //} -#if NETSTANDARD2_0_OR_GREATER + //public static double operator /(int value1, Int24 value2) + //{ + // return ((double)value1 / (double)value2); + //} -#endif + //public static double operator /(Int24 value1, int value2) + //{ + // return ((double)value1 / (double)value2); + //} + + // C# doesn't expose an exponent operator but some other .NET languages do, + // so we expose the operator via its native special IL function name + + /// + /// Returns result of first value raised to power of second value. + /// + /// Left hand operand. + /// Right hand operand. + /// Double that is the result of the operation. + [EditorBrowsable(EditorBrowsableState.Advanced), SpecialName] + public static double op_Exponent(Int24 value1, Int24 value2) + { + return Math.Pow((double)value1, (double)value2); + } + + /// + /// Returns result of first value raised to power of second value. + /// + /// Left hand operand. + /// Right hand operand. + /// Double that is the result of the operation. + [EditorBrowsable(EditorBrowsableState.Advanced), SpecialName] + public static double op_Exponent(int value1, Int24 value2) + { + return Math.Pow((double)value1, (double)value2); + } + + /// + /// Returns result of first value raised to power of second value. + /// + /// Left hand operand. + /// Right hand operand. + /// Double that is the result of the operation. + [EditorBrowsable(EditorBrowsableState.Advanced), SpecialName] + public static double op_Exponent(Int24 value1, int value2) + { + return Math.Pow((double)value1, (double)value2); + } + + #endregion + + #endregion + + #region [ Static ] + + /// + /// Represents the largest possible value of an Int24. This field is constant. + /// + public static readonly Int24 MaxValue = (Int24)MaxValue32; + + /// + /// Represents the smallest possible value of an Int24. This field is constant. + /// + public static readonly Int24 MinValue = (Int24)MinValue32; + + /// Returns the specified Int24 value as an array of three bytes. + /// Int24 value to convert to bytes. + /// An array of bytes with length 3. + /// + /// You can use this function in-lieu of a System.BitConverter.GetBytes(Int24) function. + /// Bytes will be returned in endian order of currently executing process architecture (little-endian on Intel platforms). + /// + public static byte[] GetBytes(Int24 value) + { + // We use a 32-bit integer to store 24-bit integer internally + byte[] data = new byte[3]; + int valueInt = value; + if (BitConverter.IsLittleEndian) + { + data[0] = (byte)valueInt; + data[1] = (byte)(valueInt >> 8); + data[2] = (byte)(valueInt >> 16); + } + else + { + data[0] = (byte)(valueInt >> 16); + data[1] = (byte)(valueInt >> 8); + data[2] = (byte)(valueInt); + } + + // Return serialized 3-byte representation of Int24 + return data; + } + + /// Returns a 24-bit signed integer from three bytes at a specified position in a byte array. + /// An array of bytes. + /// The starting position within value. + /// A 24-bit signed integer formed by three bytes beginning at startIndex. + /// + /// You can use this function in-lieu of a System.BitConverter.ToInt24 function. + /// Bytes endian order assumed to match that of currently executing process architecture (little-endian on Intel platforms). + /// + /// cannot be null. + /// is greater than length. + /// length from is too small to represent an . + public static Int24 GetValue(byte[] value, int startIndex) + { + if (value == null) + throw new ArgumentNullException(nameof(value), "Cannot be null"); + if (startIndex > value.Length) + throw new ArgumentOutOfRangeException( + nameof(startIndex), + "Cannot be greater than length of value" + ); + if (value.Length - startIndex < 3) + throw new ArgumentException( + "Array length from startIndex must be at least 3", + nameof(value) + ); + if (value.Length - startIndex > 3) + throw new ArgumentException( + "Array length from startIndex must be no more than 3", + nameof(value) + ); + if (value.Length == 0) + return 0; + int valueInt; + if (BitConverter.IsLittleEndian) + { + valueInt = + value[startIndex] | value[startIndex + 1] << 8 | value[startIndex + 2] << 16; + } + else + { + valueInt = + value[startIndex] << 16 | value[startIndex + 1] << 8 | value[startIndex + 2]; + } + // Deserialize value + return (Int24)ApplyBitMask(valueInt); + } + + private static void ValidateNumericRange(int value) + { + if (value > (MaxValue32 + 1) || value < MinValue32) + throw new OverflowException( + string.Format("Value of {0} will not fit in a 24-bit signed integer", value) + ); + } + + private static int ApplyBitMask(int value) + { + // Check bit 23, the sign bit in a signed 24-bit integer + if ((value & 0x00800000) > 0) + { + // If the sign-bit is set, this number will be negative - set all high-byte bits (keeps 32-bit number in 24-bit range) + value |= BitMask; + } + else + { + // If the sign-bit is not set, this number will be positive - clear all high-byte bits (keeps 32-bit number in 24-bit range) + value &= ~BitMask; + } + + return value; + } + + #endregion + } } diff --git a/src/ObjectId.cs b/src/ObjectId.cs index 9790d4c..2e204c6 100644 --- a/src/ObjectId.cs +++ b/src/ObjectId.cs @@ -223,13 +223,13 @@ public static bool TryParse(string? s, IFormatProvider? provider, out ObjectId r ? BitConverter.GetBytes(((IObjectId)this).MachineId).Reverse().ToArray() : BitConverter.GetBytes(((IObjectId)this).MachineId); - public readonly int Counter => int.Parse(Value.Substring(17, 6), NumberStyles.HexNumber); + public readonly i24 Counter => i24.Parse(Value.Substring(17, 6), NumberStyles.HexNumber); public readonly byte[] CounterBytes => BitConverter.IsLittleEndian ? BitConverter.GetBytes(((IObjectId)this).Counter).Reverse().ToArray() : BitConverter.GetBytes(((IObjectId)this).Counter); - private static int _counter = Random.NextInt32(0, i24.MaxValue); + private static i24 _counter = (i24)Random.NextInt32(0, i24.MaxValue); public static int NextCounter() => _counter++; @@ -243,7 +243,7 @@ public interface IObjectId { int Timestamp { get; } long MachineId { get; } - int Counter { get; } + i24 Counter { get; } DateTimeOffset TimestampAsDateTimeOffset { get; } #if NET6_0_OR_GREATER static abstract ObjectId NewId();