使用Lazarus解压gzip

文章首发于个人博客

背景

使用fphttpclient调用一个http接口,但接口返回的响应体是经gzip压缩过的。这本是一个很常用,也很基础的功能,不想却如历劫般充满磨难。

历劫

初劫

场景很常用,也很基础,本着能偷懒就偷懒的原则,向AI进行提问。

AI给出的答案是使用ZStream单元的Tdecompressionstream。但是,AI给的示例第二个传参的数据类型怎么和方法声明不一样?继续问AIAI就道歉,再给示例。

本以为几轮勾通下来问题就解决了,不曾想,恶梦才刚刚开始。AI每次都先道歉,然后信誓旦旦的说这次肯定可以,结果每次都不行。到后来,AI一会说是版本问题,一会说是数据问题......其实那些问题一开始就是排除掉的,但AI就是死鸭子嘴硬,真是气得令人口吐芬芳。结果。到最后,这货果然开始摆烂、甩锅,说什么它只是个模型,反正它尽力了,它不知道,问他不如你自己去网上查靠谱......浪费一上午时间,结果就来个让自己去网上查,那要你AI有个卵用!

再劫

午饭后,换个方式,先让AI去官方的gitlab查下有没有相关bug,结果果然有!然后顺藤摸瓜再查下解决方案,但是没有查到明确的解决方式,反倒是AI又开始一本正经胡说八道了,一不小心,半下午又没了!

罢了,一直被这货瞎忽悠,还不如自己静下心来深入了解下。一番了解加测试后,发现是Tdecompressionstream并不能识别gzip的格式。

复劫

既然问题出在gzip的格式上,那就先简单了解下gzip格式吧,懒得自己去搜,又问了AI,这次倒是说得有模有样,而且和测试的数据也都对得上,于是就追问了下如何解决。结果,又不知不觉被这货一本正经胡说八道忽悠瘸了,等发觉时已经深夜了。

劫中劫

官方的不能直接用,那就自己写个能用的。Tdecompressionstream是基于zlib的,而且有现成的底层功能可调用,那就仿一个支持gzip的,思路也很简单:把gzip的特征剥离后,剩下的就是原始的deflate,这个zlib肯定是支持的。

历经磨难,终于还是没写出能用的,看来一知半解就去搞还是行不通的,总不能像当初搞sm3sm4那样对着规范研究几天再说吧!

劫散

对啊!虽说当初确实研究了好几天规范,但最终的实现还是参考了现成的c代码,fpc是小众中的小众,但c却是软件世界的基石啊,写pascal的话AI会一本正经胡说八道,那写c应该就只是代码搬运了吧。

AI给了基于zlib的解压gzip的示例,自己对照着翻译成pascal,结果果然成功了!

仅是解压http的响应,够用了:

pascal 复制代码
uses
  ZBase, PasZLib;

function GZipDecompression(Input, Output: TStream): boolean;
var
  z:      z_stream;
  ret:    integer;
  inBuf, outBuf: TBytes;
  n:      uint64;
  offset: integer;
  xlen:   uint16;
begin
  Result := False;
  //数据太短
  if Input.Size < 18 then
    Exit;
  SetLength(inBuf, Input.Size);
  Input.Position := 0;
  Input.Read(inBuf[0], Input.Size);
  //检查GZip魔数
  if (inBuf[0] <> $1F) or (inBuf[1] <> $8B) then
    Exit;
  //不支持的压缩方法
  if inBuf[2] <> $08 then
    Exit;
  offset := 10;
  //额外字段
  if (inBuf[3] and $04) <> 0 then
  begin
    if offset + 2 > Input.Size then
      Exit;
    xlen := inBuf[offset] or (inBuf[offset + 1] shl 8);
    offset := offset + 2 + xlen;
  end;
  //文件名
  if (inBuf[3] and $08) <> 0 then
  begin
    while (offset < Input.Size) and (inBuf[offset] <> 0) do
      Inc(offset);
    if offset < Input.Size then  //跳过0尾
      Inc(offset);
  end;
  //注释
  if (inBuf[3] and $10) <> 0 then
  begin
    while (offset < Input.Size) and (inBuf[offset] <> 0) do
      Inc(offset);
    if offset < Input.Size then  //跳过0尾
      Inc(offset);
  end;
  //头部CRC
  if (inBuf[3] and $02) <> 0 then
  begin
    offset := offset + 2;
  end;
  //头部错误
  if offset >= Input.Size then
    Exit;
  //解压后大小
  Input.Position := Input.Size - 4;
  Input.Read(n, 4);
  SetLength(outBuf, n);

  FillChar(z, SizeOf(z), 0);
  //按原始deflate解压
  ret := inflateInit2(z, -MAX_WBITS);
  if ret <> Z_OK then
    Exit;

  z.avail_in := Input.Size - offset - 8;
  z.next_in := @inBuf[offset];
  z.avail_out := n;
  z.next_out := @outBuf[0];

  ret := inflate(z, Z_FINISH);
  if ret <> Z_STREAM_END then
  begin  //解压失败
    inflateEnd(z);
    Exit;
  end;
  n := z.total_out;
  inflateEnd(z);
  Output.Write(outBuf[0], n);
  Result := True;
end;

总结

  • AI摆烂的能力一点也不输一本正经胡说八道的能力

  • AI在小众领域、非主流领域内,最擅长的就是一本正经胡说八道,不亲自尝试很难分辨真假,所以,在小众领域、非主流领域不要相信AI

  • AI不是银弹,吹虚AI无所不能的,不是蠢就是坏

相关推荐
CesareCheung3 小时前
高级测试面试中的 AI 面试题:从原理到实战全解析
ai编程
kyriewen6 小时前
GPT-6 发布当晚,三大 AI 集体宕机 4 小时——我扒完时间线,发现最该慌的不是宕机
人工智能·程序员·ai编程
却尘7 小时前
Agent Framework(3):看懂它到底在运行什么
aigc·ai编程
promiseThen8 小时前
LWC Workflow:用 7 个 Cursor Skill 搭一条 AI 协作开发流水线
前端·ai编程
昭昭日月明9 小时前
RAGFlow 入门,不用从零造轮子
python·ai编程
魔术师Grace9 小时前
AI 为什么会越改越坏?5个Tools 拆解编程 Agent
openai·agent·ai编程
这就是佬们吗9 小时前
不写Prompt,写Loop:AI编程的下一场范式迁移
人工智能·prompt·ai编程
OpenTiny社区10 小时前
太酷了!装上OpenTiny dsh‑genui这个插件,你的DeepSeek Harness点击就能干活了!
前端·ai编程
Bigger12 小时前
谁动了我的 URL?——记一次微前端"灵异 Bug"的排查实录
前端·ai编程·vue-router
用户56190350693312 小时前
多环境 API 怎么管理?dev/test/prod 一套规范
ai编程