//******************************************************************************************************
// 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 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:
// ----------------------------------------------------------------------------------------------------
// 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;
else if (m_compressedBuffer != null)
return m_compressedBuffer.Length;
else
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("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("value cannot be null", "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("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)
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 962. Load more
//****************************************************************************************************** // 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 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: // ---------------------------------------------------------------------------------------------------- // 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; else if (m_compressedBuffer != null) return m_compressedBuffer.Length; else 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("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("value cannot be null", "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("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) 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) {