GZIP是封装了Deflate压缩的格式文件;Deflate使用了无压缩、Huffman+LZ77进行压缩;解压是Inflate,Huffman包括静态Huffman压缩和动态Huffman压缩两种模式。
Java语言实现了GZIP格式解析、Inflate的静态Huffman解压缩、CRC32校验 算法。
gzip文件格式解析代码
BinaryInputStream bis = new BinaryInputStream(bytes); // 二进制字节流读取类
Header header = new Header(); // 读取GZIP header
header.ID1 = bis.ReadUInt8(); // GZIP ID1
header.ID2 = bis.ReadUInt8(); // GzIP ID2
header.CM = bis.ReadUInt8();
header.Flag = bis.ReadUInt8();
header.Time = bis.ReadUnix32TimeStamp();
header.Xfl = bis.ReadUInt8();
header.OS = bis.ReadUInt8();
gzip.header = header;
Console.printlnf("ID=%xb%xb", header.ID1, header.ID2); // 打印
Console.printlnf("CM=%xb", header.CM);
Console.printlnf("Flag=%xb", header.Flag);
Console.printlnf("MTime=%S", header.Time);
Console.printlnf("XFL=%xb", header.Xfl);
Console.printlnf("OS=%xb", header.OS);
if ( (header.Flag & 0b00001000) != 0) { // 如果标志位是文件名
String filename = bis.ReadCString(); // 读取字节流until 0->char[]->string
Console.printlnf("filename=%S", filename);
}
else // 其他待实现
throw new java.lang.IllegalArgumentException(header.Flag +"");
byte[] result ; // 解压后的字节
int data_len ; // 解压前数据长度
int crc; // 程序计算解压后的CRC32(见上篇文章)
if (header.CM == 8) { // deflate
data_len = bytes.length - bis.GetPosition() - 8;
byte[] data = bis.ReadBytes(data_len); // 解压缩前数据
result = Deflate.uncompress(data); // 解压缩,返回解压后字节流
crc = CRC.CRC32(result); // 计算循环冗余码
}
else // 待实现
throw new java.lang.IllegalArgumentException(header.CM +"");
gzip.crc = bis.ReadUInt32(); // gzip文件自身存储的crc32值
gzip.isize = bis.ReadInt32();
System.out.println("gzip crc="+ Long.toHexString(gzip.crc) +",calc-crc=" + Integer.toHexString( crc) );
解析结果如下:
显示了GZIP标志、压缩方法、压缩时间、原始文件名、操作系统类型、CRC校验值
GZIP中存储的CRC32值(gzip crc)==解压后计算的CRC32值(calc crc)。(0xa93145a2)
Inflate -静态Huffman解压缩:
// 读取扩展Code
private static int ReadExtCode(BitsInputStream bis, int len) {
bis.setOrder(BitOrder.LeftIsHigh);
return bis.ReadBits(len);
}
// 读取距离
private static int ReadDistance(BitsInputStream bis) {
bis.setOrder(BitOrder.RightIsHigh);
return bis.ReadBits(5);
}
// 读取Code
private static int ReadCode(BitsInputStream bis) {
bis.setOrder(BitOrder.RightIsHigh);
int code = bis.ReadBits(7);
Integer value = FixHuffmanTable_7.get(code); // 7位长查表
if (value == null) {
int ext = bis.ReadBit();
code = (code << 1 | ext);
value = FixHuffmanTable_8.get(code); // 8位长查表
if (value == null) {
ext = bis.ReadBit();
code = (code << 1 | ext);
value = FixHuffmanTable_9.get(code); // 9位长查表
if (value == -1)
throw new java.lang.IllegalArgumentException(code + "");
}
}
return value;
}
// Deflate解压缩
public static byte[] uncompress(final byte[] _input) throws IOException {
IntArrayBuffer baos = new IntArrayBuffer(); // 输出窗口
// 位流读取类
BitsInputStream bis = new BitsInputStream(_input);
while (true) {
int bFinal = bis.ReadBits(1); // 读取Deflate头,0 – 还有后续子块;1 – 该子块是最后一块。
int bType = bis.ReadBits(2); // 读取Deflate头,00 – 不压缩;01 – 静态Huffman编码压缩;10 – 动态Huffman编码压缩;11 – 保留
if (bType == 0) { // 无压缩,未实现
int len = bis.ReadBits(16);
int nlen = bis.ReadBits(16);
assert len + nlen == 65535;
throw new java.lang.UnsupportedOperationException(bType + "");
}
else if (bType == 1) { // fixed Huffman
while (true) {
int value = ReadCode(bis); // 读取Huffman code
// 根据literal范围判断
if (value >= 0 && value <= 255) // literal
baos.Write(value);
else if (value == 256) // 结束标志
break ;
else if (value >= 257 && value <= 285) { // 距离或长度
int length = LengthExtraCodeLengthsTable.get(value);
int bits = LengthExtraCodeBitsTable.get(value);
if (bits != 0) {
int ext = ReadExtCode(bis, bits); // 读取长度
length = length + ext;
}
value = ReadDistance(bis); // 读取距离
int distance = DistanceExtraCodeLengthsTable.get(value);
bits = DistanceExtraCodeBitsTable.get(value);
if (bits != 0) {
int ext =ReadExtCode(bis , bits); // 读取距离
distance = distance + ext;
}
// LZ77滑动窗口计算获取量
int[] arr = baos.GetInts();
int d = arr.length - distance;
if (d < 0) {
d = 0;
length = length + distance - arr.length;
}
// 读取滑动窗口,写入到结果
for (int i=0; i<length; i++) {
int m = arr[ d + i];
baos.Write(m);
arr = baos.GetInts();
}
}
else
throw new java.lang.IllegalArgumentException(value+ "");
}
}
else if (bType == 2) { // dynamic huffman 动态Huffman待实现
throw new java.lang.UnsupportedOperationException(bType + "");
}
else
throw new java.lang.IllegalArgumentException(bType + "");
if (bFinal == 1) // 最后一个块
break ;
}
// 结果
int[] b = baos.GetInts();
byte[] result = new byte[b.length];
for (int i=0; i<b.length; i++)
result[i] = (byte) b[i];
return result;
测试结果如下:
将字符串"abcabcabcabcabcabcabcabcabcabcabcabc1111"写入到文件,
运行gzip程序(系统程序)压缩,
使用Java程序对gzip文件解析并显示解压后的内容。 (gzip使用了静态huffman)。