In-Memory Compression by LZMA in C#

I found that LZMA SDK is a powerful compression library.
The SDK doesn’t provide the in-memory compression sample but it can be done so easily.
I chose MemoryStream for IO data type, and I plan to compress small XML files with this.

* Note that there are hard-coded endian issues in the original SDK. I will check it back when I have access to big endian PowerBook.
* Remember everything goes to heap, so it’s not suitable for large data ( I guess more than 10MB.)

Partial code looks like below.
Comment if you need a complete set of source.

using System;
using System.IO;
using SevenZip;
using SevenZip.Compression;

namespace SevenZip.Compression
{
  class LZMACoder : IDisposable
  {
    private bool isDisposed = false;

    //These properties are straight copy from the SDK.
    //Actually, I don't know what these mean.

    private static Int32 dictionary = 1 << 21; //No dictionary
    private static Int32 posStateBits = 2;
    private static Int32 litContextBits = 3;   // for normal files  // UInt32 litContextBits = 0; // for 32-bit data                                             
    private static Int32 litPosBits = 0;       // UInt32 litPosBits = 2; // for 32-bit data
    private static Int32 algorithm = 2;
    private static Int32 numFastBytes = 128;
    private static bool eos = false;
    private static string mf = "bt4";

    private static CoderPropID[] propIDs = 
    {
        CoderPropID.DictionarySize,
        CoderPropID.PosStateBits,  
        CoderPropID.LitContextBits,
        CoderPropID.LitPosBits,
        CoderPropID.Algorithm,
        CoderPropID.NumFastBytes,
        CoderPropID.MatchFinder,
        CoderPropID.EndMarker
    };
    private static object[] properties = 
    {
        (Int32)(dictionary),
        (Int32)(posStateBits),  
        (Int32)(litContextBits),
        (Int32)(litPosBits),
        (Int32)(algorithm),
        (Int32)(numFastBytes),
        mf,
        eos
    };

    public LZMACoder()
    {
      if (BitConverter.IsLittleEndian == false)
      {
        Dispose();
        throw new Exception("Not implemented");        
      }
    }

    public MemoryStream decompress(MemoryStream inStream)
    {
      return decompress(inStream, false);
    }

    public MemoryStream decompress(MemoryStream inStream, bool closeInStream)
    {
      inStream.Position = 0;
      MemoryStream outStream = new MemoryStream();

      byte[] properties = new byte[5];
      if (inStream.Read(properties, 0, 5) != 5)
        throw (new Exception("input .lzma is too short"));

      SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
      decoder.SetDecoderProperties(properties);

      long outSize = 0;

      if (BitConverter.IsLittleEndian)
      {
        for (int i = 0; i < 8; i++)
        {
          int v = inStream.ReadByte();
          if (v < 0)
            throw (new Exception("Can't Read 1"));

          outSize |= ((long)(byte)v) << (8 * i);
        }
      }

      long compressedSize = inStream.Length - inStream.Position;
      decoder.Code(inStream, outStream, compressedSize, outSize, null);

      if (closeInStream)
        inStream.Close();

      return outStream;
    }

    public MemoryStream compress(MemoryStream inStream)
    {
      return compress(inStream, false);
    }

    public MemoryStream compress(MemoryStream inStream, bool closeInStream)
    {
      inStream.Position = 0;
      Int64 fileSize = inStream.Length;
      MemoryStream outStream = new MemoryStream();

      SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
      encoder.SetCoderProperties(propIDs, properties);
      encoder.WriteCoderProperties(outStream);

      if (BitConverter.IsLittleEndian)
      {
        byte[] LengthHeader = BitConverter.GetBytes(fileSize);
        outStream.Write(LengthHeader, 0, LengthHeader.Length);
      }

      encoder.Code(inStream, outStream, -1, -1, null);

      if (closeInStream)
        inStream.Close();

      return outStream;
    }

    ~LZMACoder()
    {
      Dispose();
    }

    public void Dispose()
    {
      Dispose(true);
      GC.SuppressFinalize(this); 
    }

    private void Dispose(bool disposing)
    {
      if (this.isDisposed == false)
      {
        if (disposing)
        {
          //Console.WriteLine("dispose"); 
          GC.SuppressFinalize(this);
        }
      }
      this.isDisposed = true;      
    }
  }
}

Advertisements