文章首发于个人博客
背景
使用fphttpclient调用一个http接口,但接口返回的响应体是经gzip压缩过的。这本是一个很常用,也很基础的功能,不想却如历劫般充满磨难。
历劫
初劫
场景很常用,也很基础,本着能偷懒就偷懒的原则,向AI进行提问。
AI给出的答案是使用ZStream单元的Tdecompressionstream。但是,AI给的示例第二个传参的数据类型怎么和方法声明不一样?继续问AI,AI就道歉,再给示例。
本以为几轮勾通下来问题就解决了,不曾想,恶梦才刚刚开始。AI每次都先道歉,然后信誓旦旦的说这次肯定可以,结果每次都不行。到后来,AI一会说是版本问题,一会说是数据问题......其实那些问题一开始就是排除掉的,但AI就是死鸭子嘴硬,真是气得令人口吐芬芳。结果。到最后,这货果然开始摆烂、甩锅,说什么它只是个模型,反正它尽力了,它不知道,问他不如你自己去网上查靠谱......浪费一上午时间,结果就来个让自己去网上查,那要你AI有个卵用!
再劫
午饭后,换个方式,先让AI去官方的gitlab查下有没有相关bug,结果果然有!然后顺藤摸瓜再查下解决方案,但是没有查到明确的解决方式,反倒是AI又开始一本正经胡说八道了,一不小心,半下午又没了!
罢了,一直被这货瞎忽悠,还不如自己静下心来深入了解下。一番了解加测试后,发现是Tdecompressionstream并不能识别gzip的格式。
复劫
既然问题出在gzip的格式上,那就先简单了解下gzip格式吧,懒得自己去搜,又问了AI,这次倒是说得有模有样,而且和测试的数据也都对得上,于是就追问了下如何解决。结果,又不知不觉被这货一本正经胡说八道忽悠瘸了,等发觉时已经深夜了。
劫中劫
官方的不能直接用,那就自己写个能用的。Tdecompressionstream是基于zlib的,而且有现成的底层功能可调用,那就仿一个支持gzip的,思路也很简单:把gzip的特征剥离后,剩下的就是原始的deflate,这个zlib肯定是支持的。
历经磨难,终于还是没写出能用的,看来一知半解就去搞还是行不通的,总不能像当初搞sm3、sm4那样对着规范研究几天再说吧!
劫散
对啊!虽说当初确实研究了好几天规范,但最终的实现还是参考了现成的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无所不能的,不是蠢就是坏