Using Zlib with .NET CF 3.5

Aug 12, 2009 at 10:59 AM

I'm trying to use the dev kit DEBUG Ionic.Zlib.CF.dll with my .NET CF 3.5 project and I'm getting a PlatformNotSupportedException when I call the Ionic.Zlib.GZipStream constructor in this snippet:

             byte[] buffer = Encoding.UTF8.GetBytes(text);
                MemoryStream ms = new MemoryStream();
                using (Ionic.Zlib.GZipStream zip = new Ionic.Zlib.GZipStream(ms, Ionic.Zlib.CompressionMode.Compress, true))
                {
                    zip.Write(buffer, 0, buffer.Length);
                }

Is this because the Ionic.Zlib DLL is targetting .NET CF 2.0 and not .NET CF 3.5?

Is there a .NET CF 3.5 compatible version? I just need to deflate and inflate some strings so I don't need the full DotNetZip dll.

If anyone can shed some light on this it would be much appreciated.

 

Coordinator
Aug 12, 2009 at 1:24 PM
Edited Aug 12, 2009 at 1:25 PM

I would expect it to work.  Can you share the stacktrace?

Aug 12, 2009 at 1:39 PM

Hi Cheeso,

The stack trace is:

at System.Text.Encoding.GetDataItem()\r\n   at System.Text.CodePageEncoding..ctor(Int32 codepage)\r\n   at System.Text.Encoding.GetEncoding(Int32 codepage)\r\n   at System.Text.Encoding.GetEncoding(String name)\r\n   at Ionic.Zlib.GZipStream..cctor()\r\n   at DBT.helpers.Helper.Compress_Zlib(String text)\r\n

It's a CF 3.5 SP1 app targeting a Windows Mobile 5.0 device.

TIA

Coordinator
Aug 12, 2009 at 5:21 PM

Ah, ok.

The GZIP standard says that GZIP files have to use iso-8859-1 as the encoding for some of the things in the file headers.  In accordance with that, the GZipStream code tries to instantiate a System.Text.Encoding object for iso-8859-1, in order to be able to read or write gzip files.  This instantiation will fail if the code page is not present on the device.  It is not related to the version of the .NET CF you have installed. It is a code page that gets installed (or not) with the Device OS. 

There's no fix I can make in DotNetZip in order to allow this to work.  I'm not sure if you can install a new code page on the Windows Mobile device. 

To avoid the problem:

  • install the iso-8859-1 code page (as I said, not sure if this is even possible)
  • get a different device or OS that has the required code page installed
  • don't use GZIP.  If all you want to do is squish a string, there are other compressing streams in the Ionic.Zlib library that do not require ISO-8859-1 encoding. For example, the ZlibStream, which works like GZipStream, but lacks the headers. 

 

Aug 12, 2009 at 6:35 PM

Thanks for the answer and the awesome support!!

I tried the ZlibStream - I was hoping it would out perform the standard System.IO.Compression.GZipStream but it looks about the same. (but I am trying the impossible - trying to compress a jpg)

Is this the most efficient way of doing the compress? Bearing in mind it's running on a device.

     public static string Compress_Zlib(string text)
        {

            byte[] buffer = Encoding.UTF8.GetBytes(text);
            MemoryStream ms = new MemoryStream();

              using (Ionic.Zlib.ZlibStream zip = new Ionic.Zlib.ZlibStream(ms, Ionic.Zlib.CompressionMode.Compress, true))
                {
                    zip.Write(buffer, 0, buffer.Length);
                }

            ms.Position = 0;

            byte[] compressed = new byte[ms.Length];
            ms.Read(compressed, 0, compressed.Length);

            byte[] gzBuffer = new byte[compressed.Length + 4];
            System.Buffer.BlockCopy(compressed, 0, gzBuffer, 4, compressed.Length);
            System.Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gzBuffer, 0, 4);
            return Convert.ToBase64String(gzBuffer);

        }

Coordinator
Aug 12, 2009 at 7:16 PM

Ah, well if you are trying to compress a JPG, it's already compressed, so the additional compression won't be high.

I have a couple other suggestions though:

  • use the ZlibStream constructor that allows you to specify a CompressionLevel .  Specify CompressionLevel.BestCompression.  It should do a little better than the default, at the expense of CPU time.
  • Also, if you are starting with a JPG, then where do you get the string?  Maybe you converted it with Convert.ToBase64String() before trying to compress?   I would guess you'd be able to load the jpg directly as a byte array, in which case you could skip the Convert.ToBase64String() step I am imagining, and the Encoding.UTF8.GetBytes(text) step.  Still you will be compressing a JPG so you won't get much compression.
  • also - it looks like you are pre-fixing the size of the pre-compressed but post-encoded (UTF8-encoded) data to the compressed buffer.  Do you need that?   If you use a ZlibStream on the other end, you don't need the size.  ZLIB includes framing bytes so that it knows when the compressed bytes "stop".   But you may need the length for other reasons. . .   It is the length of the UNcompressed data - specifically the uncompressed UTF8 encoding of the string form of a JPG.   Not sure you really need that.  If you want to keep the size prefix, I would just allocate space in the buffer before compression, then compress, then write the length afterwards.  This would eliminate the allocation of one byte array, and the moving of a chunk of data.

 

public static string Compress_Zlib(byte[] jpg)
{
    MemoryStream ms = new MemoryStream();
    // dummy length
    ms.Write(BitConverter.GetBytes(1), 0, 4);
    using (Ionic.Zlib.ZlibStream zip = new Ionic.Zlib.ZlibStream(ms,
                                                                 Ionic.Zlib.CompressionMode.Compress,
                                                                 Ionic.Zlib.CompressionLevel.BestCompression,
                                                                 true))
    {
        zip.Write(jpg, 0, jpg.Length);
    }

    byte[] zbuffer = ms.ToArray();
    // The first 4 bytes contains 01 00 00 00, and following
    // that, the compressed data. 

    // one of these might be what you want:
    System.Buffer.BlockCopy(BitConverter.GetBytes(zbuffer.Length), 0, zbuffer, 0, 4);
    System.Buffer.BlockCopy(BitConverter.GetBytes(zbuffer.Length-4), 0, zbuffer, 0, 4);
    System.Buffer.BlockCopy(BitConverter.GetBytes(jpg.Length), 0, zbuffer, 0, 4);

    // is this step necessary?
    return Convert.ToBase64String(zbuffer);
}

 

Dec 17, 2009 at 9:17 PM

Chesso - you simply rock (just started using your library and had same problem).

Coordinator
Dec 18, 2009 at 4:24 PM

Aw shucks, thanks!  Glad it's working for you.

May 29, 2014 at 4:29 PM
Hi Cheeso,

I have the same problem as the poster above. I am receiving GZipStream compression from an Apache Tomcat and I need to decompress it on the embedded device 5.0 (without the required code page). Is there any solution to this now? Can we somehow skip the gzip header check or such (I saw something like infalteinit2 in zlib or such., which maybe skips headers..)? I already implemented your library on desktop version of the code and it's working (decompression on the fly and resuming download). In case there is no solution to this, can you direct me further how to solve this gzipstream receiving data?
May 29, 2014 at 5:38 PM
Alternative solution: I have managed to decompress gzip using icsharpcode.sharpziplib GzipStreamInput on the Windows CE 5.0, which wouldn't decompress with DotNetZip's GZipStream.