本篇文章记录下zlib
的DEFLATE
格式的解压缩技术,在项目开发中用到了此项技术,进过不短时间的查资料才完成解压需求,记录下来已被后续只需:
OC 版本
@interface NSData (STUnzipArchive)
- (NSData *)zlibDeflate;
@end
#import "NSData+STUnzipArchive.h"
#import <zlib.h>
@implementation NSData (STUnzipArchive)
- (NSData *)zlibDeflate
{
if ([self length] == 0) return self;
NSUInteger full_length = [self length];
NSUInteger half_length = [self length] / 2;
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
BOOL done = NO;
int status;
z_stream strm;
strm.next_in = (Bytef *)[self bytes];
strm.avail_in = (unsigned)[self length];
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
if (inflateInit (&strm) != Z_OK) return nil;
while (!done)
{
// Make sure we have enough room and reset the lengths.
if (strm.total_out >= [decompressed length])
[decompressed increaseLengthBy: half_length];
strm.next_out = [decompressed mutableBytes] + strm.total_out;
strm.avail_out = (uint)([decompressed length] - strm.total_out);
// Inflate another chunk.
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;
// Set real length.
if (done)
{
[decompressed setLength: strm.total_out];
return [NSData dataWithData: decompressed];
}
else return nil;
}
@end
Swift 版本
import Foundation
import zlib
class ZStream {
fileprivate static var c_version : UnsafePointer<Int8> = zlibVersion()
fileprivate(set) static var version : String = String(format: "%s", locale: nil, c_version)
fileprivate func makeError(_ res : CInt) -> NSError? {
var err = ""
switch res {
case 0: return nil
case 1: err = "stream end"
case 2: err = "need dict"
case -1: err = "errno"
case -2: err = "stream error"
case -3: err = "data error"
case -4: err = "mem error"
case -5: err = "buf error"
case -6: err = "version error"
default: err = "undefined error"
}
return NSError(domain: "deflateswift", code: -1, userInfo: [NSLocalizedDescriptionKey:err])
}
fileprivate var strm = z_stream()
fileprivate var deflater = true
fileprivate var initd = false
fileprivate var init2 = false
fileprivate var level = CInt(-1)
fileprivate var windowBits = CInt(15)
fileprivate var out = [UInt8](repeating: 0, count: 5000)
init() { }
func write(_ bytes : [UInt8], flush: Bool) -> (bytes: [UInt8], err: NSError?) {
var mutBytes = bytes
var res : CInt
if !initd {
if deflater {
if init2 {
res = deflateInit2_(&strm, level, 8, windowBits, 8, 0, ZStream.c_version, CInt(MemoryLayout<z_stream>.size))
} else {
res = deflateInit_(&strm, level, ZStream.c_version, CInt(MemoryLayout<z_stream>.size))
}
} else {
if init2 {
res = inflateInit2_(&strm, windowBits, ZStream.c_version, CInt(MemoryLayout<z_stream>.size))
} else {
res = inflateInit_(&strm, ZStream.c_version, CInt(MemoryLayout<z_stream>.size))
}
}
if res != 0{
return ([UInt8](), makeError(res))
}
initd = true
}
var result = [UInt8]()
strm.avail_in = CUnsignedInt(bytes.count)
strm.next_in = UnsafeMutablePointer(mutating: mutBytes)
repeat {
strm.avail_out = CUnsignedInt(out.count)
strm.next_out = UnsafeMutablePointer(mutating: out)+0
if deflater {
res = deflate(&strm, flush ? 1 : 0)
} else {
res = inflate(&strm, flush ? 1 : 0)
}
if res < 0 {
return ([UInt8](), makeError(res))
}
let have = out.count - Int(strm.avail_out)
if have > 0 {
result += Array(out[0...have-1])
}
} while (strm.avail_out == 0 && res != 1)
if strm.avail_in != 0 {
return ([UInt8](), makeError(-9999))
}
return (result, nil)
}
deinit {
guard initd else { return }
if deflater {
_ = deflateEnd(&strm)
} else {
_ = inflateEnd(&strm)
}
}
}
class DeflateStream : ZStream {
convenience init(level : Int) {
self.init()
self.level = CInt(level)
}
convenience init(windowBits: Int) {
self.init()
self.init2 = true
self.windowBits = CInt(windowBits)
}
convenience init(level : Int, windowBits: Int) {
self.init()
self.init2 = true
self.level = CInt(level)
self.windowBits = CInt(windowBits)
}
}
class InflateStream : ZStream {
override init() {
super.init()
deflater = false
}
convenience init(windowBits: Int) {
self.init()
self.init2 = true
self.windowBits = CInt(windowBits)
}
}
Swift
版本使用方法:
// 压缩数据
let dataStr = "789CEDD3BD0DC2301005E0B8B027000AA6F10834D730C24D93821192829B92583E075E5E84A25441E2954F9F7FE374DD869CB84A0FAA82652CCE22FAC4EA625370682A95F53093960A872AB3AB110B46AC4E864C99D56DEC61BE269CBE6ECD863FDBC5FCC6739499F98D47B1B17D5267058CA2C07C9E259B736C166ECCEE320559B265FAF664BFB3CC6C68EBC93B6B7FD3C69FEED02C96C3E9E753742B99BADFC90B0B72705C"
let deflated = dataStr.hexadecimal()
//最终的解压数据
var inflater = InflateStream()
var (inflated, err) = inflater.write([UInt8](deflated!), flush: true)
if err != nil{
fatalError("\(err!)")
}
let s = Data(bytes: inflated, count: inflated.count)
print(inflated,s)