/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2025. All rights reserved.
*/
/**
* @file
*
* Zlib class, which provides the compress and uncompress interfaces for decompressing and compressing the entire data.
*/
package cangjie_tpc::zlib4cj
import std.collection.*
import std.io.*
public enum ZlibType {
| ZLIB // cjlint-ignore !G.ENU.01 description
| GZIP // cjlint-ignore !G.ENU.01 description
| DEFLATE // cjlint-ignore !G.ENU.01 description
| AUTO_DETECT // cjlint-ignore !G.ENU.01 description
}
/**
* This class Provides the overall decompression interface.
*
* @since 0.28.4
*/
public class Zlib {
@Frozen
public static func compress(data: Array<Byte>,
wrap!: ZlibType = ZlibType.DEFLATE,
bufferSize!: Int64 = 1024,
level!: Int64 = 6,
memLevel!: UInt32 = DEF_MEM_LEVEL,
strategy!: UInt32 = Z_DEFAULT_STRATEGY,
dict!: ?Array<Byte> = None
): Array<Byte> {
if (data.size == 0) {
return NULL_ARR_UINT8
}
let lev = if (level < 0 || level > 9) {
6
} else {
level
}
let mem = if (memLevel < 1 || memLevel > 9) {
DEF_MEM_LEVEL
} else {
memLevel
}
let str = if (strategy < 0 || strategy > 4) {
Z_DEFAULT_STRATEGY
} else {
strategy
}
let bufsize = if (bufferSize <= 0) {
1024
} else {
bufferSize
}
let def: Deflate = Deflate()
match (wrap) {
case ZLIB => def.deflateInit2(lev, 15, mem, str)
case GZIP => def.deflateInit2(lev, 15 + 16, mem, str)
case DEFLATE => def.deflateInit2(lev, -15, mem, str)
case AUTO_DETECT => def.deflateInit2(lev, -15, mem, str)
}
if (let Some(v) <- dict) {
if (v.size == 0) {
throw Zlib4cjException("deflate error: dictionary is empty, but inflate need dictionary")
}
if (def.setDictionary(v) != Z_OK) {
throw Zlib4cjException("deflate error: ${def.message}")
}
}
let size = def.deflateBound(data.size)
let out = ByteBuffer(size)
var error = Z_OK
let buffer = Array<Byte>(bufsize, repeat: 0)
def.setInBuf(data)
while(def.avail_in > 0) {
def.setOutBuf(buffer)
error = def.deflate(Z_NO_FLUSH)
let len = def.pos_out
if (len > 0) {
out.write(buffer[0..len])
def.resetOutBuf()
}
if (error == Z_OK) {}
else if (error == Z_STREAM_END) {
break
}
else if (error == Z_BUF_ERROR) {
if (def.avail_out <= 0 ) {
break
}
}
else {
throw IOException(def.message) // cjlint-ignore !G.ERR.02 description
}
}
def.resetOutBuf()
def.setOutBuf(buffer)
while (true) {
error = def.deflate(Z_FINISH)
let len = def.pos_out
if (len > 0) {
out.write(buffer[0..len])
def.resetOutBuf()
}
if (error == Z_OK) {}
else if (error == Z_STREAM_END) {
break
}
else if (error == Z_BUF_ERROR) {
if (def.avail_out <= 0 && Z_FINISH != Z_FINISH) {
break
}
}
else {
throw IOException(def.message) // cjlint-ignore !G.ERR.02 description
}
}
def.deflateEnd()
return readToEnd(out)
}
@Frozen
public static func uncompress(data: Array<Byte>,
wrap!: ZlibType = ZlibType.AUTO_DETECT,
bufferSize!: Int64 = 1024,
dict!: ?Array<Byte> = None): Array<Byte> {
if (data.size == 0) {
return NULL_ARR_UINT8
}
let bufsize = if (bufferSize <= 0) {
1024
} else {
bufferSize
}
let inf = match (wrap) {
case ZLIB => Inflate(1)
case GZIP => Inflate(2)
case DEFLATE => Inflate(-1)
case AUTO_DETECT =>
if (data[0] == 0x1F && data[1] == 0x8B && data[2] == 0x08) {
Inflate(2)
} else if (
(data[0] & 15) != 8 ||
(data[0] >> 4) > 7 ||
Int64(Int64(data[0]) << 8 | Int64(data[1])) % 31 != 0
) {
Inflate(-1)
} else {
Inflate(1)
}
}
let out = ByteBuffer()
let buffer = Array<Byte>(bufsize, repeat: 0)
var error = Z_OK
inf.setInBuf(data)
while (inf.avail_in > 0 && error != Z_STREAM_END) {
inf.setOutBuf(buffer)
while(inf.avail_out > 0 && inf.avail_in > 0) {
error = inf.inflate(Z_NO_FLUSH)
if (error >= 0) {
if (error == Z_STREAM_END) {
break
}
if (error == Z_NEED_DICT) {
if (let Some(v) <- dict) {
if (v.size == 0) {
throw Zlib4cjException("inflate error: dictionary is empty, but inflate need dictionary")
}
if (inf.setDictionary(v) != Z_OK) {
throw Zlib4cjException("inflate error: ${error}, ${inf.message}")
}
} else {
throw Zlib4cjException("inflate error: ${error}, ${erroMsg(error)}, but no dictionary set")
}
}
} else if (error == Z_DATA_ERROR) {
let syncRet = inf.inflateSync()
if (syncRet == Z_OK) { // 成功找到同步点,恢复解压
continue
} else if (syncRet == Z_BUF_ERROR) { // 需要更多数据才能恢复
break
} else if (syncRet == Z_STREAM_END) { //恢复失败
throw Zlib4cjException("inflate error: ${error}, ${inf.message}")
}
} else {
throw Zlib4cjException("inflate error: ${error}, ${inf.message}")
}
}
out.write(buffer[0..(buffer.size - inf.avail_out)])
}
inf.inflateEnd()
inf.end()
return readToEnd(out)
}
}