Skip to content

Instantly share code, notes, and snippets.

@5lx
Last active June 3, 2025 17:55
Show Gist options
  • Select an option

  • Save 5lx/d988c5a4e57c0b3708960f14ef1d7db3 to your computer and use it in GitHub Desktop.

Select an option

Save 5lx/d988c5a4e57c0b3708960f14ef1d7db3 to your computer and use it in GitHub Desktop.
package main
/*
#cgo pkg-config: zlib
#cgo linux LDFLAGS: -lz
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
typedef struct {
z_stream zstrm;
int initialized;
} InflateContext;
// 初始化上下文 (raw deflate)
InflateContext* initInflateContext() {
InflateContext* ctx = malloc(sizeof(InflateContext));
if (!ctx) return NULL;
ctx->zstrm = (z_stream){.zalloc = Z_NULL, .zfree = Z_NULL, .opaque = Z_NULL};
// 使用 -MAX_WBITS 处理 raw deflate
if (inflateInit2(&ctx->zstrm, -MAX_WBITS) != Z_OK) {
free(ctx);
return NULL;
}
ctx->initialized = 1;
return ctx;
}
// 解压数据块 (优化版)
int inflateChunk(InflateContext* ctx,
const unsigned char* in, size_t in_len,
unsigned char** out, size_t* out_len) {
if (!ctx || !ctx->initialized) return Z_STREAM_ERROR;
// 设置输入缓冲区
ctx->zstrm.next_in = (Bytef*)in;
ctx->zstrm.avail_in = (uInt)in_len;
// 初始输出缓冲区大小(更合理的初始值)
size_t buf_size = in_len > 0 ? in_len * 3 : 1024;
unsigned char* buffer = malloc(buf_size);
if (!buffer) return Z_MEM_ERROR;
size_t total_out = 0;
int ret = Z_OK;
while (ctx->zstrm.avail_in > 0) {
// 设置输出位置
ctx->zstrm.next_out = buffer + total_out;
ctx->zstrm.avail_out = (uInt)(buf_size - total_out);
// 记录解压前的输出位置
size_t prev_out = total_out;
// 执行解压
ret = inflate(&ctx->zstrm, Z_SYNC_FLUSH);
total_out = buf_size - ctx->zstrm.avail_out;
// 处理返回状态
switch (ret) {
case Z_STREAM_END:
case Z_OK:
// 正常状态,继续处理输入
break;
case Z_BUF_ERROR:
// 输出缓冲区不足时需要扩展
if (total_out == prev_out) {
// 无进展说明需要更多输入,正常退出
ret = Z_OK;
goto finalize;
}
// 扩展输出缓冲区
buf_size *= 2;
unsigned char* new_buf = realloc(buffer, buf_size);
if (!new_buf) {
ret = Z_MEM_ERROR;
goto error;
}
buffer = new_buf;
continue;
default:
// 其他错误
goto error;
}
}
finalize:
// 分配精确大小的输出
if (total_out > 0) {
*out = malloc(total_out);
if (!*out) {
ret = Z_MEM_ERROR;
goto error;
}
memcpy(*out, buffer, total_out);
} else {
*out = NULL;
}
*out_len = total_out;
free(buffer);
return ret;
error:
free(buffer);
return ret;
}
// 释放资源
void freeInflateContext(InflateContext* ctx) {
if (ctx) {
if (ctx->initialized) inflateEnd(&ctx->zstrm);
free(ctx);
}
}
*/
import "C"
import (
"bytes"
"compress/gzip"
"encoding/hex"
"fmt"
"unsafe"
)
// Inflater 封装C解压器
type Inflater struct {
ctx *C.InflateContext
}
// NewInflater 创建新的解压器
func NewInflater() (*Inflater, error) {
ctx := C.initInflateContext()
if ctx == nil {
return nil, fmt.Errorf("failed to initialize inflate context")
}
return &Inflater{ctx: ctx}, nil
}
// Inflate 解压数据块(优化内存管理)
func (inf *Inflater) Inflate(data []byte) ([]byte, int, error) {
if len(data) == 0 {
return nil, 0, nil
}
var outPtr *C.uchar
var outLen C.size_t
ret := C.inflateChunk(
inf.ctx,
(*C.uchar)(unsafe.Pointer(&data[0])),
C.size_t(len(data)),
&outPtr,
&outLen,
)
// 处理可能的错误
if ret != C.Z_OK && ret != C.Z_STREAM_END && ret != C.Z_BUF_ERROR {
return nil, int(ret), fmt.Errorf("zlib error %d", ret)
}
// 处理空输出
if outLen == 0 {
return nil, int(ret), nil
}
// 将C分配的内存复制到Go切片
result := C.GoBytes(unsafe.Pointer(outPtr), C.int(outLen))
C.free(unsafe.Pointer(outPtr))
return result, int(ret), nil
}
// Close 释放解压器资源
func (inf *Inflater) Close() {
if inf.ctx != nil {
C.freeInflateContext(inf.ctx)
inf.ctx = nil
}
}
func main() {
// 创建测试数据
testData := []byte("Hello World! This is a test string for zlib inflation optimization. " +
"Repeating: Hello World! This is a test string for zlib inflation optimization.")
buf := new(bytes.Buffer)
gc := gzip.NewWriter(buf)
gc.Write(testData)
gc.Close()
fmt.Println("Original length:", len(testData))
fmt.Println("Compressed length:", buf.Len())
fmt.Println(hex.Dump(buf.Bytes()))
compressedData := buf.Bytes()[10:] // 跳过gzip头
// 分块推送压缩数据(更真实的块大小)
chunkSize := len(compressedData) / 5
compressedChunks := make([][]byte, 0, 5)
for i := 0; i < len(compressedData); i += chunkSize {
end := i + chunkSize
if end > len(compressedData) {
end = len(compressedData)
}
compressedChunks = append(compressedChunks, compressedData[i:end])
}
// 创建解压器
inflater, err := NewInflater()
if err != nil {
panic(err)
}
defer inflater.Close()
// 解压数据(收集所有解压结果)
var fullOutput []byte
for i, chunk := range compressedChunks {
decompressed, ret, err := inflater.Inflate(chunk)
if err != nil {
panic(err)
}
if len(decompressed) > 0 {
fullOutput = append(fullOutput, decompressed...)
}
fmt.Printf("Chunk %d: input %d bytes, output %d bytes, status %d\n",
i+1, len(chunk), len(decompressed), ret)
// 流结束测试
if ret == int(C.Z_STREAM_END) {
fmt.Println("Reached end of compressed stream")
break
}
}
// 验证解压结果
if string(fullOutput) == string(testData) {
fmt.Println("SUCCESS: Decompressed data matches original")
} else {
fmt.Println("FAILURE: Decompressed data does NOT match original")
fmt.Printf("Original length: %d, Decompressed length: %d\n",
len(testData), len(fullOutput))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment