Skip to content

Instantly share code, notes, and snippets.

@robert-nix
Last active December 18, 2017 06:37
Show Gist options
  • Save robert-nix/240c06600728d6b726a5c89cb4370c5e to your computer and use it in GitHub Desktop.
Save robert-nix/240c06600728d6b726a5c89cb4370c5e to your computer and use it in GitHub Desktop.
DEFLATE using a preset dictionary with SharpZipLib (and the equivalent zlib code)

I came across a deflate stream I needed to inflate from C# and figured I would need to compile in zlib since there didn't appear to be any API for using raw deflate with a preset dictionary. As it turns out, ICSharpCode.SharpZipLib can do this, you just have to write a fake zlib header to make its Inflater ask for a dictionary.

using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Checksums;
public class DictionaryExample
{
Inflater m_inflater;
void InitInflater()
{
if (m_inflater != null)
{
return;
}
m_inflater = new Inflater(false);
// Read dictionary from embedded resource
byte[] dict;
using (var dictStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("packet.dict"))
using (var ms = new MemoryStream())
{
dictStream.CopyTo(ms);
dict = ms.GetBuffer();
}
// Calculate checksum of dictionary
var adler = new Adler32();
adler.Update(dict);
long dictAdler = adler.Value;
// Write fake zlib header
byte[] header = new byte[6];
// 7 = 32k Window, 8 = DEFLATE, b = FDICT | FLEVEL_DEFAULT, b = checksum
ushort hword = 0x78bb;
header[0] = (byte)(hword >> 8);
header[1] = (byte)(hword & 0xff);
header[2] = (byte)(dictAdler >> 24);
header[3] = (byte)(dictAdler >> 16);
header[4] = (byte)(dictAdler >> 8);
header[5] = (byte)(dictAdler);
// Let inflater read our header
m_inflater.SetInput(header);
byte[] dummy = new byte[1];
int hdWritten = m_inflater.Inflate(dummy);
if (hdWritten != 0)
{
throw new Exception("Expected inflating header to decode 0 bytes");
}
// Now inflater can read the dictionary
m_inflater.SetDictionary(dict);
}
public void Inflate(byte[] dst, byte[] src, int srcIndex, int srcCount)
{
InitInflater();
m_inflater.SetInput(src, srcIndex, srcCount);
int dstWritten = m_inflater.Inflate(dst);
if (dstWritten != dst.Length)
{
throw new Exception("inflate didn't decode enough bytes");
}
// To discard any SYNC_FLUSH stuff, we have to read another block
byte[] dummy = new byte[1];
while (!m_inflater.IsNeedingInput)
{
m_inflater.Inflate(dummy);
}
}
}
#include <zlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
int ret, dictLen, len, flush;
FILE *dictFile, *packetFile;
char *dict, *packet;
z_stream strm;
char dst[0x8000];
dictFile = fopen("packet.dict", "rb");
fseek(dictFile, 0, SEEK_END);
dictLen = ftell(dictFile);
dict = (char *)malloc(dictLen);
fseek(dictFile, 0, SEEK_SET);
fread(dict, 1, dictLen, dictFile);
packetFile = fopen("packet.zlib", "rb");
fseek(packetFile, 0, SEEK_END);
len = ftell(packetFile);
packet = (char *)malloc(len);
fseek(packetFile, 0, SEEK_SET);
fread(packet, 1, len, packetFile);
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
ret = inflateInit2(&strm, -15);
if (ret != Z_OK) {
printf("inflateInit2 failed: %d\n", ret);
return ret;
}
ret = inflateSetDictionary(&strm, dict, dictLen);
if (ret != Z_OK) {
printf("inflateSetDictionary failed: %d\n", ret);
return ret;
}
strm.next_in = packet;
strm.avail_in = len;
strm.next_out = dst;
strm.avail_out = 0x8000;
ret = inflate(&strm, 1);
if (ret != Z_OK) {
printf("inflate failed: %d\n", ret);
return ret;
}
fwrite(dst, 1, strm.total_out, stdout);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment