123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- ;;; GNU Guix --- Functional package management for GNU
- ;;; Copyright © 2019 Pierre Neidhardt <mail@ambrevar.xyz>
- ;;; Copyright © 2019 Ludovic Courtès <ludo@gnu.org>
- ;;;
- ;;; This file is part of GNU Guix.
- ;;;
- ;;; GNU Guix is free software; you can redistribute it and/or modify it
- ;;; under the terms of the GNU General Public License as published by
- ;;; the Free Software Foundation; either version 3 of the License, or (at
- ;;; your option) any later version.
- ;;;
- ;;; GNU Guix is distributed in the hope that it will be useful, but
- ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
- ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;;; GNU General Public License for more details.
- ;;;
- ;;; You should have received a copy of the GNU General Public License
- ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
- (define-module (guix lzlib)
- #:use-module (rnrs bytevectors)
- #:use-module (rnrs arithmetic bitwise)
- #:use-module (ice-9 binary-ports)
- #:use-module (ice-9 match)
- #:use-module (system foreign)
- #:use-module (guix config)
- #:use-module (srfi srfi-11)
- #:export (lzlib-available?
- make-lzip-input-port
- make-lzip-output-port
- make-lzip-input-port/compressed
- call-with-lzip-input-port
- call-with-lzip-output-port
- %default-member-length-limit
- %default-compression-level))
- ;;; Commentary:
- ;;;
- ;;; Bindings to the lzlib / liblz API. Some convenience functions are also
- ;;; provided (see the export).
- ;;;
- ;;; While the bindings are complete, the convenience functions only support
- ;;; single member archives. To decompress single member archives, we loop
- ;;; until lz-decompress-read returns 0. This is simpler. To support multiple
- ;;; members properly, we need (among others) to call lz-decompress-finish and
- ;;; loop over lz-decompress-read until lz-decompress-finished? returns #t.
- ;;; Otherwise a multi-member archive starting with an empty member would only
- ;;; decompress the empty member and stop there, resulting in truncated output.
- ;;; Code:
- (define %lzlib
- ;; File name of lzlib's shared library. When updating via 'guix pull',
- ;; '%liblz' might be undefined so protect against it.
- (delay (dynamic-link (if (defined? '%liblz)
- %liblz
- "liblz"))))
- (define (lzlib-available?)
- "Return true if lzlib is available, #f otherwise."
- (false-if-exception (force %lzlib)))
- (define (lzlib-procedure ret name parameters)
- "Return a procedure corresponding to C function NAME in liblz, or #f if
- either lzlib or the function could not be found."
- (match (false-if-exception (dynamic-func name (force %lzlib)))
- ((? pointer? ptr)
- (pointer->procedure ret ptr parameters))
- (#f
- #f)))
- (define-wrapped-pointer-type <lz-decoder>
- ;; Scheme counterpart of the 'LZ_Decoder' opaque type.
- lz-decoder?
- pointer->lz-decoder
- lz-decoder->pointer
- (lambda (obj port)
- (format port "#<lz-decoder ~a>"
- (number->string (object-address obj) 16))))
- (define-wrapped-pointer-type <lz-encoder>
- ;; Scheme counterpart of the 'LZ_Encoder' opaque type.
- lz-encoder?
- pointer->lz-encoder
- lz-encoder->pointer
- (lambda (obj port)
- (format port "#<lz-encoder ~a>"
- (number->string (object-address obj) 16))))
- ;; From lzlib.h
- (define %error-number-ok 0)
- (define %error-number-bad-argument 1)
- (define %error-number-mem-error 2)
- (define %error-number-sequence-error 3)
- (define %error-number-header-error 4)
- (define %error-number-unexpected-eof 5)
- (define %error-number-data-error 6)
- (define %error-number-library-error 7)
- ;; Compression bindings.
- (define lz-compress-open
- (let ((proc (lzlib-procedure '* "LZ_compress_open" (list int int uint64)))
- ;; member-size is an "unsigned long long", and the C standard guarantees
- ;; a minimum range of 0..2^64-1.
- (unlimited-size (- (expt 2 64) 1)))
- (lambda* (dictionary-size match-length-limit #:optional (member-size unlimited-size))
- "Initialize the internal stream state for compression and returns a
- pointer that can only be used as the encoder argument for the other
- lz-compress functions, or a null pointer if the encoder could not be
- allocated.
- See the manual: (lzlib) Compression functions."
- (let ((encoder-ptr (proc dictionary-size match-length-limit member-size)))
- (if (not (= (lz-compress-error encoder-ptr) -1))
- (pointer->lz-encoder encoder-ptr)
- (throw 'lzlib-error 'lz-compress-open))))))
- (define lz-compress-close
- (let ((proc (lzlib-procedure int "LZ_compress_close" '(*))))
- (lambda (encoder)
- "Close encoder. ENCODER can no longer be used as an argument to any
- lz-compress function. "
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-close ret)
- ret)))))
- (define lz-compress-finish
- (let ((proc (lzlib-procedure int "LZ_compress_finish" '(*))))
- (lambda (encoder)
- "Tell that all the data for this member have already been written (with
- the `lz-compress-write' function). It is safe to call `lz-compress-finish' as
- many times as needed. After all the produced compressed data have been read
- with `lz-compress-read' and `lz-compress-member-finished?' returns #t, a new
- member can be started with 'lz-compress-restart-member'."
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-finish (lz-compress-error encoder))
- ret)))))
- (define lz-compress-restart-member
- (let ((proc (lzlib-procedure int "LZ_compress_restart_member" (list '* uint64))))
- (lambda (encoder member-size)
- "Start a new member in a multimember data stream.
- Call this function only after `lz-compress-member-finished?' indicates that the
- current member has been fully read (with the `lz-compress-read' function)."
- (let ((ret (proc (lz-encoder->pointer encoder) member-size)))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-restart-member
- (lz-compress-error encoder))
- ret)))))
- (define lz-compress-sync-flush
- (let ((proc (lzlib-procedure int "LZ_compress_sync_flush" (list '*))))
- (lambda (encoder)
- "Make available to `lz-compress-read' all the data already written with
- the `LZ-compress-write' function. First call `lz-compress-sync-flush'. Then
- call 'lz-compress-read' until it returns 0.
- Repeated use of `LZ-compress-sync-flush' may degrade compression ratio,
- so use it only when needed. "
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-sync-flush
- (lz-compress-error encoder))
- ret)))))
- (define lz-compress-read
- (let ((proc (lzlib-procedure int "LZ_compress_read" (list '* '* int))))
- (lambda* (encoder lzfile-bv #:optional (start 0) (count (bytevector-length lzfile-bv)))
- "Read up to COUNT bytes from the encoder stream, storing the results in LZFILE-BV.
- Return the number of uncompressed bytes written, a positive integer."
- (let ((ret (proc (lz-encoder->pointer encoder)
- (bytevector->pointer lzfile-bv start)
- count)))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-read (lz-compress-error encoder))
- ret)))))
- (define lz-compress-write
- (let ((proc (lzlib-procedure int "LZ_compress_write" (list '* '* int))))
- (lambda* (encoder bv #:optional (start 0) (count (bytevector-length bv)))
- "Write up to COUNT bytes from BV to the encoder stream. Return the
- number of uncompressed bytes written, a strictly positive integer."
- (let ((ret (proc (lz-encoder->pointer encoder)
- (bytevector->pointer bv start)
- count)))
- (if (< ret 0)
- (throw 'lzlib-error 'lz-compress-write (lz-compress-error encoder))
- ret)))))
- (define lz-compress-write-size
- (let ((proc (lzlib-procedure int "LZ_compress_write_size" '(*))))
- (lambda (encoder)
- "The maximum number of bytes that can be immediately written through the
- `lz-compress-write' function.
- It is guaranteed that an immediate call to `lz-compress-write' will accept a
- SIZE up to the returned number of bytes. "
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-write-size (lz-compress-error encoder))
- ret)))))
- (define lz-compress-error
- (let ((proc (lzlib-procedure int "LZ_compress_errno" '(*))))
- (lambda (encoder)
- "ENCODER can be a Scheme object or a pointer."
- (let* ((error-number (proc (if (lz-encoder? encoder)
- (lz-encoder->pointer encoder)
- encoder))))
- error-number))))
- (define lz-compress-finished?
- (let ((proc (lzlib-procedure int "LZ_compress_finished" '(*))))
- (lambda (encoder)
- "Return #t if all the data have been read and `lz-compress-close' can
- be safely called. Otherwise return #f."
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (match ret
- (1 #t)
- (0 #f)
- (_ (throw 'lzlib-error 'lz-compress-finished? (lz-compress-error encoder))))))))
- (define lz-compress-member-finished?
- (let ((proc (lzlib-procedure int "LZ_compress_member_finished" '(*))))
- (lambda (encoder)
- "Return #t if the current member, in a multimember data stream, has
- been fully read and 'lz-compress-restart-member' can be safely called.
- Otherwise return #f."
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (match ret
- (1 #t)
- (0 #f)
- (_ (throw 'lzlib-error 'lz-compress-member-finished? (lz-compress-error encoder))))))))
- (define lz-compress-data-position
- (let ((proc (lzlib-procedure uint64 "LZ_compress_data_position" '(*))))
- (lambda (encoder)
- "Return the number of input bytes already compressed in the current
- member."
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-data-position
- (lz-compress-error encoder))
- ret)))))
- (define lz-compress-member-position
- (let ((proc (lzlib-procedure uint64 "LZ_compress_member_position" '(*))))
- (lambda (encoder)
- "Return the number of compressed bytes already produced, but perhaps
- not yet read, in the current member."
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-member-position
- (lz-compress-error encoder))
- ret)))))
- (define lz-compress-total-in-size
- (let ((proc (lzlib-procedure uint64 "LZ_compress_total_in_size" '(*))))
- (lambda (encoder)
- "Return the total number of input bytes already compressed."
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-total-in-size
- (lz-compress-error encoder))
- ret)))))
- (define lz-compress-total-out-size
- (let ((proc (lzlib-procedure uint64 "LZ_compress_total_out_size" '(*))))
- (lambda (encoder)
- "Return the total number of compressed bytes already produced, but
- perhaps not yet read."
- (let ((ret (proc (lz-encoder->pointer encoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-compress-total-out-size
- (lz-compress-error encoder))
- ret)))))
- ;; Decompression bindings.
- (define lz-decompress-open
- (let ((proc (lzlib-procedure '* "LZ_decompress_open" '())))
- (lambda ()
- "Initializes the internal stream state for decompression and returns a
- pointer that can only be used as the decoder argument for the other
- lz-decompress functions, or a null pointer if the decoder could not be
- allocated.
- See the manual: (lzlib) Decompression functions."
- (let ((decoder-ptr (proc)))
- (if (not (= (lz-decompress-error decoder-ptr) -1))
- (pointer->lz-decoder decoder-ptr)
- (throw 'lzlib-error 'lz-decompress-open))))))
- (define lz-decompress-close
- (let ((proc (lzlib-procedure int "LZ_decompress_close" '(*))))
- (lambda (decoder)
- "Close decoder. DECODER can no longer be used as an argument to any
- lz-decompress function. "
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-close ret)
- ret)))))
- (define lz-decompress-finish
- (let ((proc (lzlib-procedure int "LZ_decompress_finish" '(*))))
- (lambda (decoder)
- "Tell that all the data for this stream have already been written (with
- the `lz-decompress-write' function). It is safe to call
- `lz-decompress-finish' as many times as needed."
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-finish (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-reset
- (let ((proc (lzlib-procedure int "LZ_decompress_reset" '(*))))
- (lambda (decoder)
- "Reset the internal state of DECODER as it was just after opening it
- with the `lz-decompress-open' function. Data stored in the internal buffers
- is discarded. Position counters are set to 0."
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-reset
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-sync-to-member
- (let ((proc (lzlib-procedure int "LZ_decompress_sync_to_member" '(*))))
- (lambda (decoder)
- "Reset the error state of DECODER and enters a search state that lasts
- until a new member header (or the end of the stream) is found. After a
- successful call to `lz-decompress-sync-to-member', data written with
- `lz-decompress-write' will be consumed and 'lz-decompress-read' will return 0
- until a header is found.
- This function is useful to discard any data preceding the first member, or to
- discard the rest of the current member, for example in case of a data
- error. If the decoder is already at the beginning of a member, this function
- does nothing."
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-sync-to-member
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-read
- (let ((proc (lzlib-procedure int "LZ_decompress_read" (list '* '* int))))
- (lambda* (decoder file-bv #:optional (start 0) (count (bytevector-length file-bv)))
- "Read up to COUNT bytes from the decoder stream, storing the results in FILE-BV.
- Return the number of uncompressed bytes written, a non-negative positive integer."
- (let ((ret (proc (lz-decoder->pointer decoder)
- (bytevector->pointer file-bv start)
- count)))
- (if (< ret 0)
- (throw 'lzlib-error 'lz-decompress-read (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-write
- (let ((proc (lzlib-procedure int "LZ_decompress_write" (list '* '* int))))
- (lambda* (decoder bv #:optional (start 0) (count (bytevector-length bv)))
- "Write up to COUNT bytes from BV to the decoder stream. Return the
- number of uncompressed bytes written, a non-negative integer."
- (let ((ret (proc (lz-decoder->pointer decoder)
- (bytevector->pointer bv start)
- count)))
- (if (< ret 0)
- (throw 'lzlib-error 'lz-decompress-write (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-write-size
- (let ((proc (lzlib-procedure int "LZ_decompress_write_size" '(*))))
- (lambda (decoder)
- "Return the maximum number of bytes that can be immediately written
- through the `lz-decompress-write' function.
- It is guaranteed that an immediate call to `lz-decompress-write' will accept a
- SIZE up to the returned number of bytes. "
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-write-size (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-error
- (let ((proc (lzlib-procedure int "LZ_decompress_errno" '(*))))
- (lambda (decoder)
- "DECODER can be a Scheme object or a pointer."
- (let* ((error-number (proc (if (lz-decoder? decoder)
- (lz-decoder->pointer decoder)
- decoder))))
- error-number))))
- (define lz-decompress-finished?
- (let ((proc (lzlib-procedure int "LZ_decompress_finished" '(*))))
- (lambda (decoder)
- "Return #t if all the data have been read and `lz-decompress-close' can
- be safely called. Otherwise return #f."
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (match ret
- (1 #t)
- (0 #f)
- (_ (throw 'lzlib-error 'lz-decompress-finished? (lz-decompress-error decoder))))))))
- (define lz-decompress-member-finished?
- (let ((proc (lzlib-procedure int "LZ_decompress_member_finished" '(*))))
- (lambda (decoder)
- "Return #t if the current member, in a multimember data stream, has
- been fully read and `lz-decompress-restart-member' can be safely called.
- Otherwise return #f."
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (match ret
- (1 #t)
- (0 #f)
- (_ (throw 'lzlib-error 'lz-decompress-member-finished? (lz-decompress-error decoder))))))))
- (define lz-decompress-member-version
- (let ((proc (lzlib-procedure int "LZ_decompress_member_version" '(*))))
- (lambda (decoder)
- (let ((ret (proc (lz-decoder->pointer decoder))))
- "Return the version of current member from member header."
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-data-position
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-dictionary-size
- (let ((proc (lzlib-procedure int "LZ_decompress_dictionary_size" '(*))))
- (lambda (decoder)
- (let ((ret (proc (lz-decoder->pointer decoder))))
- "Return the dictionary size of current member from member header."
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-member-position
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-data-crc
- (let ((proc (lzlib-procedure unsigned-int "LZ_decompress_data_crc" '(*))))
- (lambda (decoder)
- (let ((ret (proc (lz-decoder->pointer decoder))))
- "Return the 32 bit Cyclic Redundancy Check of the data decompressed
- from the current member. The returned value is valid only when
- `lz-decompress-member-finished' returns #t. "
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-member-position
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-data-position
- (let ((proc (lzlib-procedure uint64 "LZ_decompress_data_position" '(*))))
- (lambda (decoder)
- "Return the number of decompressed bytes already produced, but perhaps
- not yet read, in the current member."
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-data-position
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-member-position
- (let ((proc (lzlib-procedure uint64 "LZ_decompress_member_position" '(*))))
- (lambda (decoder)
- "Return the number of input bytes already decompressed in the current
- member."
- (let ((ret (proc (lz-decoder->pointer decoder))))
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-member-position
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-total-in-size
- (let ((proc (lzlib-procedure uint64 "LZ_decompress_total_in_size" '(*))))
- (lambda (decoder)
- (let ((ret (proc (lz-decoder->pointer decoder))))
- "Return the total number of input bytes already compressed."
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-total-in-size
- (lz-decompress-error decoder))
- ret)))))
- (define lz-decompress-total-out-size
- (let ((proc (lzlib-procedure uint64 "LZ_decompress_total_out_size" '(*))))
- (lambda (decoder)
- (let ((ret (proc (lz-decoder->pointer decoder))))
- "Return the total number of compressed bytes already produced, but
- perhaps not yet read."
- (if (= ret -1)
- (throw 'lzlib-error 'lz-decompress-total-out-size
- (lz-decompress-error decoder))
- ret)))))
- ;; High level functions.
- (define* (lzread! decoder port bv
- #:optional (start 0) (count (bytevector-length bv)))
- "Read up to COUNT bytes from PORT into BV at offset START. Return the
- number of uncompressed bytes actually read; it is zero if COUNT is zero or if
- the end-of-stream has been reached."
- (define (feed-decoder! decoder)
- ;; Feed DECODER with data read from PORT.
- (match (get-bytevector-n port (lz-decompress-write-size decoder))
- ((? eof-object? eof) eof)
- (bv (lz-decompress-write decoder bv))))
- (let loop ((read 0)
- (start start))
- (cond ((< read count)
- (match (lz-decompress-read decoder bv start (- count read))
- (0 (cond ((lz-decompress-finished? decoder)
- read)
- ((eof-object? (feed-decoder! decoder))
- (lz-decompress-finish decoder)
- (loop read start))
- (else ;read again
- (loop read start))))
- (n (loop (+ read n) (+ start n)))))
- (else
- read))))
- (define (lzwrite! encoder source source-offset source-count
- target target-offset target-count)
- "Write up to SOURCE-COUNT bytes from SOURCE to ENCODER, and read up to
- TARGET-COUNT bytes into TARGET at TARGET-OFFSET. Return two values: the
- number of bytes read from SOURCE, and the number of bytes written to TARGET,
- possibly zero."
- (define read
- (if (> (lz-compress-write-size encoder) 0)
- (match (lz-compress-write encoder source source-offset source-count)
- (0 (lz-compress-finish encoder) 0)
- (n n))
- 0))
- (define written
- (lz-compress-read encoder target target-offset target-count))
- (values read written))
- (define* (lzwrite encoder bv lz-port
- #:optional (start 0) (count (bytevector-length bv)))
- "Write up to COUNT bytes from BV at offset START into LZ-PORT. Return
- the number of uncompressed bytes written, a non-negative integer."
- (let ((written 0)
- (read 0))
- (while (and (< 0 (lz-compress-write-size encoder))
- (< written count))
- (set! written (+ written
- (lz-compress-write encoder bv (+ start written) (- count written)))))
- (when (= written 0)
- (lz-compress-finish encoder))
- (let ((lz-bv (make-bytevector written)))
- (let loop ((rd 0))
- (set! rd (lz-compress-read encoder lz-bv 0 (bytevector-length lz-bv)))
- (put-bytevector lz-port lz-bv 0 rd)
- (set! read (+ read rd))
- (unless (= rd 0)
- (loop rd))))
- ;; `written' is the total byte count of uncompressed data.
- written))
- ;;;
- ;;; Port interface.
- ;;;
- ;; Alist of (levels (dictionary-size match-length-limit)). 0 is the fastest.
- ;; See bbexample.c in lzlib's source.
- (define %compression-levels
- `((0 (65535 16))
- (1 (,(bitwise-arithmetic-shift-left 1 20) 5))
- (2 (,(bitwise-arithmetic-shift-left 3 19) 6))
- (3 (,(bitwise-arithmetic-shift-left 1 21) 8))
- (4 (,(bitwise-arithmetic-shift-left 3 20) 12))
- (5 (,(bitwise-arithmetic-shift-left 1 22) 20))
- (6 (,(bitwise-arithmetic-shift-left 1 23) 36))
- (7 (,(bitwise-arithmetic-shift-left 1 24) 68))
- (8 (,(bitwise-arithmetic-shift-left 3 23) 132))
- (9 (,(bitwise-arithmetic-shift-left 1 25) 273))))
- (define %default-compression-level
- 6)
- (define* (make-lzip-input-port port)
- "Return an input port that decompresses data read from PORT, a file port.
- PORT is automatically closed when the resulting port is closed."
- (define decoder (lz-decompress-open))
- (define (read! bv start count)
- (lzread! decoder port bv start count))
- (make-custom-binary-input-port "lzip-input" read! #f #f
- (lambda ()
- (lz-decompress-close decoder)
- (close-port port))))
- (define* (make-lzip-output-port port
- #:key
- (level %default-compression-level))
- "Return an output port that compresses data at the given LEVEL, using PORT,
- a file port, as its sink. PORT is automatically closed when the resulting
- port is closed."
- (define encoder (apply lz-compress-open
- (car (assoc-ref %compression-levels level))))
- (define (write! bv start count)
- (lzwrite encoder bv port start count))
- (make-custom-binary-output-port "lzip-output" write! #f #f
- (lambda ()
- (lz-compress-finish encoder)
- ;; "lz-read" the trailing metadata added by `lz-compress-finish'.
- (let ((lz-bv (make-bytevector (* 64 1024))))
- (let loop ((rd 0))
- (set! rd (lz-compress-read encoder lz-bv 0 (bytevector-length lz-bv)))
- (put-bytevector port lz-bv 0 rd)
- (unless (= rd 0)
- (loop rd))))
- (lz-compress-close encoder)
- (close-port port))))
- (define* (make-lzip-input-port/compressed port
- #:key
- (level %default-compression-level))
- "Return an input port that compresses data read from PORT, with the given LEVEL.
- PORT is automatically closed when the resulting port is closed."
- (define encoder (apply lz-compress-open
- (car (assoc-ref %compression-levels level))))
- (define input-buffer (make-bytevector 8192))
- (define input-len 0)
- (define input-offset 0)
- (define input-eof? #f)
- (define (read! bv start count)
- (cond
- (input-eof?
- (match (lz-compress-read encoder bv start count)
- (0 (if (lz-compress-finished? encoder)
- 0
- (read! bv start count)))
- (n n)))
- ((= input-offset input-len)
- (match (get-bytevector-n! port input-buffer 0
- (bytevector-length input-buffer))
- ((? eof-object?)
- (set! input-eof? #t)
- (lz-compress-finish encoder))
- (count
- (set! input-offset 0)
- (set! input-len count)))
- (read! bv start count))
- (else
- (let-values (((read written)
- (lzwrite! encoder
- input-buffer input-offset
- (- input-len input-offset)
- bv start count)))
- (set! input-offset (+ input-offset read))
- ;; Make sure we don't return zero except on EOF.
- (if (= 0 written)
- (read! bv start count)
- written)))))
- (make-custom-binary-input-port "lzip-input/compressed"
- read! #f #f
- (lambda ()
- (close-port port))))
- (define* (call-with-lzip-input-port port proc)
- "Call PROC with a port that wraps PORT and decompresses data read from it.
- PORT is closed upon completion."
- (let ((lzip (make-lzip-input-port port)))
- (dynamic-wind
- (const #t)
- (lambda ()
- (proc lzip))
- (lambda ()
- (close-port lzip)))))
- (define* (call-with-lzip-output-port port proc
- #:key
- (level %default-compression-level))
- "Call PROC with an output port that wraps PORT and compresses data. PORT is
- close upon completion."
- (let ((lzip (make-lzip-output-port port
- #:level level)))
- (dynamic-wind
- (const #t)
- (lambda ()
- (proc lzip))
- (lambda ()
- (close-port lzip)))))
- ;;; lzlib.scm ends here
|