Last active
June 3, 2025 17:55
-
-
Save 5lx/d988c5a4e57c0b3708960f14ef1d7db3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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