zlib-Deflate压缩算法

写在前边

最近在做一些关于网络优化的一些事情,涉及到对纯数据(Data、字符串、Json等)进行压缩,用到了Deflate压缩算法,这里就简单说一下如何用OC实现 Deflate

先看代码 (后面会给出详细解析)

Deflate.h

//
//  Deflate.h
//  CompressionTest

#import 

NS_ASSUME_NONNULL_BEGIN

@interface Deflate : NSObject

+ (NSData *)compress: (NSData *)data;
+ (NSData *)decompress: (NSData *)data;
@end

NS_ASSUME_NONNULL_END

Deflate.m

//
//  Deflate.m
//  CompressionTest

#import "Deflate.h"

#include 

#define CHUNK 16384

@implementation Deflate

/**
 Deflate 压缩
 @param data 需要压缩的数据
 @return 返回压缩数据
 */
+ (NSData *)compress:(NSData *)data {
    
    if ([data length] == 0) return data;
    // 初始化 z_stream
    z_stream stream;
    stream.zalloc = Z_NULL;
    stream.zfree = Z_NULL;
    stream.opaque = Z_NULL;
    stream.total_out = 0;
    stream.next_in = (Bytef *)[data bytes];
    stream.avail_in = (int)[data length];
    
    // Compresssion Levels:
    //   Z_NO_COMPRESSION
    //   Z_BEST_SPEED
    //   Z_BEST_COMPRESSION
    //   Z_DEFAULT_COMPRESSION
    
    if (deflateInit2(&stream,
                     Z_DEFAULT_COMPRESSION,
                     Z_DEFLATED,
                     -15,
                     MAX_MEM_LEVEL,
                     Z_DEFAULT_STRATEGY) != Z_OK) return nil;
    NSMutableData * compressed = [NSMutableData dataWithLength: CHUNK];
    do {
        if (stream.total_out >= [compressed length])
            // 追加长度
            [compressed increaseLengthBy:CHUNK];
        stream.next_out = [compressed mutableBytes] + stream.total_out;
        stream.avail_out = (uint)[compressed length] - (uint)stream.total_out;
        deflate(&stream, Z_FINISH);
        
    } while (stream.avail_out == 0);
    deflateEnd(&stream);
    [compressed setLength: stream.total_out];
    return [NSData dataWithData:compressed];
}


/**
 Deflate 解压缩

 @param data 需要解压数据
 @return 已经解压的数据
 */
+ (NSData *)decompress:(NSData *)data {
    
    if ([data length] == 0) return data;
    // 初始化 z_stream
    z_stream strm;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.total_out = 0;
    strm.next_in = (Bytef *)[data bytes];
    strm.avail_in = (int)[data length];

    unsigned full_length = (int)[data length];
    unsigned half_length = (int)[data length] / 2;
    NSMutableData * decompressed = [[NSMutableData alloc]
                                    initWithLength: full_length + half_length];
    BOOL done = false;
    int status;
    
    if (inflateInit2(&strm, -15) != Z_OK) return nil;
    while (!done) {
        if (strm.total_out >= [decompressed length])
            [decompressed increaseLengthBy: half_length];
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = (uint)[decompressed length] - (uint)strm.total_out;
        
        status = inflate(&strm, Z_SYNC_FLUSH);
        if (status == Z_STREAM_END)
            done = YES;
        else if (status != Z_OK)
            break;
    }
    if (inflateEnd(&strm) != Z_OK) return nil;
    if (done) {
        [decompressed setLength: strm.total_out];
        return [NSData dataWithData:decompressed];
    }
    return nil;
}

@end

相关概念

deflate(RFC1951):一种压缩算法,使用LZ77和哈弗曼进行编码;
zlib(RFC1950):一种格式,是对deflate进行了简单的封装,他也是一个实现库(delphi中有zlib,zlibex)
gzip(RFC1952):一种格式,也是对deflate进行的封装。

gzip = gzip头 + deflate编码的实际内容 + gzip
zlib = zlib头 + deflate编码的实际内容 + zlib

基础数据结构

z_stream : 压缩算法,压缩程度以及输入输出buffer和长度等都保存在这里,可以理解为压缩上下文。

常用的函数

deflateInit : 参数比较少,里面的实现其实是调用的deflateInit2

deflateInit2: 压缩初始化的基础函数,有很多参数,下面会重点介绍。

deflate : 压缩函数。

deflateEnd : 压缩完成以后,释放空间,但是注意,仅仅是释放deflateInit中申请的空间,自己申请的空间还是需要自己释放。

inflateInit : 解压初始化函数,内部调用的inflateInit2

inflateInit2 : 解压初始化的基础函数,后面重点介绍。

infalte : 解压函数。

inflateEnd : 同deflateEnd作用类似。

compress : 全部附加选项默认压缩,内部调用compress2。

compress2 : 带level的压缩方式。

uncompress : 解压缩。

压缩函数介绍

deflateInit2 : 初始化函数

函数原型为 :

int ZEXPORT deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, intstrategy)

z_stream:这个是压缩上下文,我们依照官方给的例子进行初始化

strm.zalloc = NULL; 
strm.zfree = NULL; 
strm.opaque = NULL;

strm.next_in = 你的待压缩数据
strm.next_out = 压缩以后数据存储的buffer
strm.avail_in = 待压缩数据的长度
strm.avail_out = 压缩数据存储buffer的长度.

level: 压缩的等级,目前有四个值

#define Z_NO_COMPRESSION         0 	//不压缩
#define Z_BEST_SPEED             1	//速度优先,可以理解为最低限度的压缩.
#define Z_BEST_COMPRESSION       9	//压缩优先,但是速度会有些慢
#define Z_DEFAULT_COMPRESSION  (-1) //默认选项,compress里面用的就是这个选项
/* compression levels */

method: 值只有一个,当前唯一的defalte压缩方法,用于以后扩展

#define Z_DEFLATED   8
/* The deflate compression method (the only one supported in this version) */

windowBits: 窗口比特数

* 	-(15 ~ 8) : 纯deflate压缩
* 	+(15 ~ 8) : 带zlib头和尾
* 	> 16 : 带gzip头和尾

memLevel: 目前只有一个选项,MAX_MEM_LEVEL,无非是运行过程中对内存使用的限制.

/* Maximum value for memLevel in deflateInit2 */
#ifndef MAX_MEM_LEVEL
#  ifdef MAXSEG_64K
#    define MAX_MEM_LEVEL 8
#  else
#    define MAX_MEM_LEVEL 9
#  endif
#endif

strategy :用于调整压缩算法,直接给默认就行Z_DEFAULT_STRATEGY.

#define Z_FILTERED            1	//用于由filter(或者称为predictor)生成的数据
#define Z_HUFFMAN_ONLY        2	//用于强制哈夫曼编码(不做字符匹配)
#define Z_RLE                 3	//限制匹配长度为1
#define Z_FIXED               4	//阻止使用动态哈夫曼编码,从而允许获得更简单的解码
#define Z_DEFAULT_STRATEGY    0	//用于普通数据
/* compression strategy; see deflateInit2() below for details */

Z_FILTERED,用于由filter(或者称为predictor)生成的数据.过滤的数据包含很多小的随机数据。这种情况下,压缩算法能够获得更好的压缩效果。该选项可以强制更多的哈夫曼编码和更少的字符匹配。有时候可以作为Z_DEFAULT_STRATEGY和Z_HUFFMAN_ONLY的折衷。
Z_FIXED,阻止使用动态哈夫曼编码,从而允许获得更简单的解码。
strategy参数只影响压缩比,而不会影响到压缩输出的正确性,因此没有正确的设置也不要紧。

deflate 压缩函数

函数原型 :

ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));

解压函数介绍

inflateInit2 初始化

原型函数

inflateInit2(z_streampstrm, int windowBits)

strm: 和deflate一样,初始化三个回调以后即可,有的参考文档说还需要初始化第四个选项,具体记不清哪个了,不过我试过以后发现貌似不用。

windownBits : 含义和deflateInit2一样,而且一定要对应起来.

inflate 解压

函数原型

ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush));

z_streamp : 四个参数.

strm.next_in = 你的待解压数据
strm.next_out = 解压以后数据存储的buffer
strm.avail_in = 待解压数据的长度
strm.avail_out = 解压数据存储buffer的长度.

flush : 和deflate一样,如果是Z_NO_FLUSH说明还有数据没有解压,如果是Z_FINISH说明这是最后一包待解压数据.

inflateEnd: 释放上面两步骤里面申请的资源.

uncompress :

函数原型 :

ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen,const Bytef *source, uLong sourceLen));

dest : 解压以后的数据存在这里

destLen : dest 大小

source : 待解压数据buffer

sourceLen : 待解压数据长度

其实这个函数就是简单封装了inflateInit, inflate, inflateEnd.同样,这个函数只适合单独解压场景,不适合需要多次传入的场景.

相关链接:

https://yq.aliyun.com/articles/40985
https://github.com/tsolomko/SWCompression

你可能感兴趣的:(移动开发,IOS开发,iOS开发进阶)