//******************************************************************************************************// 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(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; } } /// <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(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) *(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. Load more
//****************************************************************************************************** // 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(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; } } /// <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(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) *(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 ]