# -*- coding: utf-8 -*- # ============================================================ # gzip - 单文件 gzip 解码器(RFC 1952 + RFC 1951 deflate) # # OOP 范式对齐 zipfile: 数据由 GzipFile 对象持有并负责释放; # 内部位流/Huffman 助手为模块级私有函数。 # # 用法: # gz: gzip.GzipFile | t.CPtr = gzip.GzipFile("train-images-idx3-ubyte.gz") # if gz is None or gz.ok == 0: ... # buf: bytes = gz.read_all() # 内部 malloc, read_all 返回即解压完成 # n: t.CSizeT = gz.dst_len # 原始长度(由对象持有, close 时释放) # gz.close() # # 覆盖 stored/fixed/dynamic 三种 deflate 块; 不解 CRC。 # ============================================================ import t, c import stdlib import string import w32.fileio as fileio from stdint import * # ============================================================ # GzipFile # ============================================================ class GzipFile: ok: t.CInt # 1 = 可正常使用 err: t.CInt # 错误码(0 无, <0 出错) src: BYTEPTR # 整个 .gz 文件的内存副本(构造时读入, close 释放) src_len: t.CSizeT dst: BYTEPTR # 解压输出缓冲(read_all 生成, close 释放) dst_len: t.CSizeT # 解压结果长度 _need_close_src: t.CInt # 是否持有 src(读失败时为 0) def __init__(self, path: str): self.ok = 0 self.err = 0 self.src = None self.src_len = 0 self.dst = None self.dst_len = 0 self._need_close_src = 0 if path is None: self.err = -1 return f: fileio.File | t.CPtr = fileio.File(path, fileio.MODE.R) if f.closed: self.err = -2 # 打不开文件 return sz: LONGLONG = f.size() if sz < 18: f.close() self.err = -3 # 文件过小 return self.src_len = t.CSizeT(sz) # read_all 契约: 最多读 max_count-1 字节并补 NUL; 故按 src_len+1 申请与请求 buf: BYTEPTR = stdlib.malloc(self.src_len + 1) if buf is None: f.close() self.err = -4 return got: LONG = f.read_all(buf, ULONG(self.src_len + 1)) f.close() if got <= 0 or t.CSizeT(got) != self.src_len: stdlib.free(buf) self.err = -5 # 读取失败或被截断 return # gzip 魔数校验 ub: t.CUnsignedChar | t.CPtr = buf if ub[0] != 0x1F or ub[1] != 0x8B or ub[2] != 8: stdlib.free(buf) self.err = -6 return self.src = buf self._need_close_src = 1 self.ok = 1 def read_all(self) -> BYTEPTR: """解压整个成员。返回内部持有的输出缓冲(或 None); 长度见 self.dst_len。""" if self.ok == 0 or self.src is None: return None if self.dst is not None: return self.dst # 已解压过 want: t.CUInt32T = gzip_isize(self.src, self.src_len) cap: t.CSizeT = t.CSizeT(want) if cap == 0: cap = self.src_len * 64 # 无 ISIZE 时保守初值 if cap < 1024: cap = 1024 # 保险起见: ISIZE 与实际不符时报"目标缓冲不足", 就翻倍重试(正常情况下一次成功) out: BYTEPTR = None n: LONG = 0 while True: out = stdlib.malloc(cap) if out is None: self.err = -7 return None n = _gzip_decode(self.src, self.src_len, out, cap) if n == -9 or n == -18 or n == -23: stdlib.free(out) out = None if cap >= 1073741824: self.err = t.CInt(n) return None cap = cap * 2 continue break if n <= 0: stdlib.free(out) self.err = t.CInt(n) # n 为负错误码, 原样保存 return None self.dst = out self.dst_len = t.CSizeT(n) return out def close(self): if self._need_close_src != 0 and self.src is not None: stdlib.free(self.src) if self.dst is not None: stdlib.free(self.dst) self.src = None self.dst = None self.src_len = 0 self.dst_len = 0 self._need_close_src = 0 self.ok = 0 # ============================================================ # 公共访问器(跨模块读字段用函数, 避免 stub 字段访问陷阱) # ============================================================ def gzip_open(path: str) -> GzipFile | t.CPtr: return GzipFile(path) def gzip_ok(gz: GzipFile | t.CPtr) -> t.CInt: if gz is None: return 0 return gz.ok def gzip_err(gz: GzipFile | t.CPtr) -> t.CInt: if gz is None: return -1 return gz.err def gzip_dst_len(gz: GzipFile | t.CPtr) -> t.CSizeT: if gz is None: return 0 return gz.dst_len def gzip_read(gz: GzipFile | t.CPtr) -> BYTEPTR: if gz is None: return None return gz.read_all() def gzip_close(gz: GzipFile | t.CPtr): if gz is not None: gz.close() def gzip_decode_bytes(src: t.CPtr, src_len: t.CSizeT, dst: t.CPtr, dst_cap: t.CSizeT) -> LONG: """原始缓冲到缓冲的解压(不读文件), 供下载流程直接使用""" return _gzip_decode(src, src_len, dst, dst_cap) # ============================================================ # 位流状态(模块级, 单线程; 每次 _gzip_decode 重置) # ============================================================ _g_bits: t.CUInt64T = 0 _g_bitcnt: t.CInt = 0 _g_pos: t.CSizeT = 0 _g_src: t.CPtr = None _g_src_len: t.CSizeT = 0 def gzip_isize(src: t.CPtr, src_len: t.CSizeT) -> t.CUInt32T: """gzip 尾部 ISIZE(小端 u32), 即原始长度提示""" if src is None or src_len < 4: return 0 ub: t.CUnsignedChar | t.CPtr = src p: t.CSizeT = src_len - 4 # 小端 u32 拼装(64 位中间量后收窄) v64: t.CUInt64T = t.CUInt64T(ub[p]) \ + t.CUInt64T(ub[p + 1]) * 256 \ + t.CUInt64T(ub[p + 2]) * 65536 \ + t.CUInt64T(ub[p + 3]) * 16777216 return t.CUInt32T(v64) def _reset(src: t.CPtr, src_len: t.CSizeT): global _g_bits, _g_bitcnt, _g_pos, _g_src, _g_src_len _g_bits = 0 _g_bitcnt = 0 _g_pos = 0 _g_src = src _g_src_len = src_len def _fill(n: t.CInt) -> t.CInt: global _g_bits, _g_bitcnt, _g_pos, _g_src, _g_src_len sb: t.CUnsignedChar | t.CPtr = _g_src while _g_bitcnt < n: if _g_pos >= _g_src_len: return -1 _g_bits = _g_bits | (t.CUInt64T(sb[_g_pos]) << _g_bitcnt) _g_pos += 1 _g_bitcnt += 8 return 0 def _bits(n: t.CInt) -> t.CInt: global _g_bits, _g_bitcnt if _fill(n) != 0: return -1 v: t.CUInt64T = _g_bits & ((t.CUInt64T(1) << n) - 1) _g_bits = _g_bits >> n _g_bitcnt -= n return t.CInt(v) def _byte() -> t.CInt: return _bits(8) def _align(): global _g_bits, _g_bitcnt _g_bits = 0 _g_bitcnt = 0 def _decode(count: t.CInt | t.CPtr, first: t.CInt | t.CPtr, off: t.CInt | t.CPtr, sorted: t.CInt | t.CPtr) -> t.CInt: code: t.CInt = 0 ln: t.CInt = 1 while ln <= 15: b: t.CInt = _bits(1) if b < 0: return -1 code = (code << 1) | b if count[ln] != 0: d: t.CInt = code - first[ln] if d >= 0 and d < count[ln]: return sorted[off[ln] + d] ln += 1 return -1 def _build(first: t.CInt | t.CPtr, off: t.CInt | t.CPtr, count: t.CInt | t.CPtr) -> t.CInt: anylen: t.CInt = 0 l1: t.CInt = 1 while l1 <= 15: if count[l1] != 0: anylen = 1 l1 += 1 if anylen == 0: return -1 left: t.CInt = 1 l2: t.CInt = 1 while l2 <= 15: left = left << 1 if count[l2] > left: return -1 left = left - count[l2] l2 += 1 code: t.CInt = 0 l3: t.CInt = 1 while l3 <= 15: code = (code + count[l3 - 1]) << 1 first[l3] = code l3 += 1 offsum: t.CInt = 0 l4: t.CInt = 1 while l4 <= 15: off[l4] = offsum offsum += count[l4] l4 += 1 return 0 def _collect(len_arr: t.CInt | t.CPtr, cnt: t.CInt | t.CPtr, sorted_arr: t.CInt | t.CPtr, nsym: t.CInt, pos: t.CInt | t.CPtr): string.memset(cnt, 0, 17 * 4) s1: t.CInt = 0 while s1 < nsym: if len_arr[s1] != 0: cnt[len_arr[s1]] += 1 s1 += 1 run: t.CInt = 0 l5: t.CInt = 1 while l5 <= 15: pos[l5] = run run += cnt[l5] l5 += 1 s2: t.CInt = 0 while s2 < nsym: if len_arr[s2] != 0: sorted_arr[pos[len_arr[s2]]] = s2 pos[len_arr[s2]] += 1 s2 += 1 def _len_extra(code: t.CInt) -> t.CInt: if code == 285: return 0 if code >= 281: return 5 if code >= 277: return 4 if code >= 273: return 3 if code >= 269: return 2 if code >= 265: return 1 return 0 def _len_base(code: t.CInt) -> t.CInt: if code == 285: return 258 if code >= 281: return 131 + ((code - 281) << 5) if code >= 277: return 67 + ((code - 277) << 4) if code >= 273: return 35 + ((code - 273) << 3) if code >= 269: return 19 + ((code - 269) << 2) if code >= 265: return 11 + ((code - 265) << 1) return 3 + (code - 257) def _dist_extra(code: t.CInt) -> t.CInt: if code < 4: return 0 return (code - 4) // 2 + 1 def _dist_base(code: t.CInt) -> t.CInt: if code < 4: return code + 1 e: t.CInt = (code - 4) // 2 + 1 f: t.CInt = (1 << (e + 1)) + 1 if code % 2 == 0: return f return f + (1 << e) def _cl_order_set(arr: t.CInt | t.CPtr, idx: t.CInt, val: t.CInt): arr[idx] = val # ============================================================ # 核心解压(模块级私有) # ============================================================ def _gzip_decode(src: t.CPtr, src_len: t.CSizeT, dst: t.CPtr, dst_cap: t.CSizeT) -> LONG: if src is None or dst is None or src_len < 18 or dst_cap == 0: return -1 ub: t.CUnsignedChar | t.CPtr = src if ub[0] != 0x1F or ub[1] != 0x8B or ub[2] != 8: return -2 _reset(src, src_len) flg: t.CInt = t.CInt(ub[3]) hdr_len: t.CSizeT = 10 if flg & 0x04 != 0: xlen: t.CSizeT = t.CSizeT(ub[hdr_len]) | (t.CSizeT(ub[hdr_len + 1]) << 8) hdr_len += 2 + xlen if flg & 0x08 != 0: while hdr_len < src_len and ub[hdr_len] != 0: hdr_len += 1 hdr_len += 1 if flg & 0x10 != 0: while hdr_len < src_len and ub[hdr_len] != 0: hdr_len += 1 hdr_len += 1 if flg & 0x02 != 0: hdr_len += 2 if hdr_len >= src_len: return -3 global _g_pos _g_pos = hdr_len dout: t.CUnsignedChar | t.CPtr = dst wpos: t.CSizeT = 0 lit_len: t.CInt | t.CPtr = stdlib.malloc(288 * 4) lit_cnt: t.CInt | t.CPtr = stdlib.malloc(17 * 4) lit_first: t.CInt | t.CPtr = stdlib.malloc(17 * 4) lit_off: t.CInt | t.CPtr = stdlib.malloc(17 * 4) lit_sorted: t.CInt | t.CPtr = stdlib.malloc(288 * 4) dst_len: t.CInt | t.CPtr = stdlib.malloc(32 * 4) dst_cnt: t.CInt | t.CPtr = stdlib.malloc(17 * 4) dst_first: t.CInt | t.CPtr = stdlib.malloc(17 * 4) dst_off: t.CInt | t.CPtr = stdlib.malloc(17 * 4) dst_sorted: t.CInt | t.CPtr = stdlib.malloc(32 * 4) cl_len: t.CInt | t.CPtr = stdlib.malloc(19 * 4) cl_order: t.CInt | t.CPtr = stdlib.malloc(19 * 4) cl_cnt: t.CInt | t.CPtr = stdlib.malloc(17 * 4) cl_first: t.CInt | t.CPtr = stdlib.malloc(17 * 4) cl_off: t.CInt | t.CPtr = stdlib.malloc(17 * 4) cl_sorted: t.CInt | t.CPtr = stdlib.malloc(19 * 4) pos_tmp: t.CInt | t.CPtr = stdlib.malloc(17 * 4) if lit_len is None or lit_cnt is None or lit_first is None \ or lit_off is None or lit_sorted is None or dst_len is None \ or dst_cnt is None or dst_first is None or dst_off is None \ or dst_sorted is None or cl_len is None or cl_order is None \ or cl_cnt is None or cl_first is None or cl_off is None \ or cl_sorted is None or pos_tmp is None: return -4 # 全部表缓冲清零(初始 malloc 未清零; 防个别路径读到垃圾表导致提前/错乱收尾) string.memset(lit_len, 0, 288 * 4) string.memset(lit_cnt, 0, 17 * 4) string.memset(lit_first, 0, 17 * 4) string.memset(lit_off, 0, 17 * 4) string.memset(lit_sorted, 0, 288 * 4) string.memset(dst_len, 0, 32 * 4) string.memset(dst_cnt, 0, 17 * 4) string.memset(dst_first, 0, 17 * 4) string.memset(dst_off, 0, 17 * 4) string.memset(dst_sorted, 0, 32 * 4) string.memset(cl_len, 0, 19 * 4) string.memset(cl_order, 0, 19 * 4) string.memset(cl_cnt, 0, 17 * 4) string.memset(cl_first, 0, 17 * 4) string.memset(cl_off, 0, 17 * 4) string.memset(cl_sorted, 0, 19 * 4) string.memset(pos_tmp, 0, 17 * 4) _cl_order_set(cl_order, 0, 16) _cl_order_set(cl_order, 1, 17) _cl_order_set(cl_order, 2, 18) _cl_order_set(cl_order, 3, 0) _cl_order_set(cl_order, 4, 8) _cl_order_set(cl_order, 5, 7) _cl_order_set(cl_order, 6, 9) _cl_order_set(cl_order, 7, 6) _cl_order_set(cl_order, 8, 10) _cl_order_set(cl_order, 9, 5) _cl_order_set(cl_order, 10, 11) _cl_order_set(cl_order, 11, 4) _cl_order_set(cl_order, 12, 12) _cl_order_set(cl_order, 13, 3) _cl_order_set(cl_order, 14, 13) _cl_order_set(cl_order, 15, 2) _cl_order_set(cl_order, 16, 14) _cl_order_set(cl_order, 17, 1) _cl_order_set(cl_order, 18, 15) err: t.CInt = 0 final: t.CInt = 0 while final == 0 and err == 0: bfinal: t.CInt = _bits(1) btype: t.CInt = _bits(2) if bfinal < 0 or btype < 0: err = -5 break final = bfinal if btype == 0: _align() slen: t.CInt = _bits(16) snlen: t.CInt = _bits(16) if slen < 0 or snlen < 0: err = -6 break if (slen ^ snlen) != 0xFFFF: err = -7 break i: t.CSizeT = 0 while i < t.CSizeT(slen): ch: t.CInt = _byte() if ch < 0: err = -8 break if wpos >= dst_cap: err = -9 break dout[wpos] = t.CUnsignedChar(ch) wpos += 1 i += 1 if err != 0: break elif btype == 1: string.memset(lit_cnt, 0, 17 * 4) j: t.CInt = 0 while j < 144: lit_len[j] = 8 j += 1 while j < 256: lit_len[j] = 9 j += 1 while j < 280: lit_len[j] = 7 j += 1 while j < 288: lit_len[j] = 8 j += 1 _collect(lit_len, lit_cnt, lit_sorted, 288, pos_tmp) if _build(lit_first, lit_off, lit_cnt) != 0: err = -10 break else: hlit: t.CInt = _bits(5) + 257 hdist: t.CInt = _bits(5) + 1 hclen: t.CInt = _bits(4) + 4 if hlit < 0 or hdist < 0 or hclen < 0: err = -11 break string.memset(cl_len, 0, 19 * 4) k: t.CInt = 0 while k < hclen: vv: t.CInt = _bits(3) if vv < 0: err = -12 break cl_len[cl_order[k]] = vv k += 1 if err != 0: break _collect(cl_len, cl_cnt, cl_sorted, 19, pos_tmp) if _build(cl_first, cl_off, cl_cnt) != 0: err = -13 break string.memset(lit_len, 0, 288 * 4) string.memset(dst_len, 0, 32 * 4) sym_i: t.CInt = 0 while sym_i < hlit + hdist: sym: t.CInt = _decode(cl_cnt, cl_first, cl_off, cl_sorted) if sym < 0: err = -14 break if sym < 16: if sym_i < hlit: lit_len[sym_i] = sym else: dst_len[sym_i - hlit] = sym sym_i += 1 elif sym == 16: prev: t.CInt = 0 if sym_i > 0: if sym_i - 1 < hlit: prev = lit_len[sym_i - 1] else: prev = dst_len[sym_i - hlit - 1] rp: t.CInt = _bits(2) + 3 r2: t.CInt = 0 while r2 < rp and sym_i < hlit + hdist: if sym_i < hlit: lit_len[sym_i] = prev else: dst_len[sym_i - hlit] = prev sym_i += 1 r2 += 1 elif sym == 17: rp2: t.CInt = _bits(3) + 3 r3: t.CInt = 0 while r3 < rp2 and sym_i < hlit + hdist: if sym_i < hlit: lit_len[sym_i] = 0 else: dst_len[sym_i - hlit] = 0 sym_i += 1 r3 += 1 else: rp3: t.CInt = _bits(7) + 11 r4: t.CInt = 0 while r4 < rp3 and sym_i < hlit + hdist: if sym_i < hlit: lit_len[sym_i] = 0 else: dst_len[sym_i - hlit] = 0 sym_i += 1 r4 += 1 if err != 0: break _collect(lit_len, lit_cnt, lit_sorted, hlit, pos_tmp) if _build(lit_first, lit_off, lit_cnt) != 0: err = -15 break _collect(dst_len, dst_cnt, dst_sorted, hdist, pos_tmp) if _build(dst_first, dst_off, dst_cnt) != 0: err = -16 break if err != 0: break run: t.CInt = 1 while run == 1 and err == 0: sym: t.CInt = _decode(lit_cnt, lit_first, lit_off, lit_sorted) if sym < 0: err = -17 break if sym < 256: if wpos >= dst_cap: err = -18 break dout[wpos] = t.CUnsignedChar(sym) wpos += 1 elif sym == 256: run = 0 else: le: t.CInt = _len_extra(sym) ev: t.CInt = _bits(le) if ev < 0: err = -19 break length: t.CSizeT = t.CSizeT(_len_base(sym) + ev) ds: t.CInt = _decode(dst_cnt, dst_first, dst_off, dst_sorted) if ds < 0: err = -20 break de: t.CInt = _dist_extra(ds) dv: t.CInt = _bits(de) if dv < 0: err = -21 break dist: t.CSizeT = t.CSizeT(_dist_base(ds) + dv) if dist == 0 or dist > wpos: err = -22 break ci: t.CSizeT = 0 while ci < length: if wpos >= dst_cap: err = -23 break dout[wpos] = dout[wpos - dist] wpos += 1 ci += 1 if err != 0: break stdlib.free(lit_len) stdlib.free(lit_cnt) stdlib.free(lit_first) stdlib.free(lit_off) stdlib.free(lit_sorted) stdlib.free(dst_len) stdlib.free(dst_cnt) stdlib.free(dst_first) stdlib.free(dst_off) stdlib.free(dst_sorted) stdlib.free(cl_len) stdlib.free(cl_order) stdlib.free(cl_cnt) stdlib.free(cl_first) stdlib.free(cl_off) stdlib.free(cl_sorted) stdlib.free(pos_tmp) if err != 0: return LONG(err) # err 本身为负错误码, 不能再取负(否则被上层当成"长度>0 成功") return LONG(wpos)