Darryl
Darryl

Reputation: 857

How do I use a dictionary with ZLIB.NET?

I am trying to use a dictionary with the ZLIB .NET library but the ZStream member inflateSetDictionary always returns Z_STREAM_ERROR. I have tracked this down to a sub call to Inflate.inflateSetDictionary which tests if (z.istate.mode == DICT0)

Does anyone know how to use dictionaries with this library or know of any good examples. A simplified version of my code is shown below...

public class Form1 : System.Windows.Forms.Form
{
    static private string sDictionary = "VALUE1,VALUE2,VALUE3";

    class GZOutputStream : ZOutputStream
    {
        public GZOutputStream(Stream in_Renamed)
            : base(in_Renamed)
        {

            byte[] dictionary = System.Text.ASCIIEncoding.ASCII.GetBytes(sDictionary);
            z.inflateSetDictionary(dictionary, dictionary.Length);
        }

        public GZOutputStream(Stream in_Renamed, int level)
            : base(in_Renamed, level)
        {

            byte[] dictionary = System.Text.ASCIIEncoding.ASCII.GetBytes(sDictionary);
            z.deflateSetDictionary(dictionary, dictionary.Length);
        }
    }


    class GZInputStream : ZInputStream
    {
        public GZInputStream(Stream in_Renamed)
            : base(in_Renamed)
        {

            byte[] dictionary = System.Text.ASCIIEncoding.ASCII.GetBytes(sDictionary);
            z.inflateSetDictionary(dictionary, dictionary.Length);
        }

        public GZInputStream(Stream in_Renamed, int level)
            : base(in_Renamed, level)
        {

            byte[] dictionary = System.Text.ASCIIEncoding.ASCII.GetBytes(sDictionary);
            z.deflateSetDictionary(dictionary, dictionary.Length);
        }
    }


    public static void CopyStream(System.IO.Stream input, System.IO.Stream output)
    {
        byte[] buffer = new byte[2000];
        int len;
        while ((len = input.Read(buffer, 0, 2000)) > 0)
        {
            output.Write(buffer, 0, len);
        }
        output.Flush();
    }

    private void compressFile(string inFile, string outFile)
    {
        FileStream outFileStream = new System.IO.FileStream(outFile, System.IO.FileMode.Create);
        GZOutputStream outZStream = new GZOutputStream(outFileStream, 1); // zlibConst.Z_DEFAULT_COMPRESSION);
        System.IO.FileStream inFileStream = new System.IO.FileStream(inFile, System.IO.FileMode.Open);          
        try
        {
            CopyStream(inFileStream, outZStream);
        }
        finally
        {
            outZStream.Close();
            outFileStream.Close();
            inFileStream.Close();
        }
    }


    private void decompressFile(string inFile, string outFile)
    {
        FileStream outFileStream = new FileStream(outFile, FileMode.Create);
        GZOutputStream outZStream = new GZOutputStream(outFileStream);
        FileStream inFileStream = new FileStream(inFile, FileMode.Open);            
        try
        {
            CopyStream(inFileStream, outZStream);
        }
        finally
        {
            outZStream.Close();
            outFileStream.Close();
            inFileStream.Close();
        }
    }

}

Upvotes: 1

Views: 1083

Answers (2)

Eamon Nerbonne
Eamon Nerbonne

Reputation: 48066

I managed to get zlib compression with dictionary to work based on DotNetZip (nuget, github).

Since the api calls requires neither obvious, well documented, nor short, I placed the wrapper in its own nuget package ZlibWithDictionary (nuget, github).

Usage is simple:

var compressed_byte_array = DeflateCompression.ZlibCompressWithDictionary(
    bytes_array_to_compress, 
    CompressionLevel.Default, 
    null /*use default window size in bits; possible values 9-15*/, 
    CompressionStrategy.Default, 
    byte_array_of_dictionary
);
var decompressed_byte_array = DeflateCompression.ZlibDecompressWithDictionary(
    compressed_byte_array,
    byte_array_of_dictionary
);

If you prefer to manually use the appropriate zlib apis, you can peruse the source for the DeflateCompression wrapper.

Upvotes: 1

Darryl
Darryl

Reputation: 857

Although I haven't completley solved my problem I wanted to share my progress...

When decompressing the data stream you must wait until Z_NEED_DICT is returned by the inflate(..) function, therefor I have added the following section of code to the ZLIB classes ZInputStream and ZOutputStream to check the error value returned from z.inflate...

            if (err == zlibConst.Z_NEED_DICT)
            {
                byte[] dictionary = System.Text.ASCIIEncoding.ASCII.GetBytes(sDictionary);
                z.inflateSetDictionary(dictionary, dictionary.Length);
                err = z.inflate(flush_Renamed_Field);
            }

Additionaly there appears to be an error in for following section of code from the Inflate.inflate function...

                    case DICT4: 

                    if (z.avail_in == 0)
                        return r; r = f;

                    z.avail_in--; z.total_in++;
                     z.istate.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & unchecked((int)0xff000000L);
                    z.istate.mode = DICT3;
                    goto case DICT3;

In this code the value of z.istate.need gets set to a value 0xffffffffxx000000, this is incorrect as the top bits should be zero e.g. 0x00000000xx0000.

The following change fixes this problem..

z.istate.need = ((z.next_in[z.next_in_index++] & 0xff) << 24) & 0xff000000L;

Upvotes: 0

Related Questions