689 lines · 29.3 KB · C#
RawDownload
//******************************************************************************************************//  PatternDecompressor.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://  ----------------------------------------------------------------------------------------------------//  01/27/2012 - Stephen C. Wills//       Generated original version of source code.//  12/14/2012 - Starlynn Danyelle Gilliam//       Modified Header.////****************************************************************************************************** using System; namespace GSF.IO.Compression{    /// <summary>    /// Defines functions for decompression of data compressed by the PatternCompressor.    /// </summary>    public class PatternDecompressor    {        #region [ Members ]         // Fields        private byte[] m_dataBuffer;        private int m_dataBufferOffset;        private int m_dataBufferEnd;         private uint[] m_backBuffer;        private int m_backBufferStart;        private int m_backBufferLength;         private byte m_compressionStrength;         #endregion         #region [ Properties ]         /// <summary>        /// Gets the compression strength defined by the compressed stream.        /// </summary>        public byte CompressionStrength        {            get            {                return m_compressionStrength;            }            private set            {                if (value > 31)                    throw new ArgumentOutOfRangeException("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;            }        }         /// <summary>        /// Gets the length of the data left in the buffer.        /// </summary>        public int DataBufferLength        {            get            {                return m_dataBufferEnd - m_dataBufferOffset;            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Inserts the given data into the data buffer for decompression.        /// </summary>        /// <param name="data">The data to be inserted into the data buffer.</param>        public void AugmentBuffer(byte[] data)        {            AugmentBuffer(data, 0, data.Length);        }         /// <summary>        /// Inserts the given data into the data buffer for decompression.        /// </summary>        /// <param name="data">The data to be inserted into the data buffer.</param>        /// <param name="dataLength">The amount of data to be taken from the given buffer and placed in the data buffer.</param>        public void AugmentBuffer(byte[] data, int dataLength)        {            AugmentBuffer(data, 0, dataLength);        }         /// <summary>        /// Inserts the given data into the data buffer for decompression.        /// </summary>        /// <param name="data">The data to be inserted into the data buffer.</param>        /// <param name="offset">The amount of data to be ignored at the beginning of the buffer.</param>        /// <param name="dataLength">The amount of data to be taken from the given buffer and placed in the data buffer.</param>        public void AugmentBuffer(byte[] data, int offset, int dataLength)        {            int dataBufferLength = DataBufferLength;            int neededLength = dataBufferLength + dataLength;             byte[] temp;            int desiredLength;             // First check to see if the data will fit in the remaining available buffer space.            if (m_dataBuffer != null && dataLength < m_dataBuffer.Length - m_dataBufferEnd)            {                Buffer.BlockCopy(data, offset, m_dataBuffer, m_dataBufferEnd, dataLength);                m_dataBufferEnd += dataLength;            }             // Next, check to see if the data will fit in the full buffer space.            if (m_dataBuffer != null && neededLength < m_dataBuffer.Length)            {                Buffer.BlockCopy(m_dataBuffer, m_dataBufferOffset, m_dataBuffer, 0, dataBufferLength);                Buffer.BlockCopy(data, offset, m_dataBuffer, dataBufferLength, dataLength);                 m_dataBufferOffset = 0;                m_dataBufferEnd = neededLength;            }             // Buffer is not large enough: reallocate.            if (m_dataBuffer != null && m_dataBuffer.Length < neededLength)            {                // Assume that the buffer will grow rather than shrink.                // Hopefully, this reduces the need for reallocation.                desiredLength = (int)(neededLength * 1.2);                 temp = new byte[desiredLength];                Buffer.BlockCopy(m_dataBuffer, m_dataBufferOffset, temp, 0, dataBufferLength);                Buffer.BlockCopy(data, offset, temp, dataBufferLength, dataLength);                 m_dataBuffer = temp;                m_dataBufferOffset = 0;                m_dataBufferEnd = neededLength;            }             // Buffer does not exist: allocate.            if (m_dataBuffer == null)            {                // Assume that the buffer will grow rather than shrink.                // Hopefully, this reduces the need for reallocation.                desiredLength = (int)(neededLength * 1.2);                 m_dataBuffer = new byte[desiredLength];                Buffer.BlockCopy(data, offset, m_dataBuffer, dataBufferLength, dataLength);                 m_dataBufferOffset = 0;                m_dataBufferEnd = dataLength;            }        }         /// <summary>        /// Decompresses enough bytes of data to fill up the <paramref name="buffer"/>.        /// </summary>        /// <param name="buffer">The buffer that holds the data.</param>        /// <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 void Decompress(byte[] buffer)        {            Decompress(buffer, 0, buffer.Length);        }         /// <summary>        /// Decompresses <paramref name="length"/> bytes of data and places it in the <paramref name="buffer"/>.        /// </summary>        /// <param name="buffer">The buffer that holds the data.</param>        /// <param name="length">The amount of data to be decompressed and written to the buffer. The value of this parameter must be a multiple of four.</param>        /// <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 void Decompress(byte[] buffer, int length)        {            Decompress(buffer, 0, length);        }         /// <summary>        /// Decompresses <paramref name="length"/> bytes of data and places it in the <paramref name="buffer"/> starting at <paramref name="offset"/>.        /// </summary>        /// <param name="buffer">The buffer that holds the data.</param>        /// <param name="offset">The amount of data at the beginning of the buffer that will not be overwritten.</param>        /// <param name="length">The amount of data to be decompressed and written to the buffer. The value of this parameter must be a multiple of four.</param>        /// <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 void Decompress(byte[] buffer, int offset, int length)        {            const int SizeOf32Bits = sizeof(uint);            byte* start, iter, end;             if ((object)buffer == null)                throw new ArgumentNullException("buffer", "Cannot write data to null buffer");             if (length % SizeOf32Bits != 0)                throw new ArgumentException("Buffer length must be a multiple of four", "length");             if (offset < 0)                throw new ArgumentOutOfRangeException("offset", "offset must be greater than or equal to zero");             if (length < 0)                throw new ArgumentOutOfRangeException("length", "length must be greater than or equal to zero");             if (buffer.Length < offset + length)                throw new ArgumentOutOfRangeException("length", "length exceeds buffer array boundaries");             fixed (byte* pBuffer = buffer)            {                start = pBuffer + offset;                end = start + length;                 for (iter = start; iter < end; iter += 4)                    *(uint*)iter = DecompressValue();            }        }         /// <summary>        /// Decompresses eight bytes of data and writes the data into a 64-bit floating point number.        /// </summary>        /// <param name="value">The 64-bit floating point number that the data is to be written to.</param>        public unsafe void Decompress(out double value)        {            fixed (double* pValue = &value)            {                // High word and low word on big endian systems                uint* highWord = (uint*)pValue;                uint* lowWord = highWord + 1;                *highWord = DecompressValue();                *lowWord = DecompressValue();            }        }         /// <summary>        /// Decompresses four bytes of data and writes the data into a 32-bit floating point number.        /// </summary>        /// <param name="value">The 32-bit floating point number that the data is to be written to.</param>        public unsafe void Decompress(out float value)        {            fixed (float* pValue = &value)                *(uint*)pValue = DecompressValue();        }         /// <summary>        /// Decompresses eight bytes of data and writes the data into a 64-bit signed integer.        /// </summary>        /// <param name="value">The 64-bit integer that the data is to be written to.</param>        public unsafe void Decompress(out long value)        {            fixed (long* pValue = &value)            {                // High word and low word on big endian systems                uint* highWord = (uint*)pValue;                uint* lowWord = highWord + 1;                *highWord = DecompressValue();                *lowWord = DecompressValue();            }        }         /// <summary>        /// Decompresses eight bytes of data and writes the data into a 64-bit unsigned integer.        /// </summary>        /// <param name="value">The 64-bit integer that the data is to be written to.</param>        public unsafe void Decompress(out ulong value)        {            fixed (ulong* pValue = &value)            {                // High word and low word on big endian systems                uint* highWord = (uint*)pValue;                uint* lowWord = highWord + 1;                *highWord = DecompressValue();                *lowWord = DecompressValue();            }        }         /// <summary>        /// Decompresses four bytes of data and writes the data into a 32-bit signed integer.        /// </summary>        /// <param name="value">The 32-bit integer that the data is to be written to.</param>        public unsafe void Decompress(out int value)        {            fixed (int* pValue = &value)                *(uint*)pValue = DecompressValue();        }         /// <summary>        /// Decompresses four bytes of data and writes the data into a 32-bit unsigned integer.        /// </summary>        /// <param name="value">The 32-bit integer that the data is to be written to.</param>        public void Decompress(out uint value)        {            value = DecompressValue();        }         /// <summary>        /// Decompresses four bytes of data and writes the data into a 16-bit signed integer. The high-order bytes are discarded.        /// </summary>        /// <param name="value">The 16-bit integer that the data is to be written to.</param>        public void Decompress(out short value)        {            value = (short)DecompressValue();        }         /// <summary>        /// Decompresses four bytes of data and writes the data into a 16-bit unsigned integer. The high-order bytes are discarded.        /// </summary>        /// <param name="value">The 16-bit integer that the data is to be written to.</param>        public void Decompress(out ushort value)        {            value = (ushort)DecompressValue();        }         /// <summary>        /// Decompresses four bytes of data and writes the data into an 8-bit integer. The high-order bytes are discarded.        /// </summary>        /// <param name="value">The 8-bit integer that the data is to be written to.</param>        public void Decompress(out byte value)        {            value = (byte)DecompressValue();        }         /// <summary>        /// Resets the decompressor by flushing the back buffer. Subsequent calls        /// to the Decompress methods will not be decompressed using previously        /// decompressed values.        /// </summary>        public void Reset()        {            m_backBufferStart = 0;            m_backBufferLength = 0;        }         /// <summary>        /// Clears out the data buffer so that subsequent calls to the Decompress        /// methods do not use the data that was previously in the data buffer.        /// </summary>        /// <remarks>        /// This method should not be called during normal operation, as data will        /// be lost and the compressed stream will likely be corrupted. Only call        /// this method if the stream has already been corrupted.        /// </remarks>        public void EmptyBuffer()        {            m_dataBufferOffset = 0;            m_dataBufferEnd = 0;        }         // Decompresses four bytes and returns them as a 32-bit unsigned integer.        private uint DecompressValue()        {            const int KeyMask = 0xE0;            const int Uncompressed = 0xE0;            const int ResetStream = 0xC0;             byte decompressionKey;            uint decompressedValue;             if (DataBufferLength < 1)                throw new InvalidOperationException("Unable to decompress: insufficient data in the buffer. Augment the data buffer.");             decompressionKey = m_dataBuffer[m_dataBufferOffset];             if ((decompressionKey & KeyMask) == Uncompressed)                throw new NotSupportedException("Stream decompression of uncompressed data");             if ((decompressionKey & KeyMask) == ResetStream)            {                // Decompression key indicates that this is                // the first, uncompressed value in the stream                Reset();                decompressedValue = GetFirstValue();            }            else            {                // Decompression key indicates that this value is compressed                decompressedValue = GetDecompressedValue();            }             // Insert the decompressed value into            // the back buffer for future lookups            InsertIntoBackBuffer(decompressedValue);             return decompressedValue;        }         // Gets the first four data bytes in the stream and returns them as a 32-bit unsigned integer.        private unsafe uint GetFirstValue()        {            // Size of the first value includes the 1-byte stream header            const int FirstValueSize = 1 + sizeof(uint);            const int CompressionStrengthMask = 0x1F;             byte* iter;             if (DataBufferLength < FirstValueSize)                throw new InvalidOperationException("Unable to decompress: insufficient data in the buffer. Augment the data buffer.");             fixed (byte* pDataBuffer = m_dataBuffer)            {                // Move the iterator up to the start of the data                iter = pDataBuffer + m_dataBufferOffset;                 // Compression strength is found in the decompression key.                // This is necessary to properly initialize the back buffer.                CompressionStrength = (byte)(*iter & CompressionStrengthMask);                 // Advance the buffer offset                m_dataBufferOffset += FirstValueSize;                 // First value is uncompressed                return *(uint*)(iter + 1);            }        }         // Decompresses four bytes of data and returns them as a 32-bit unsigned integer.        private unsafe uint GetDecompressedValue()        {            const int SizeOf32Bits = sizeof(uint);            const int BackReferenceMask = 0x1F;             byte* iter;            byte decompressionKey;            byte difference, backBufferIndex;             byte* pValue;            uint value;             fixed (byte* pDataBuffer = m_dataBuffer)            {                // Move the iterator up to the start of the data                iter = pDataBuffer + m_dataBufferOffset;                pValue = (byte*)&value;                 // Get the decompression key for this value                decompressionKey = *iter;                difference = (byte)(decompressionKey >> 5);                backBufferIndex = (byte)(decompressionKey & BackReferenceMask);                iter++;                 if (DataBufferLength < 1 + difference)                    throw new InvalidOperationException("Unable to decompress: insufficient data in the buffer. Augment the data buffer.");                 // Initialize the value with data from the back buffer                value = m_backBuffer[backBufferIndex];                 // If bytes are in big endian order, then low order bytes are right most in memory so skip ahead                if (!BitConverter.IsLittleEndian)                    pValue += SizeOf32Bits - difference;                 // Overwrite low order bytes with compressed data                for (int i = 0; i < difference; i++, pValue++, iter++)                    *pValue = *iter;                 // Advance the buffer offset and return the decompressed value                m_dataBufferOffset += 1 + difference;                return value;            }        }         // 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;            }        }         #endregion         #region [ Static ]

Lines 1–500 of 689.

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