最近在做一些关于网络优化的一些事情,涉及到对纯数据(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
: 解压缩。
函数原型为 :
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参数只影响压缩比,而不会影响到压缩输出的正确性,因此没有正确的设置也不要紧。
函数原型 :
ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush));
原型函数
inflateInit2(z_streampstrm, int windowBits)
strm
: 和deflate
一样,初始化三个回调以后即可,有的参考文档说还需要初始化第四个选项,具体记不清哪个了,不过我试过以后发现貌似不用。
windownBits
: 含义和deflateInit2
一样,而且一定要对应起来.
函数原型
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