-
Notifications
You must be signed in to change notification settings - Fork 4
/
api.lisp
73 lines (67 loc) · 2.84 KB
/
api.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
(in-package 3bz)
(defun decompress (context state)
(etypecase state
(gzip-state
(decompress-gzip context state))
(zlib-state
(decompress-zlib context state))
(deflate-state
(decompress-deflate context state))))
(defun replace-output-buffer (state buffer)
(unless (or (zerop (ds-output-offset state))
(ds-output-overflow state))
;; we don't create/fill window until output buffer overflows, so
;; would need to do that here. error for now until someone needs
;; that ability...
(error "can't switch buffers without filling old one yet."))
(setf (ds-output-buffer state) buffer)
(setf (ds-output-offset state) 0)
(setf (ds-output-overflow state) nil))
(defun decompress-vector (compressed &key (format :zlib) (start 0) (end (length compressed)) output)
"decompress octet-vector COMPRESSED using
FORMAT (:deflate,:zlib,:gzip). If output is supplied, it should be an
octet-vector large enough to hold entire uncompressed output.
Returns buffer containing decompressed data (OUTPUT if supplied) and #
of octets decompressed."
(let ((parts nil)
(state (ecase format
(:gzip (make-gzip-state))
(:zlib (make-zlib-state))
(:deflate (make-deflate-state))))
(rc (make-octet-vector-context compressed :start start :end end)))
(if output
(progn
(setf (ds-output-buffer state) output)
(setf (ds-output-offset state) 0)
(let ((c (decompress rc state)))
(unless (ds-finished state)
(cond
((ds-input-underrun state)
(error "incomplete ~a stream" format))
((ds-output-overflow state)
(error "not enough space to decompress ~a stream" format))
(t (error "?"))))
(values output c)))
(progn
(loop for out = (make-array (min (- end start) 32768)
:element-type 'octet)
then (make-array (* 2 (length out)) :element-type 'octet)
do (replace-output-buffer state out)
(let ((c (decompress rc state)))
(assert (not (ds-input-underrun state)))
(if (zerop c)
(assert (ds-finished state))
(push (cons out c) parts)))
until (ds-finished state))
(let* ((s (reduce '+ parts :key 'cdr))
(b (make-array s :element-type 'octet)))
(loop for start = 0 then (+ start c)
for (p . c) in (nreverse parts)
do (replace b p :start1 start :end2 c))
(values b (length b)))))))
(defun finished (state)
(ds-finished state))
(defun input-underrun (state)
(ds-input-underrun state))
(defun output-overflow (state)
(ds-output-overflow state))