三方库设计说明

1 需求场景分析

一个用于提供流式压缩和解压功能、整体压缩和解压功能。

2 三方库对外提供的特性

(1) 支持流式压缩。

(2) 支持压缩和解压功能。

(3) 支持设置压缩头信息(仅支持 GZIP 压缩文件类型)。

(4) 支持压缩开始前预设字典。

3 License分析

4 依赖分析

仓颉 std 下的 math、collection

5 特性设计文档

5.1 核心特性1
5.1.1 特性介绍
支持流式压缩。
5.1.2 实现方案
采用两种算法模式,adler32、crc32数据校验

a) LZ77 算法

b) Huffman算法
5.1.3 接口设计

💡 Stream 流压缩和解压缩基类。

成员函数 入参 返回值 作用描述
init Unit 初始化 Stream
isInbufEmpty Bool 判断内部输出缓冲区是否为空,为空后,调用解压缩接口前需先重新设置输入数据
isHaveOutData Bool 检查输出缓冲区中是否有数据
setInBuf buf: Array Int64 设置输入缓冲区
setInBuf buf: Array, start: Int64, len: Int64 Int64 设置输入缓冲区
setOutBuf buf: Array Int64 设置输出缓冲区
setOutBuf buf: Array, start: Int64, len: Int64 Int64 设置输出缓冲区
resetOutBuf Unit 重置输出缓冲区状态
getOutDataLength Int64 返回输出缓冲区中数据长度
getTotalOut Int64 返回总输出数数据字节数
getTotalIn Int64 返回总输入数据字节数

💡 GzHeader 当压缩文件格式是 GZIP 时,设置压缩头参数。

成员变量 类型 作用描述
text Int32 text 只有 0 和 1, 是个标志位,为 1 说明输入的数据是文本
time Int32 修改时间
xflags Int32 压缩程度(值为 2、4、0)
os Int32 操作系统(0-255)
extra ArrayList 额外字段
name ArrayList 文件名
comment ArrayList 注释
hcrc Bool 是否写入 gzip 头的校验值到 gzip 头中
成员函数 入参 返回值 作用描述
init 初始化 GzHeader

💡 Deflate 此类继承 Stream, 实现压缩功能,提供流式压缩接口 deflateInit 、 deflate 、 deflateBound 、 setGzipHeader 、 setDictionary

成员函数 入参 返回值 作用描述
init 初始化 deflate
deflateInit wrap!: WrapType = ZLIB, level!: UInt32 = LEVEL_DEFAULT_COMPRESSION, wbits!: UInt32 = DEF_WINDOW_BITS, mlevel!: UInt32 = DEF_MEM_LEVEL, strategy!: UInt32 = Z_DEFAULT_STRATEGY UInt32 初始化压缩参数、状态,为内部缓冲区申请内存
deflate flush: UInt32 UInt32 压缩数据
deflateEnd UInt32 deflate 结束
deflateBound sourceLen: Int64 Int64 根据源数据大小,计算压缩后数据最大尺寸,必须在 deflateInit 和 setGzipHeader 之后调用
setDictionary dict: Array UInt32 压缩开始前预设字典 (deflateInit 之后, deflate 之前调用)
setGzipHeader gzhead: GzHeader UInt32 压缩文件类型选择 GZIP 时,用来设置 gzip 格式头信息(deflateInit 之后 ,deflate 之前调用)

💡 Inflate 此类实现解压功能, 提供流式压缩接口 inflateInit 、 inflate 、 getGzipHeader 、 setDictionary 、 inflateSync 。

接口名 入参 返回值 作用描述
init 初始化 inflate
inflateInit wrap!: WrapType = ZLIB, wbits!:UInt32 = DEF_WINDOW_BITS, ischeck!: Bool = true UInt32 初始化解压参数、状态、内部缓冲区
inflate flush: UInt32 UInt32 解压数据
inflateEnd UInt32 inflate 结束
setDictionary dict: Array UInt32 设置字典
getGzipHeader gzhead: GzHeader UInt32 解压 gzip 格式文件后,通过此函数获取 gzip 格式文件头部信息
inflateSync UInt32 解压时跳过非法的压缩数据,从下一个全刷新点开始解压

💡 WrapType 压缩文件格式。

GZIP、ZLIB、DEFLATE

5.1.4 展示示例
from zlib4cj import zlib.* 
from std import collection.* 
from std import io.*

main() { 
    var fileName: String = "../../../../README.md"
    var data: Array<UInt8> = readFile(fileName) 
    // Normal data test deflateStored, result: success
    testDeflateStored(data)  
}

func testDeflateStored(data: Array<UInt8>): Unit { 
    compressAndUncompressTestStored(data, "this is dictionary")
}

func compressAndUncompressTestStored(data: Array<UInt8>, strDictionary: String): Unit { 
    let buf: Array<UInt8> = compressTestStored(data, strDictionary) 
    let retbuf: ArrayList<UInt8> = uncompressTestStored(buf, strDictionary)
    checkResult(data, retbuf, "testDeflateStored: " + strDictionary)     
}

func compressTestStored(inbuf: Array<UInt8>, strDictionary: String): Array<UInt8> { 
    var deflate: Deflate = Deflate() 
    var outlen: Int64 = deflate.deflateBound(inbuf.size()) 
    var outbuf: Array<UInt8> = Array<UInt8>(outlen, item: 0) 
    deflate.setInBuf(inbuf) 
    deflate.setOutBuf(outbuf) 

    if (deflate.deflateInit(level: LEVEL_NO_COMPRESSION) != Z_OK) { 
        println("error compressTestStored() -> deflateInit(): ${deflate.message}") 
    }
    if (deflate.setDictionary(strDictionary.toArray()) != Z_OK) {
        println("error compressTestStored() -> setDictionary(): ${deflate.message}") 
    }
    if (deflate.deflate(Z_FINISH) != Z_STREAM_END) { 
        println("error compressTestStored() -> deflate(): ${deflate.message}") 
    }
    var retbuf: Array<UInt8> = outbuf[0..deflate.getOutDataLength()] 
    return retbuf 
}
func uncompressTestStored(inbuf: Array<UInt8>, strDictionary: String): ArrayList<UInt8> { 
    var retbuf: ArrayList<UInt8> = ArrayList<UInt8>() 
    var outbuf: Array<UInt8> = Array<UInt8>(inbuf.size(), item: 0) 
    var inflate: Inflate = Inflate() 
    inflate.setInBuf(inbuf) 
    inflate.setOutBuf(outbuf) 
    if (inflate.inflateInit() != Z_OK) { 
        println("error uncompressTestStored() -> inflateInit(): ${inflate.message}") 
    }
    var ret: UInt32
    while (true) { 
        ret = inflate.inflate(Z_NO_FLUSH)
        match { 
            case ret == Z_NEED_DICT =>
                if (inflate.setDictionary(strDictionary.toArray()) != Z_OK) { 
                    println("error uncompressTestStored() -> setDictionary(): ${inflate.message}") 
                } 
            case ret == Z_OK => 
                if (inflate.isHaveOutData()) { 
                    retbuf.appendAll(outbuf[0..inflate.getOutDataLength()]) 
                    inflate.resetOutBuf() 
                } 
            case ret == Z_STREAM_END => 
                if (inflate.isHaveOutData()) {
                    retbuf.appendAll(outbuf[0..inflate.getOutDataLength()]) 
                    inflate.resetOutBuf() 
                }
                break 
            case _ => 
                println("error uncompressTestStored() -> inflate(): ${inflate.message}") 
                break 
        } 
    }
    return retbuf 
}
func readFile(path: String): Array<UInt8> { 
    var data = Array<UInt8>()
    var fs = FileStream(path, AccessMode.Read)
    var readnum: Int64 = 0
    if (fs.openFile()) {
        data = fs.readAllBytes()
    }
    return data
}   
func compareBuffer(input: Array<UInt8>, uncompr: ArrayList<UInt8>): Bool {
    if (input.size() != uncompr.size()) {
        return false
    }
    for (i in 0..input.size()) {
        if (input[i] != uncompr[i]) {
            return false
        }
    }
    return true
}
func checkResult(input: Array<UInt8>, uncompr: ArrayList<UInt8>, desc: String): Unit {
    if (compareBuffer(input, uncompr)) {
        print("successed: ${desc}\n")
    } else {
        print("failed: ${desc}\n")
    }
}

运行结果如下:

successed: testDeflateStored: this is dictionary
5.2 核心特性2
5.2.1 特性介绍
此类提供一个整体的压缩和解压缩接口,用于压缩和解压缩整个数据。
5.2.2 实现方案
使用流式压缩进行封装。
5.2.3 接口设计

💡 Zlib 提供一个整体的压缩和解压缩接口。

成员函数 入参 返回值 作用描述
compress inbuf: Array, level!: UInt32 = LEVEL_DEFAULT_COMPRESSION, wrap!: WrapType = ZLIB ArrayList 压缩
uncompress inbuf: Array, wrap!: WrapType = ZLIB ArrayList 解压
5.2.4 展示示例
// EXEC: cjc %import-path %L %l %f
// EXEC: ./main

from zlib4cj import zlib.*
from std import collection.* 
from std import io.*

main() { 
    var fileName: String = "../../../../README.md"
    var data: Array<UInt8> = readFile(fileName) 
    // wrap: ZLIB success
    testCompressAndUncompress(data)     
}

func testCompressAndUncompress(data: Array<UInt8>): Unit {
    let buf: ArrayList<UInt8> = Zlib.compress(data)
    let arr: Array<UInt8> = Array<UInt8>(buf.size(), { i => buf[i] })
    let result: ArrayList<UInt8> = Zlib.uncompress(arr)
    checkResult(data, result, "testCompressAndUncompress")     
}
func readFile(path: String): Array<UInt8> { 
    var data = Array<UInt8>()
    var fs = FileStream(path)
    var readnum: Int64 = 0
    if (fs.openFile()) {
        data = fs.readAllBytes()
    }
    return data
}   
func compareBuffer(input: Array<UInt8>, uncompr: ArrayList<UInt8>): Bool {
    if (input.size() != uncompr.size()) {
        return false
    }
    for (i in 0..input.size()) {
        if (input[i] != uncompr[i]) {
            return false
        }
    }
    return true
}
func checkResult(input: Array<UInt8>, uncompr: ArrayList<UInt8>, desc: String): Unit {
    if (compareBuffer(input, uncompr)) {
        print("successed: ${desc}\n")
    } else {
        print("failed: ${desc}\n")
    }
}

运行结果如下:

successed: testCompressAndUncompress