954 lines · 41.5 KB · C#
RawDownload
//******************************************************************************************************//  PatternCompressor.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://  ----------------------------------------------------------------------------------------------------//  10/27/2011 - J. Ritchie Carroll//       Initial version of source generated.//  01/25/2012 - Stephen C. Wills//       Removed 64-bit compression routine, added 32-bit decompression routine.//  01/26/2012 - Stephen C. Wills//       Changed class to non-static so that compressor instances//       can be created to handle stream compression.//  12/14/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System;using GSF.Parsing; namespace GSF.IO.Compression{    /// <summary>    /// Defines functions used for high-speed pattern compression against native types.    /// </summary>    public class PatternCompressor    {        #region [ Members ]         // Fields        private byte[] m_compressedBuffer;        private int m_compressedBufferLength;        private int m_maxCompressedBufferLength;         private uint[] m_backBuffer;        private int m_backBufferStart;        private int m_backBufferLength;         private byte m_compressionStrength;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new instance of the PatternCompressor with the given compression strength.        /// </summary>        /// <param name="compressionStrength">The compression strength of the PatternCompressor.</param>        public PatternCompressor(byte compressionStrength = 5)        {            CompressionStrength = compressionStrength;            m_maxCompressedBufferLength = -1;        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets the buffer into which compressed values are written.        /// </summary>        /// <remarks>        /// When setting the compressed buffer, the new buffer is assumed to be empty        /// (<see cref="CompressedBufferLength"/> is set to zero).        /// </remarks>        public byte[] CompressedBuffer        {            get            {                return m_compressedBuffer;            }            set            {                m_compressedBuffer = value;                m_compressedBufferLength = 0;            }        }         /// <summary>        /// Gets the amount of compressed data in the compressed buffer.        /// </summary>        public int CompressedBufferLength        {            get            {                return m_compressedBufferLength;            }        }         /// <summary>        /// Gets or sets the maximum compressed buffer length.        /// </summary>        /// <remarks>        /// The compressor will not write beyond the MaxCompressedBufferLength.        /// Defaults to the full length of the compressed buffer.        /// Set to -1 to return to the default.        /// </remarks>        public int MaxCompressedBufferLength        {            get            {                if (m_maxCompressedBufferLength > 0)                    return m_maxCompressedBufferLength;                 if (m_compressedBuffer != null)                    return m_compressedBuffer.Length;                 return 0;            }            set            {                m_maxCompressedBufferLength = value;            }        }         /// <summary>        /// Gets or sets the compression strength of the PatternCompressor.        /// </summary>        /// <remarks>        /// When setting the compression strength, the PatternCompressor        /// is automatically reset (the back buffer is emptied).        /// </remarks>        public byte CompressionStrength        {            get            {                return m_compressionStrength;            }            set            {                if (value > 31)                    throw new ArgumentOutOfRangeException(nameof(value), "Compression strength must be 0 to 31");                 if (m_backBuffer == null || m_backBuffer.Length < value + 1)                    m_backBuffer = new uint[value + 1];                 m_backBufferStart = 0;                m_backBufferLength = 0;                 m_compressionStrength = value;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        /// <exception cref="ArgumentNullException"><paramref name="value"/> cannot be null.</exception>        public unsafe int Compress(ISupportBinaryImage value)        {            byte[] buffer;            int bufferLength;             int compressedSize = 0;            byte* iter, end;             if ((object)value == null)                throw new ArgumentNullException(nameof(value));             bufferLength = value.BinaryLength.AlignDoubleWord();            buffer = new byte[bufferLength];             fixed (byte* start = buffer)            {                value.GenerateBinaryImage(buffer, 0);                end = start + bufferLength;                 for (iter = start; iter < end; iter += 4)                    compressedSize += Compress(iter);            }             return compressedSize;        }         /// <summary>        /// Compresses all of the data in the given <paramref name="buffer"/>.        /// </summary>        /// <param name="buffer">The buffer to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> cannot be null.</exception>        /// <exception cref="ArgumentException"><paramref name="buffer"/> length must be a multiple of four.</exception>        public int Compress(byte[] buffer)        {            return Compress(buffer, 0, buffer.Length);        }         /// <summary>        /// Compresses <paramref name="length"/> bytes of data in the given <paramref name="buffer"/>.        /// </summary>        /// <param name="buffer">The buffer to be compressed.</param>        /// <param name="length">The amount of data to be compressed. Must be a multiple of four.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> cannot be null.</exception>        /// <exception cref="ArgumentException"><paramref name="length"/> must be a multiple of four.</exception>]        /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> must be greater than or equal to zero.</exception>        /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> exceeds <paramref name="buffer"/> array boundaries</exception>        public int Compress(byte[] buffer, int length)        {            return Compress(buffer, 0, length);        }         /// <summary>        /// Compresses <paramref name="length"/> bytes of data in the given <paramref name="buffer"/>, starting at <paramref name="offset"/>.        /// </summary>        /// <param name="buffer">The buffer to be compressed.</param>        /// <param name="offset">The amount of data to ignore at the start of the buffer.</param>        /// <param name="length">The amount of data to be compressed. Must be a multiple of four.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        /// <exception cref="ArgumentNullException"><paramref name="buffer"/> cannot be null.</exception>        /// <exception cref="ArgumentException"><paramref name="length"/> must be a multiple of four.</exception>        /// <exception cref="ArgumentOutOfRangeException"><paramref name="offset"/> must be greater than or equal to zero.</exception>        /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> must be greater than or equal to zero.</exception>        /// <exception cref="ArgumentOutOfRangeException"><paramref name="length"/> exceeds <paramref name="buffer"/> array boundaries</exception>        public unsafe int Compress(byte[] buffer, int offset, int length)        {            const int SizeOf32Bits = sizeof(uint);            int compressedSize = 0;            byte* start, iter, end;             if ((object)buffer == null)                throw new ArgumentNullException(nameof(buffer), "Cannot write data to null buffer");             if (length % SizeOf32Bits != 0)                throw new ArgumentException("Buffer length must be a multiple of four", nameof(length));             if (offset < 0)                throw new ArgumentOutOfRangeException(nameof(offset), "offset must be greater than or equal to zero");             if (length < 0)                throw new ArgumentOutOfRangeException(nameof(length), "length must be greater than or equal to zero");             if (buffer.Length < offset + length)                throw new ArgumentOutOfRangeException(nameof(length), "length exceeds buffer array boundaries");             fixed (byte* pBuffer = buffer)            {                start = pBuffer + offset;                end = start + length;                 for (iter = start; iter < end; iter += 4)                    compressedSize += Compress(iter);            }             return compressedSize;        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public unsafe int Compress(double value)        {            uint* pItem = (uint*)&value;            int compressedSize = 0;             compressedSize += Compress((byte*)pItem);            compressedSize += Compress((byte*)(pItem + 1));             return compressedSize;        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public unsafe int Compress(float value)        {            return Compress((byte*)&value);        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public unsafe int Compress(long value)        {            uint* pItem = (uint*)&value;            int compressedSize = 0;             compressedSize += Compress((byte*)pItem);            compressedSize += Compress((byte*)(pItem + 1));             return compressedSize;        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public unsafe int Compress(ulong value)        {            uint* pItem = (uint*)&value;            int compressedSize = 0;             compressedSize += Compress((byte*)pItem);            compressedSize += Compress((byte*)(pItem + 1));             return compressedSize;        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public unsafe int Compress(int value)        {            return Compress((byte*)&value);        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public unsafe int Compress(uint value)        {            return Compress((byte*)&value);        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public int Compress(short value)        {            return Compress((int)value);        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public int Compress(ushort value)        {            return Compress((uint)value);        }         /// <summary>        /// Compresses the given value and places it in the compressed buffer.        /// </summary>        /// <param name="value">The value to be compressed.</param>        /// <returns>The size, in bytes, of the compressed value.</returns>        public int Compress(byte value)        {            return Compress((uint)value);        }         /// <summary>        /// Sets <see cref="CompressedBufferLength"/> to zero and allows the        /// compressor to write over the values in the compressed buffer.        /// </summary>        public void EmptyCompressedBuffer()        {            m_compressedBufferLength = 0;        }         /// <summary>        /// Resets the compressor by flushing the back buffer. Subsequent calls        /// to the Compress methods will not be compressed using previously        /// compressed values.        /// </summary>        public void Reset()        {            m_backBufferStart = 0;            m_backBufferLength = 0;        }         // Helper method to compress a generic set of four        // bytes, given a pointer to the first byte.        private unsafe int Compress(byte* pValue)        {            uint value = *(uint*)pValue;            int compressedSize;             if (m_compressedBuffer == null)                throw new InvalidOperationException("Cannot write compressed value to a null buffer");             if (m_backBufferLength == 0)                compressedSize = InsertFirstValue(value);            else                compressedSize = InsertCompressedValue(value);             InsertIntoBackBuffer(value);            return compressedSize;        }         // Inserts the given value into the compressed buffer as the first,        // uncompressed value in the stream. This is called when the back        // buffer is empty.        private unsafe int InsertFirstValue(uint value)        {            // Size of the first value includes the 1-byte stream header            const int FirstValueSize = 1 + sizeof(uint);             // Ensure that the value will fit in the compressed buffer            if (MaxCompressedBufferLength - m_compressedBufferLength < FirstValueSize)                throw new InvalidOperationException("Value cannot fit in compressed buffer. Empty compressed buffer.");             // Insert value into compressed buffer            fixed (byte* pCompressedBuffer = m_compressedBuffer)            {                byte* compressedBufferEnd = pCompressedBuffer + m_compressedBufferLength;                *compressedBufferEnd = (byte)(0xC0 | m_compressionStrength);                *(uint*)(compressedBufferEnd + 1) = value;                m_compressedBufferLength += FirstValueSize;            }             return FirstValueSize;        }         // Compresses the given value and inserts it into the compressed buffer.        private unsafe int InsertCompressedValue(uint value)        {            byte* compressedBufferEnd;             byte* pValue = (byte*)&value;            byte decompressionKey = GetDecompressionKey(value);            byte difference = (byte)(decompressionKey >> 5);             int compressedSize = 1 + difference;             if (MaxCompressedBufferLength - m_compressedBufferLength < compressedSize)                throw new InvalidOperationException("Value cannot fit in compressed buffer. Empty compressed buffer.");             fixed (byte* pCompressedBuffer = m_compressedBuffer)            {                compressedBufferEnd = pCompressedBuffer + m_compressedBufferLength;                *compressedBufferEnd = decompressionKey;                compressedBufferEnd++;                 if (!BitConverter.IsLittleEndian)                    pValue += sizeof(uint) - difference;                 for (int i = 0; i < difference; i++, compressedBufferEnd++, pValue++)                    *compressedBufferEnd = *pValue;                 m_compressedBufferLength += compressedSize;            }             return compressedSize;        }         // Inserts the value into the back buffer.        private void InsertIntoBackBuffer(uint value)        {            if (m_backBufferLength < m_compressionStrength + 1)            {                m_backBuffer[m_backBufferLength] = value;                m_backBufferLength++;            }            else            {                m_backBuffer[m_backBufferStart] = value;                m_backBufferStart++;                 if (m_backBufferStart >= m_backBufferLength)                    m_backBufferStart = 0;            }        }         // Gets the 8-bit decompression key for the given value        // based on the values that are in the back buffer.        private unsafe byte GetDecompressionKey(uint value)        {            uint* backBufferIterator;             uint result;            byte difference;             byte smallestDifference = 5;            int backBufferIndex = 0;             // Pin back buffer to navigate using pointer            fixed (uint* pBackBuffer = m_backBuffer)            {

Lines 1–500 of 954.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t