(:author "Nathan Froyd"
 :email "froydnj@gmail.com"
 :package "Chipz"
 :cl-package "CHIPZ"
 :version #.(asdf:component-version (asdf:find-system :chipz))
 :homepage "http://www.method-combination.net/lisp/chipz/"
 :download "http://www.method-combination.net/lisp/files/chipz.tar.gz")

(:h1 ${package})

(:p ${package} " is a library for decompressing DEFLATE and BZIP2 data.
DEFLATE data, defined in " (:url "http://www.ietf.org/rfc/rfc1951.txt"
"RFC1951") ", forms the core of popular compression formats such as
zlib (" (:url "http://www.ietf.org/rfc/rfc1950.txt" "RFC 1950") ") and
gzip (" (:url "http://www.ietf.org/rfc/rfc1952.txt" "RFC 1952") ").  As
such, " ${package} " also provides for decompressing data in those
formats as well.  BZIP2 is the format used by the popular compression
tool " (:url "http://www.bzip.org/" "bzip2") ".")

(:p ${package} " is the reading complement to " (:url
"http://www.xach.com/salza2/" "Salza") ".")

(:h2 "Installation")

(:p ${package} " can be downloaded at " (:url ${download} ${download})
".  The latest version is " ${version} ".")

(:p "It comes with an ASDF system definition, so " `(ASDF:OOS
'ASDF:LOAD-OP :CHIPZ)` " should be all that you need to get started.")

(:h2 "License")

(:p ${package} " is released under a MIT-like license; you can do pretty
much anything you want to with the code except claim that you wrote
it.")

(:h2 "Using the library")

(:p "The main function of the library is " `decompress` ":")

(:describe :generic-function (chipz:decompress output))

(:p "Five distinct use cases are covered by this single function:")

(:ul
(:li "Decompressing from an octet vector to a fresh octet vector;")
(:li "Decompressing from a stream to a fresh octet vector;")
(:li "Decompressing from an octet vector to a user-specified octet vector;")
(:li "Decompressing from an octet vector to a stream;")
(:li "Decompressing from a stream to a stream;"))

(:note ${package} " does not provide for decompressing data from a stream
to a user-specified buffer, as the buffer management involved cannot be
done automatically by the library--the application must be involved in
this case.")

(:h3 ((:a name "one-shot")) "One-shot decompression")

(:p "The first and second use cases above are intended to be convenient
\"one-shot\" decompression methods.  Therefore, although the description
of the following methods attached to this generic function have an "
`decompression-state` " parameter, as returned by " @make-dstate ",
respectively, the usual way to use them will be to provide a "
`format` " argument.  This " `format` " argument should be one of:")

(:ul
(:li `chipz:bzip2` " for decompressing data in the bzip2 format;")
(:li `chipz:gzip` " for decompressing data in the gzip format;")
(:li `chipz:zlib` " for decompressing data in the zlib format;")
(:li `chipz:deflate` " for decompressing data in the deflate format."))

(:p "The " `format` " argument can also be a keyword, such as "
`:gzip` ", for backwards compatibility.  Using symbols in the " `CHIPZ`
" package is preferred, however.")

(:p "Most applications will use " `chipz:gzip` " or " `chipz:bzip2` ", a
few applications will use " `chipz:zlib` ", and uses of "
`chipz:deflate` " will probably be few and far between.")

(:p "All the method signatures described below also accept a "
`format` " argument in lieu of an " `decompression-state` " argument.")

(:p "The signatures of the first two methods are as follows.")

(:describe :method (chipz:decompress (null chipz:decompression-state vector) output))
(:describe :method (chipz:decompress (null chipz:decompression-state stream) output))

(:p "A simple function to retrieve the contents of a gzip-compressed
file, then, might be:")

(:pre
"(defun gzip-contents (pathname)
  (with-open-file (stream pathname :direction :input
                                   :element-type '(unsigned-byte 8))
    (chipz:decompress nil 'chipz:gzip stream)))")

(:p "These one-shot methods also support a " `:buffer-size` " argument
as a hint of the size of decompressed data.  The library uses this to
pre-allocate the output buffer to the hinted size.  Therefore, if you
know the size of the decompressed data or have a good estimate, fewer
allocations will be done, leading to slightly better performance.  If "
`:buffer-size` " is not provided or proves to be too small, the library
will of course grow the output buffer as necessary.")

(:h3 "Decompressing to a vector")

(:p "An alternate way to deal with compressed data is to read in a
buffer's worth of data, decompress the buffer, and then deal with any
remaining input and the produced output, looping to read and process
more data as appropriate.  This scheme is the third use case
described above and is handled in zlib with the " (:tt "inflate") "
function.  In " ${package} ", it is just another method of " `decompress`
".")

(:describe :method (chipz:decompress (vector chipz:decompression-state vector)  (values n-bytes-consumed n-bytes-produced)))

(:p "This method decompresses the data from " 'input' " between "
'input-start' " and " 'input-end' " and place the uncompressed data in "
'output' ", limited by " 'output-start' " and " 'output-end' ".  Please
note that it is possible to consume some or all of the input without
producing any output and to produce some or all of the output without
consuming any input.")

(:p "As above, you can use a " `format` " argument instead of an "
`decompression-state` ".  You will usually not want to do this unless
you know exactly how large the decompressed data is going to be;
otherwise, you will only decompress a portion of the data and any
intermediate state required to decompress the remainder of the data will
be thrown away.")

(:h3 "Decompressing to a stream")

(:p "Finally, " `decompress` " can also be used to write the
decompressed data directly to a stream, enabling a poor man's gunzip
function:")

(:pre "(defun gunzip (gzip-filename output-filename)
  (with-open-file (gzstream gzip-filename :direction :input
                            :element-type '(unsigned-byte 8))
    (with-open-file (stream output-filename :direction :output
                            :element-type '(unsigned-byte 8)
                            :if-exists :supersede)
      (chipz:decompress stream 'chipz:gzip gzstream)
      output-filename)))")

(:p "The relevant methods in this case are:")

(:describe :method (chipz:decompress (stream chipz:decompression-state vector) stream))
(:describe :method (chipz:decompress (stream chipz:decompression-state stream) stream))

(:p "Both return the output stream.")

(:h3 "Creating " `decompression-state` " objects")

(:p "The core data structure of " ${package} " is a "
`decompression-state` ", which stores the internal state of an ongoing
decompression process.  You create a " `decompression-state` " with "
@make-dstate ".")

(:describe :function (chipz:make-dstate dstate))

(:p "Return an " `decompression-state` " object suitable for
uncompressing data in " 'data-format' ".  " 'data-format' " should be:")

(:ul
(:li `chipz:bzip2` " for decompressing data in the bzip2 format;")
(:li `chipz:gzip` " for decompressing data in the gzip format;")
(:li `chipz:zlib` " for decompressing data in the zlib format;")
(:li `chipz:deflate` " for decompressing data in the deflate format."))

(:p "As with " @decompress ", you can use keywords instead, but doing so
is deprecated.")

(:p "Prior to adding bzip2 support, " ${package} " supported only
deflate-based formats.  " @make-inflate-state " was the primary
interface then; it is now deprecated, but kept around for backwards
compatibility.")

(:describe :function (chipz:make-inflate-state inflate-state))

(:p @make-inflate-state " supports the same " 'data-format' " arguments
as " @make-dstate " does, with the obvious exception of "
'chipz:bzip2' ".  The " `inflate-state` " object returned is a "
`decompression-state` ", so it can be passed to " @decompress " and "
@finish-dstate ".")

(:p "Once you are done with a " `decompression-state` " object, you must
call " @finish-dstate " on it.  " @finish-dstate " checks that the
given " 'state' " decompressed all the data in a given stream.  It does
not dispose of any resources associated with " 'state' "; it is meant
purely as an error-checking construct.  Therefore, it is inappropriate
to call from, say, the cleanup forms of " (:tt "UNWIND-PROTECT") ".  The
cleanup forms may be run when an error is thrown during decompression
and of course the stream will only be partially decompressed at that
point.")

(:describe :function (chipz:finish-dstate t))

(:p @finish-inflate-state " does the same thing, but only for "
`inflate-state` ".  Its use, like that of " @make-inflate-state " is
deprecated.")

(:describe :function (chipz:finish-inflate-state t))

(:h2 "Gray streams")

(:p ${package} " includes support for creating Gray streams to wrap
streams containing compressed data and read the uncompressed data from
those streams.  SBCL, Allegro, Lispworks, CMUCL, and OpenMCL are
supported at this time.")

(:describe :function (chipz::make-decompressing-stream decompressing-stream))

(:p "Return a stream that provides transparent decompression of the data
from " 'stream' " in " 'format' ".  That is, " `read-byte` " and "
`read-sequence` " will decompress the data read from " 'stream' " and
return portions of the decompressed data as requested.  " 'format' " is
as in the " ((:a href "#one-shot") "one-shot decompression
methods") ".")

(:h2 "Conditions")

(:describe :condition chipz-error)

(:p "All errors signaled by " ${package} " are of this type.")

(:describe :condition invalid-format-error)

(:p "This error is signaled when the " 'format' " argument to "
@decompress " or " @make-dstate " is not one of the symbols specified
for " @make-dstate ".  This error is also signaled in "
@make-inflate-state " if the " 'format' " argument is not valid for that
function.")

(:describe :condition decompression-error)

(:p "All errors signaled during decompression are of this type.")

(:describe :condition invalid-checksum-error)

(:p "The zlib, gzip, and bzip2 formats all contain checksums to verify
the integrity of the uncompressed data; this error is signaled when the
stored checksum is found to be inconsistent with the checksum computed
by " ${package} ".  It indicates that the compressed data has probably
been corrupted in some fashion (or there is an error in " ${package}
").")

(:describe :condition premature-end-of-stream)

(:p "This error is signaled when " @finish-dstate " is
called on an " `decompression-state` " that has not finished processing
an entire decompressed data stream.")

(:describe :condition inflate-error)

(:p "All errors signaled while decompressing deflate-based formats are
of this type.")

(:describe :condition invalid-zlib-header-error)

(:p "This error is signaled when an invalid zlib header is read.")

(:describe :condition invalid-gzip-header-error)

(:p "This error is signaled when an invalid gzip header is read.")

(:describe :condition reserved-block-type-error)

(:p "This error is signaled when a deflate block is read whose
type is 3.  This type is reserved for future expansion and should
not be found in the wild.")

(:describe :condition invalid-stored-block-length-error)

(:p "This error is signaled when the length of a deflate stored
block is found to be invalid.")

(:describe :condition bzip2-error)

(:p "All errors signaled while decompressing bzip2-based formats are of
this type.")

(:describe :condition invalid-bzip2-data)

(:p "This error is signaled when the compressed bzip2 data is found to
be corrupt in some way that prevents further decompression.")
