//****************************************************************************************************** // ByteEncoding.cs - Gbtc // // Copyright © 2010, 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 Eclipse Public License -v 1.0 (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/eclipse-1.0.php // // 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: // ---------------------------------------------------------------------------------------------------- // 01/25/2008 - J. Ritchie Carroll // Initial version of source generated. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 10/8/2012 - 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.Text; using System.Text.RegularExpressions; namespace GSF { /// /// Defines a set of methods used to convert byte buffers to and from user presentable data formats. /// public abstract class ByteEncoding { #region [ Members ] // Nested Types #region [ Hexadecimal Encoding Class ] /// /// Handles conversion of byte buffers to and from a hexadecimal data format. /// [EditorBrowsable(EditorBrowsableState.Never)] public class HexadecimalEncoding : ByteEncoding { internal HexadecimalEncoding() { // This class is meant for internal instatiation only. } /// Decodes given string back into a byte buffer. /// Encoded hexadecimal data string to decode. /// Original spacing character that was inserted between encoded bytes. /// Decoded bytes. public override byte[] GetBytes(string hexData, char spacingCharacter) { if (!string.IsNullOrEmpty(hexData)) { // Removes spacing characters, if needed. hexData = hexData.Trim(); if (spacingCharacter != NoSpacing) hexData = hexData.Replace(spacingCharacter.ToString(), ""); // Processes the string only if it has data in hex format (Example: 48 65 6C 6C 21). if (Regex.Matches(hexData, "[^a-fA-F0-9]").Count == 0) { // Trims the end of the string to discard any additional characters, if present in the string, // that would prevent the string from being a hex encoded string. // Note: Requires that each character be represented by its 2 character hex value. hexData = hexData.Substring(0, hexData.Length - hexData.Length % 2); byte[] bytes = new byte[hexData.Length / 2]; int index = 0; for (int x = 0; x <= hexData.Length - 1; x += 2) { bytes[index] = Convert.ToByte(hexData.Substring(x, 2), 16); index++; } return bytes; } else { throw new ArgumentException("Input string is not a valid hex encoded string - invalid characters encountered", "hexData"); } } else { throw new ArgumentNullException("hexData", "Input string cannot be null or empty"); } } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Offset into buffer to begin encoding. /// Length of buffer to encode. /// Spacing character to place between encoded bytes. /// String of encoded bytes. public override string GetString(byte[] bytes, int offset, int length, char spacingCharacter) { return BytesToString(bytes, offset, length, spacingCharacter, "X2"); } } #endregion #region [ Decimal Encoding Class ] /// /// Handles conversion of byte buffers to and from a decimal data format. /// [EditorBrowsable(EditorBrowsableState.Never)] public class DecimalEncoding : ByteEncoding { internal DecimalEncoding() { // This class is meant for internal instatiation only. } /// Decodes given string back into a byte buffer. /// Encoded decimal data string to decode. /// Original spacing character that was inserted between encoded bytes. /// Decoded bytes. public override byte[] GetBytes(string decData, char spacingCharacter) { if (!string.IsNullOrEmpty(decData)) { // Removes spacing characters, if needed. decData = decData.Trim(); if (spacingCharacter != NoSpacing) decData = decData.Replace(spacingCharacter.ToString(), ""); // Processes the string only if it has data in decimal format (Example: 072 101 108 108 033). if (Regex.Matches(decData, "[^0-9]").Count == 0) { // Trims the end of the string to discard any additional characters, if present in the // string, that would prevent the string from being an integer encoded string. // Note: Requires that each character be represented by its 3 character decimal value. decData = decData.Substring(0, decData.Length - decData.Length % 3); byte[] bytes = new byte[decData.Length / 3]; int index = 0; for (int x = 0; x <= decData.Length - 1; x += 3) { bytes[index] = Convert.ToByte(decData.Substring(x, 3), 10); index++; } return bytes; } else { throw new ArgumentException("Input string is not a valid decimal encoded string - invalid characters encountered", "decData"); } } else { throw new ArgumentNullException("decData", "Input string cannot be null or empty"); } } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Offset into buffer to begin encoding. /// Length of buffer to encode. /// Spacing character to place between encoded bytes. /// String of encoded bytes. public override string GetString(byte[] bytes, int offset, int length, char spacingCharacter) { return BytesToString(bytes, offset, length, spacingCharacter, "D3"); } } #endregion #region [ Binary Encoding Class ] /// /// Handles conversion of byte buffers to and from a binary (i.e., 0 and 1's) data format. /// [EditorBrowsable(EditorBrowsableState.Never)] public class BinaryEncoding : ByteEncoding { private string[] m_byteImages; private bool m_reverse; // This class is meant for internal instantiation only. internal BinaryEncoding(Endianness targetEndianness) { if (targetEndianness == Endianness.BigEndian) { if (BitConverter.IsLittleEndian) { // If OS is little endian and we want big endian, this reverses the bit order. m_reverse = true; } else { // If OS is big endian and we want big endian, this keeps the OS bit order. m_reverse = false; } } else { if (BitConverter.IsLittleEndian) { // If OS is little endian and we want little endian, this keeps OS bit order. m_reverse = false; } else { // If OS is big endian and we want little endian, this reverses the bit order. m_reverse = true; } } } /// Decodes given string back into a byte buffer. /// Encoded binary data string to decode. /// Original spacing character that was inserted between encoded bytes. /// Decoded bytes. public override byte[] GetBytes(string binaryData, char spacingCharacter) { if (!string.IsNullOrEmpty(binaryData)) { // Removes spacing characters, if needed. binaryData = binaryData.Trim(); if (spacingCharacter != NoSpacing) binaryData = binaryData.Replace(spacingCharacter.ToString(), ""); // Processes the string only if it has data in binary format (Example: 01010110 1010101). if (Regex.Matches(binaryData, "[^0-1]").Count == 0) { // Trims the end of the string to discard any additional characters, if present in the // string, that would prevent the string from being a binary encoded string. // Note: Requires each character be represented by its 8 character binary value. binaryData = binaryData.Substring(0, binaryData.Length - binaryData.Length % 8); byte[] bytes = new byte[binaryData.Length / 8]; int index = 0; for (int x = 0; x <= binaryData.Length - 1; x += 8) { bytes[index] = (byte)Bits.Nil; if (m_reverse) { if (binaryData[x + 7] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit00); if (binaryData[x + 6] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit01); if (binaryData[x + 5] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit02); if (binaryData[x + 4] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit03); if (binaryData[x + 3] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit04); if (binaryData[x + 2] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit05); if (binaryData[x + 1] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit06); if (binaryData[x + 0] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit07); } else { if (binaryData[x + 0] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit00); if (binaryData[x + 1] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit01); if (binaryData[x + 2] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit02); if (binaryData[x + 3] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit03); if (binaryData[x + 4] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit04); if (binaryData[x + 5] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit05); if (binaryData[x + 6] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit06); if (binaryData[x + 7] == '1') bytes[index] = bytes[index].SetBits(Bits.Bit07); } index++; } return bytes; } else { throw new ArgumentException("Input string is not a valid binary encoded string - invalid characters encountered", "binaryData"); } } else { throw new ArgumentNullException("binaryData", "Input string cannot be null or empty"); } } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Offset into buffer to begin encoding. /// Length of buffer to encode. /// Spacing character to place between encoded bytes. /// String of encoded bytes. public override string GetString(byte[] bytes, int offset, int length, char spacingCharacter) { if ((object)bytes == null) throw new ArgumentNullException("bytes", "Input buffer cannot be null"); // Initializes byte image array on first call for speed in future calls. if ((object)m_byteImages == null) { StringBuilder byteImage; m_byteImages = new string[256]; for (int imageByte = byte.MinValue; imageByte <= byte.MaxValue; imageByte++) { byteImage = new StringBuilder(); if (m_reverse) { if (imageByte.CheckBits(Bits.Bit07)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit06)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit05)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit04)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit03)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit02)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit01)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit00)) byteImage.Append('1'); else byteImage.Append('0'); } else { if (imageByte.CheckBits(Bits.Bit00)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit01)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit02)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit03)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit04)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit05)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit06)) byteImage.Append('1'); else byteImage.Append('0'); if (imageByte.CheckBits(Bits.Bit07)) byteImage.Append('1'); else byteImage.Append('0'); } m_byteImages[imageByte] = byteImage.ToString(); } } StringBuilder binaryImage = new StringBuilder(); for (int x = 0; x < length; x++) { if (spacingCharacter != NoSpacing && x > 0) binaryImage.Append(spacingCharacter); binaryImage.Append(m_byteImages[bytes[offset + x]]); } return binaryImage.ToString(); } } #endregion #region [ Base64 Encoding Class ] /// /// Handles conversion of byte buffers to and from a base64 data format. /// [EditorBrowsable(EditorBrowsableState.Never)] public class Base64Encoding : ByteEncoding { internal Base64Encoding() { // This class is meant for internal instantiation only. } /// Decodes given string back into a byte buffer. /// Encoded binary data string to decode. /// Original spacing character that was inserted between encoded bytes. /// Decoded bytes. public override byte[] GetBytes(string binaryData, char spacingCharacter) { // Removes spacing characters, if needed. binaryData = binaryData.Trim(); if (spacingCharacter != NoSpacing) binaryData = binaryData.Replace(spacingCharacter.ToString(), ""); return Convert.FromBase64String(binaryData); } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Offset into buffer to begin encoding. /// Length of buffer to encode. /// Spacing character to place between encoded bytes. /// String of encoded bytes. public override string GetString(byte[] bytes, int offset, int length, char spacingCharacter) { if ((object)bytes == null) throw new ArgumentNullException("bytes", "Input buffer cannot be null"); string base64String = Convert.ToBase64String(bytes, offset, length); if (spacingCharacter == NoSpacing) { return base64String; } else { StringBuilder base64Image = new StringBuilder(); for (int x = 0; x <= base64String.Length - 1; x++) { if (x > 0) base64Image.Append(spacingCharacter); base64Image.Append(base64String[x]); } return base64Image.ToString(); } } } #endregion #region [ ASCII Encoding Class ] /// /// Handles conversion of byte buffers to and from a ASCII data format. /// [EditorBrowsable(EditorBrowsableState.Never)] public class ASCIIEncoding : ByteEncoding { internal ASCIIEncoding() { // This class is meant for internal instantiation only. } /// Decodes given string back into a byte buffer. /// Encoded binary data string to decode. /// Original spacing character that was inserted between encoded bytes. /// Decoded bytes. public override byte[] GetBytes(string binaryData, char spacingCharacter) { // Removes spacing characters, if needed. binaryData = binaryData.Trim(); if (spacingCharacter != NoSpacing) binaryData = binaryData.Replace(spacingCharacter.ToString(), ""); return Encoding.ASCII.GetBytes(binaryData); } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Offset into buffer to begin encoding. /// Length of buffer to encode. /// Spacing character to place between encoded bytes. /// String of encoded bytes. public override string GetString(byte[] bytes, int offset, int length, char spacingCharacter) { if ((object)bytes == null) throw new ArgumentNullException("bytes", "Input buffer cannot be null"); string asciiString = Encoding.ASCII.GetString(bytes, offset, length); if (spacingCharacter == NoSpacing) { return asciiString; } else { StringBuilder asciiImage = new StringBuilder(); for (int x = 0; x <= asciiString.Length - 1; x++) { if (x > 0) asciiImage.Append(spacingCharacter); asciiImage.Append(asciiString[x]); } return asciiImage.ToString(); } } } #endregion /// /// Constant used to specify that "no spacing" should be used for data conversion. /// public const char NoSpacing = char.MinValue; #endregion #region [ Methods ] /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// String representation of byte array. public virtual string GetString(byte[] bytes) { return GetString(bytes, NoSpacing); } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Spacing character to place between encoded bytes. /// String of encoded bytes. public virtual string GetString(byte[] bytes, char spacingCharacter) { if ((object)bytes == null) throw new ArgumentNullException("bytes", "Input buffer cannot be null"); return GetString(bytes, 0, bytes.Length, spacingCharacter); } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Offset into buffer to begin encoding. /// Length of buffer to encode. /// String of encoded bytes. public virtual string GetString(byte[] bytes, int offset, int length) { if ((object)bytes == null) throw new ArgumentNullException("bytes", "Input buffer cannot be null"); return GetString(bytes, offset, length, NoSpacing); } /// Encodes given buffer into a user presentable representation. /// Bytes to encode. /// Offset into buffer to begin encoding. /// Length of buffer to encode. /// Spacing character to place between encoded bytes. /// String of encoded bytes. public abstract string GetString(byte[] bytes, int offset, int length, char spacingCharacter); /// Decodes given string back into a byte buffer. /// Encoded string to decode. /// Decoded bytes. public virtual byte[] GetBytes(string value) { if (string.IsNullOrEmpty(value)) throw new ArgumentNullException("value", "Input string cannot be null"); return GetBytes(value, NoSpacing); } /// Decodes given string back into a byte buffer. /// Encoded string to decode. /// Original spacing character that was inserted between encoded bytes. /// Decoded bytes public abstract byte[] GetBytes(string value, char spacingCharacter); #endregion #region [ Static ] // Static Fields private static ByteEncoding s_hexadecimalEncoding; private static ByteEncoding s_decimalEncoding; private static ByteEncoding s_bigEndianBinaryEncoding; private static ByteEncoding s_littleEndianBinaryEncoding; private static ByteEncoding s_base64Encoding; private static ByteEncoding s_asciiEncoding; /// Handles encoding and decoding of a byte buffer into a hexadecimal-based presentation format. public static ByteEncoding Hexadecimal { get { if ((object)s_hexadecimalEncoding == null) s_hexadecimalEncoding = new HexadecimalEncoding(); return s_hexadecimalEncoding; } } /// Handles encoding and decoding of a byte buffer into an integer-based presentation format. public static ByteEncoding Decimal { get { if ((object)s_decimalEncoding == null) s_decimalEncoding = new DecimalEncoding(); return s_decimalEncoding; } } /// Handles encoding and decoding of a byte buffer into a big-endian binary (i.e., 0 and 1's) based /// presentation format. /// /// Although endianness is typically used in the context of byte order (see to handle byte /// order swapping), this property allows you visualize "bits" in big-endian order, right-to-left. Note that bits are /// normally stored in the same order as their bytes.). /// public static ByteEncoding BigEndianBinary { get { if ((object)s_bigEndianBinaryEncoding == null) s_bigEndianBinaryEncoding = new BinaryEncoding(Endianness.BigEndian); return s_bigEndianBinaryEncoding; } } /// Handles encoding and decoding of a byte buffer into a little-endian binary (i.e., 0 and 1's) based /// presentation format. /// /// Although endianness is typically used in the context of byte order (see to handle byte /// order swapping), this property allows you visualize "bits" in little-endian order, left-to-right. Note that bits are /// normally stored in the same order as their bytes. /// public static ByteEncoding LittleEndianBinary { get { if ((object)s_littleEndianBinaryEncoding == null) s_littleEndianBinaryEncoding = new BinaryEncoding(Endianness.LittleEndian); return s_littleEndianBinaryEncoding; } } /// Handles encoding and decoding of a byte buffer into a base64 presentation format. public static ByteEncoding Base64 { get { if ((object)s_base64Encoding == null) s_base64Encoding = new Base64Encoding(); return s_base64Encoding; } } /// Handles encoding and decoding of a byte buffer into an ASCII character presentation format. public static ByteEncoding ASCII { get { if ((object)s_asciiEncoding == null) s_asciiEncoding = new ASCIIEncoding(); return s_asciiEncoding; } } /// Handles byte to string conversions for implementations that are available from Byte.ToString. /// Encoded string to decode. /// Offset into byte array to begin decoding straing at. /// Number of bytes to decode starting at /// Character to insert between each byte /// String decoding format. /// Decoded string internal static string BytesToString(byte[] bytes, int offset, int length, char spacingCharacter, string format) { if ((object)bytes == null) throw new ArgumentNullException("bytes", "Input buffer cannot be null"); StringBuilder byteString = new StringBuilder(); for (int x = 0; x <= length - 1; x++) { if (spacingCharacter != NoSpacing && x > 0) byteString.Append(spacingCharacter); byteString.Append(bytes[x + offset].ToString(format)); } return byteString.ToString(); } #endregion } }