Skip to content

Instantly share code, notes, and snippets.

@ststeiger
Created November 18, 2014 10:22
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ststeiger/cb9750664952f775a341 to your computer and use it in GitHub Desktop.
Save ststeiger/cb9750664952f775a341 to your computer and use it in GitHub Desktop.
LZMA-Compression with SevenZip SDK
// http://www.nullskull.com/a/768/7zip-lzma-inmemory-compression-with-c.aspx
// http://www.7-zip.org/sdk.html
namespace SevenZip.Compression.LZMA
{
public static class SevenZipHelper
{
static int dictionary = 1 << 23;
// static Int32 posStateBits = 2;
// static Int32 litContextBits = 3; // for normal files
// UInt32 litContextBits = 0; // for 32-bit data
// static Int32 litPosBits = 0;
// UInt32 litPosBits = 2; // for 32-bit data
// static Int32 algorithm = 2;
// static Int32 numFastBytes = 128;
static bool eos = false;
static CoderPropID[] propIDs =
{
CoderPropID.DictionarySize,
CoderPropID.PosStateBits,
CoderPropID.LitContextBits,
CoderPropID.LitPosBits,
CoderPropID.Algorithm,
CoderPropID.NumFastBytes,
CoderPropID.MatchFinder,
CoderPropID.EndMarker
};
// these are the default properties, keeping it simple for now:
static object[] properties =
{
(System.Int32)(dictionary),
(System.Int32)(2),
(System.Int32)(3),
(System.Int32)(0),
(System.Int32)(2),
(System.Int32)(128),
"bt4",
eos
};
public static byte[] Compress(byte[] inputBytes)
{
byte[] retVal = null;
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
encoder.SetCoderProperties(propIDs, properties);
using (System.IO.MemoryStream strmInStream = new System.IO.MemoryStream(inputBytes))
{
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream())
{
encoder.WriteCoderProperties(strmOutStream);
long fileSize = strmInStream.Length;
for (int i = 0; i < 8; i++)
strmOutStream.WriteByte((byte)(fileSize >> (8 * i)));
encoder.Code(strmInStream, strmOutStream, -1, -1, null);
retVal = strmOutStream.ToArray();
} // End Using outStream
} // End Using inStream
return retVal;
} // End Function Compress
public static byte[] Compress(string inFileName)
{
byte[] retVal = null;
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
encoder.SetCoderProperties(propIDs, properties);
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream())
{
encoder.WriteCoderProperties(strmOutStream);
long fileSize = strmInStream.Length;
for (int i = 0; i < 8; i++)
strmOutStream.WriteByte((byte)(fileSize >> (8 * i)));
encoder.Code(strmInStream, strmOutStream, -1, -1, null);
retVal = strmOutStream.ToArray();
} // End Using outStream
} // End Using inStream
return retVal;
} // End Function Compress
public static void Compress(string inFileName, string outFileName)
{
SevenZip.Compression.LZMA.Encoder encoder = new SevenZip.Compression.LZMA.Encoder();
encoder.SetCoderProperties(propIDs, properties);
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
using (System.IO.Stream strmOutStream = new System.IO.FileStream(outFileName, System.IO.FileMode.Create))
{
encoder.WriteCoderProperties(strmOutStream);
long fileSize = strmInStream.Length;
for (int i = 0; i < 8; i++)
strmOutStream.WriteByte((byte)(fileSize >> (8 * i)));
encoder.Code(strmInStream, strmOutStream, -1, -1, null);
strmOutStream.Flush();
strmOutStream.Close();
} // End Using outStream
} // End Using inStream
} // End Function Compress
public static byte[] Decompress(string inFileName)
{
byte[] retVal = null;
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
strmInStream.Seek(0, 0);
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream())
{
byte[] properties2 = new byte[5];
if (strmInStream.Read(properties2, 0, 5) != 5)
throw (new System.Exception("input .lzma is too short"));
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = strmInStream.ReadByte();
if (v < 0)
throw (new System.Exception("Can't Read 1"));
outSize |= ((long)(byte)v) << (8 * i);
} //Next i
decoder.SetDecoderProperties(properties2);
long compressedSize = strmInStream.Length - strmInStream.Position;
decoder.Code(strmInStream, strmOutStream, compressedSize, outSize, null);
retVal = strmOutStream.ToArray();
} // End Using newOutStream
} // End Using newInStream
return retVal;
} // End Function Decompress
public static void Decompress(string inFileName, string outFileName)
{
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
using (System.IO.Stream strmInStream = new System.IO.FileStream(inFileName, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
strmInStream.Seek(0, 0);
using (System.IO.Stream strmOutStream = new System.IO.FileStream(outFileName, System.IO.FileMode.Create))
{
byte[] properties2 = new byte[5];
if (strmInStream.Read(properties2, 0, 5) != 5)
throw (new System.Exception("input .lzma is too short"));
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = strmInStream.ReadByte();
if (v < 0)
throw (new System.Exception("Can't Read 1"));
outSize |= ((long)(byte)v) << (8 * i);
} // Next i
decoder.SetDecoderProperties(properties2);
long compressedSize = strmInStream.Length - strmInStream.Position;
decoder.Code(strmInStream, strmOutStream, compressedSize, outSize, null);
strmOutStream.Flush();
strmOutStream.Close();
} // End Using newOutStream
} // End Using newInStream
} // End Function Decompress
public static byte[] Decompress(byte[] inputBytes)
{
byte[] retVal = null;
SevenZip.Compression.LZMA.Decoder decoder = new SevenZip.Compression.LZMA.Decoder();
using (System.IO.MemoryStream strmInStream = new System.IO.MemoryStream(inputBytes))
{
strmInStream.Seek(0, 0);
using (System.IO.MemoryStream strmOutStream = new System.IO.MemoryStream())
{
byte[] properties2 = new byte[5];
if (strmInStream.Read(properties2, 0, 5) != 5)
throw (new System.Exception("input .lzma is too short"));
long outSize = 0;
for (int i = 0; i < 8; i++)
{
int v = strmInStream.ReadByte();
if (v < 0)
throw (new System.Exception("Can't Read 1"));
outSize |= ((long)(byte)v) << (8 * i);
} // Next i
decoder.SetDecoderProperties(properties2);
long compressedSize = strmInStream.Length - strmInStream.Position;
decoder.Code(strmInStream, strmOutStream, compressedSize, outSize, null);
retVal = strmOutStream.ToArray();
} // End Using newOutStream
} // End Using newInStream
return retVal;
} // End Function Decompress
} // End Class SevenZipHelper
} // End Namespace SevenZip.Compression.LZMA
@Dafnafrank
Copy link

Dafnafrank commented Nov 16, 2016

Hi,
I really want to use your code.
Run it and I am getting an exception from the 'Code' method: "DataErrorException()"
if (rep0 == 0xFFFFFFFF)
break;
throw new DataErrorException();

Any idea why and how I can fix this?
I simply run the 'Decompress' method and gave it a 7z file name.

@kirides
Copy link

kirides commented May 9, 2019

I will just leave this in here for a little smoother API (Tested using latest LZMA 19.00 SDK)

Usage:

LZMA.CompressFile("input.txt", "output.txt.lzma" [, LzmaSpeed.Fastest, DictionarySize.VerySmall, Action<long, long> onProgress]);
LZMA.DecompressFile("input.txt", "output.txt.lzma" [, Action<long, long> onProgress])

LZMA.Compress(inStream, outStream [, LzmaSpeed.Fastest, DictionarySize.VerySmall, Action<long, long> onProgress]);
LZMA.Decompress(inStream, outStream [, Action<long, long> onProgress])
namespace Encoding.SevenZip
{
    using System;
    using System.IO.Compression;
    using SevenZip;
    using SevenZip.Compression.LZMA;

    public enum LzmaSpeed : int
    {
        Fastest = 5,
        VeryFast = 8,
        Fast = 16,
        Medium = 32,
        Slow = 64,
        VerySlow = 128,
    }
    public enum DictionarySize : int
    {
        ///<summary>64 KiB</summary>
        VerySmall = 1 << 16,
        ///<summary>1 MiB</summary>
        Small = 1 << 20,
        ///<summary>4 MiB</summary>
        Medium = 1 << 22,
        ///<summary>8 MiB</summary>
        Large = 1 << 23,
        ///<summary>16 MiB</summary>
        Larger = 1 << 24,
        ///<summary>64 MiB</summary>
        VeryLarge = 1 << 26,
    }
    public static class LZMA
    {
        public static void CompressFile(string filename, string outfile, LzmaSpeed speed = LzmaSpeed.Fastest, DictionarySize dictionarySize = DictionarySize.VerySmall, Action<long, long>? onProgress = null)
        {
            using (var fs = File.OpenRead(filename))
            using (var target = File.Create(outfile))
            {
                Compress(fs, target, speed, dictionarySize, onProgress);
            }
        }

        public static void DecompressFile(string filename, string outfile, Action<long, long>? onProgress = null)
        {
            using (var fs = File.OpenRead(filename))
            using (var target = File.Create(outfile))
            {
                Decompress(fs, target, onProgress);
            }
        }

        public static void Compress(Stream input, Stream output, LzmaSpeed speed = LzmaSpeed.Fastest, DictionarySize dictionarySize = DictionarySize.VerySmall, Action<long, long>? onProgress = null)
        {
            int posStateBits = 2; // default: 2
            int litContextBits = 3; // 3 for normal files, 0; for 32-bit data
            int litPosBits = 0; // 0 for 64-bit data, 2 for 32-bit.
            var numFastBytes = (int)speed;
            string matchFinder = "BT4"; // default: BT4
            bool endMarker = true;

            CoderPropID[] propIDs =
            {
                CoderPropID.DictionarySize,
                CoderPropID.PosStateBits, // (0 <= x <= 4).
                CoderPropID.LitContextBits, // (0 <= x <= 8).
                CoderPropID.LitPosBits, // (0 <= x <= 4).
                CoderPropID.NumFastBytes,
                CoderPropID.MatchFinder, // "BT2", "BT4".
                CoderPropID.EndMarker
            };

            object[] properties =
            {
                (int)dictionarySize,
                posStateBits,
                (int)litContextBits,
                (int)litPosBits,
                numFastBytes,
                matchFinder,
                endMarker
            };

            var lzmaEncoder = new Encoder();

            lzmaEncoder.SetCoderProperties(propIDs, properties);
            lzmaEncoder.WriteCoderProperties(output);
            var fileSize = input.Length;
            for (int i = 0; i < 8; i++) output.WriteByte((byte)(fileSize >> (8 * i)));

            ICodeProgress? prg = null;
            if (onProgress != null)
            {
                prg = new DelegateCodeProgress(onProgress);
            }
            lzmaEncoder.Code(input, output, -1, -1, prg);
        }

        public static void Decompress(Stream input, Stream output, Action<long, long>? onProgress = null)
        {
            var decoder = new SevenZip.Compression.LZMA.Decoder();

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

            long fileLength = 0;
            for (int i = 0; i < 8; i++)
            {
                int v = input.ReadByte();
                if (v < 0) throw new Exception("Can't Read 1");
                fileLength |= ((long)(byte)v) << (8 * i);
            }

            ICodeProgress? prg = null;
            if (onProgress != null)
            {
                prg = new DelegateCodeProgress(onProgress);
            }
            long compressedSize = input.Length - input.Position;

            decoder.Code(input, output, compressedSize, fileLength, prg);
        }

        private class DelegateCodeProgress : ICodeProgress
        {
            private readonly Action<long, long> handler;
            public DelegateCodeProgress(Action<long, long> handler) => this.handler = handler;
            public void SetProgress(long inSize, long outSize) => handler(inSize, outSize);
        }
    }
}

@sugiarto-dvd
Copy link

I hope you read this, and kindly answer it.
Can you tell me how to compress / decompress multiple file at once ?

When I'm using your solution , it can compress only 1 file into 1 7z file.

Thank you.

@jorensanbar
Copy link

LZMA.Decompress(inStream, outStream [, Action<long, long> onProgress])

How I can assing this to a ProgressBar?

@kirides
Copy link

kirides commented Nov 17, 2021

LZMA.Decompress(inStream, outStream [, Action<long, long> onProgress])

How I can assing this to a ProgressBar?

Like this for example:

var fileStream = File.OpenRead("..."); // Get Filestream to compress
var zipStream = File.Create("...");    // Output file
var totalSize = fileStream.Length;     // <- Get filesize once

// Using IProgress<T> for notifying
var prg = new Progress<(long processedBytes, long resultSize)>();
prg.ProgressChanged += (s, e) =>
{
    // Update UI / Progressbar, etc.
    // \/ this writes "Compressing: 1.52%" \/
    Console.WriteLine($"Compressing: {e.processedBytes / totalSize:p}");
};

// you can do this in Task.Run(() => ...) - Progress<T> will raise the event on the creation Thread
// pass IProgress to progress lambda, so that you can call "Report" on it
IProgress<(long processedBytes, long resultSize)> iprg = prg;
LZMA.Compress(fileStream, zipStream, LzmaSpeed.Fastest, DictionarySize.Medium,
    (cur, max) => iprg.Report((cur, max)));

@arjun-5670
Copy link

@kirides Can you explain how to use this SDK to extract the 7z file into a destination folder with individual files?

@kirides
Copy link

kirides commented Jul 10, 2023

@kirides Can you explain how to use this SDK to extract the 7z file into a destination folder with individual files?

tldr: Not with this kind of code

7z files are different from LZMA/2 compressed files.

7z files are archives (like .zip)
whereas LZMA/2 compressed files are more like "something.gz", or "dir.tar.xz"

@amfm0777
Copy link

amfm0777 commented Nov 2, 2023

Great Code!. I am trying to compress a folder. But i dont find any doc to do it. Writing filesize#1, file content#1, filesize#2, file content#2 does not seem to work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment