Stream 通用Lisp中数据压缩的流接口

Stream 通用Lisp中数据压缩的流接口,stream,compression,common-lisp,Stream,Compression,Common Lisp,在中,有一个非常有用的函数make decompressing stream,它提供了一个接口(在后台使用灰色流)来透明地解压缩从提供的流读取的数据。这允许我编写一个函数read tag(它从结构化二进制数据流中读取一个“tag”,很像Common Lisp的read函数从流中读取一个Lisp“form”),该函数同时处理压缩和未压缩数据,例如: ;; For uncompressed data: (read-tag in-stream) ;; For compressed data: (rea

在中,有一个非常有用的函数
make decompressing stream
,它提供了一个接口(在后台使用灰色流)来透明地解压缩从提供的流读取的数据。这允许我编写一个函数
read tag
(它从结构化二进制数据流中读取一个“tag”,很像Common Lisp的
read
函数从流中读取一个Lisp“form”),该函数同时处理压缩和未压缩数据,例如:

;; For uncompressed data:
(read-tag in-stream)
;; For compressed data:
(read-tag (chipz:make-decompressing-stream 'chipz:zlib in-stream))
据我所知,相关压缩库的API没有提供(现成的)等效接口来执行反向任务。我自己如何实现这样一个接口?让我们称之为
makecompressingstream
。它将与我自己的补充
write tag
功能一起使用,并提供与阅读相同的好处:

;; For uncompressed-data:
(write-tag out-stream current-tag)
;; For compressed data:
(write-tag (make-compressing-stream 'salza2:zlib-compressor out-stream)
                 current-tag)
在salza2的文档(链接在上面)的概述中,它说:“salza2提供了一个创建压缩程序对象的接口。该对象充当八位字节(单个八位字节或八位字节向量)的接收器,是压缩数据格式的八位字节源。压缩的八位字节数据提供给用户定义的回调,该回调可以将其写入流、复制到另一个向量等。“就我目前的目的而言,我只需要zlib和gzip格式的压缩,这两种格式都提供了标准压缩器

因此,我认为可以这样做:首先,将我的“tag”对象转换为八进制向量,然后使用
salza2:compress octet vector
对其进行压缩,第三,提供一个回调函数,将压缩后的数据直接写入文件。通过阅读,我认为第一步可以使用
flexi-streams:with output to sequence
-参见-但是我确实不确定回调函数,尽管查看了salza2的源代码。但问题是:一个标签可以包含任意数量的任意嵌套标签,这种结构的“叶子”标签每个都可以承载相当大的负载;换句话说,一个标签可以包含相当多的数据

因此,标签->未压缩八位字节->压缩八位字节->文件转换在理想情况下需要分块执行,这就提出了一个我不知道如何回答的问题,即:压缩格式-AIUI-倾向于在它们的头文件中存储有效负载数据的校验和;如果我一次压缩一个数据块,并将每个压缩的数据块附加到输出文件中,那么每个数据块肯定会有一个头和校验和,而不是整个标记数据的一个头和校验和,这就是我想要的?我怎样才能解决这个问题?还是已经由salza2处理了


谢谢你的帮助,抱歉说得太多:)

据我所知,你不能直接从一个文件中解压多个块

(defun bytes (&rest elements)
    (make-array (length elements) 
     :element-type '(unsigned-byte 8)
     :initial-contents elements))

(defun compress (chunk &optional mode)
  (with-open-file (output #P"/tmp/compressed"
                          :direction :output
                          :if-exists mode
                          :if-does-not-exist :create
                          :element-type '(unsigned-byte 8))
    (salza2:with-compressor (c 'salza2:gzip-compressor
                               :callback (salza2:make-stream-output-callback output))
      (salza2:compress-octet-vector chunk c))))

(compress (bytes 10 20 30) :supersede)
(compress (bytes 40 50 60) :append)   
现在,
/tmp/compressed
包含两个连续的压缩数据块。 调用
decompress
仅读取第一个块:

(chipz:decompress nil 'chipz:gzip #P"/tmp/compressed")
=> #(10 20 30)
查看
chipz
的源代码,流是使用内部缓冲区读取的,这意味着第一个块后面的字节可能已经读取,但没有解压缩。这就解释了为什么在同一个流上使用两个连续的
解压
调用时,第二个调用会出现EOF错误

(with-open-file (input #P"/tmp/compressed" 
                       :element-type '(unsigned-byte 8))
  (list
   #1=(multiple-value-list(ignore-errors(chipz:decompress nil 'chipz:gzip input)))
   #1#))

=> ((#(10 20 30))
    (NIL #<CHIPZ:PREMATURE-END-OF-STREAM {10155E2163}>))
(打开文件(输入#P)/tmp/compressed)
:元素类型(无符号字节8))
(名单
#1=(多值列表(忽略错误(chipz:decompressnil'chipz:gzip输入)))
#1#))
=> ((#(10 20 30))
(无)
我不知道数据应该有多大,但如果出现问题,您可能需要更改解压缩算法,以便当我们处于
done
状态时(请参见inflate.lisp),返回足够的数据以将剩余字节作为新块进行处理。或者,您可以压缩到不同的文件中,并使用类似TAR的归档格式(请参阅)