123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- //
- // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- // Official repository: https://github.com/boostorg/beast
- //
- #ifndef BOOST_BEAST_ZLIB_DEFLATE_STREAM_HPP
- #define BOOST_BEAST_ZLIB_DEFLATE_STREAM_HPP
- #include <boost/beast/core/detail/config.hpp>
- #include <boost/beast/zlib/error.hpp>
- #include <boost/beast/zlib/zlib.hpp>
- #include <boost/beast/zlib/detail/deflate_stream.hpp>
- #include <algorithm>
- #include <cstdlib>
- #include <cstdint>
- #include <cstring>
- #include <memory>
- namespace boost {
- namespace beast {
- namespace zlib {
- // This is a derivative work based on Zlib, copyright below:
- /*
- Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- Jean-loup Gailly Mark Adler
- jloup@gzip.org madler@alumni.caltech.edu
- The data format used by the zlib library is described by RFCs (Request for
- Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950
- (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format).
- */
- /** Raw deflate compressor.
- This is a port of zlib's "deflate" functionality to C++.
- */
- class deflate_stream
- : private detail::deflate_stream
- {
- public:
- /** Construct a default deflate stream.
- Upon construction, the stream settings will be set
- to these default values:
- @li `level = 6`
- @li `windowBits = 15`
- @li `memLevel = 8`
- @li `strategy = Strategy::normal`
- Although the stream is ready to be used immediately
- after construction, any required internal buffers are
- not dynamically allocated until needed.
- */
- deflate_stream()
- {
- reset(6, 15, DEF_MEM_LEVEL, Strategy::normal);
- }
- /** Reset the stream and compression settings.
- This function initializes the stream to the specified
- compression settings.
- Although the stream is ready to be used immediately
- after a reset, any required internal buffers are not
- dynamically allocated until needed.
- @note Any unprocessed input or pending output from
- previous calls are discarded.
- */
- void
- reset(
- int level,
- int windowBits,
- int memLevel,
- Strategy strategy)
- {
- doReset(level, windowBits, memLevel, strategy);
- }
- /** Reset the stream without deallocating memory.
- This function performs the equivalent of calling `clear`
- followed by `reset` with the same compression settings,
- without deallocating the internal buffers.
- @note Any unprocessed input or pending output from
- previous calls are discarded.
- */
- void
- reset()
- {
- doReset();
- }
- /** Clear the stream.
- This function resets the stream and frees all dynamically
- allocated internal buffers. The compression settings are
- left unchanged.
- @note Any unprocessed input or pending output from
- previous calls are discarded.
- */
- void
- clear()
- {
- doClear();
- }
- /** Returns the upper limit on the size of a compressed block.
- This function makes a conservative estimate of the maximum number
- of bytes needed to store the result of compressing a block of
- data based on the current compression level and strategy.
- @param sourceLen The size of the uncompressed data.
- @return The maximum number of resulting compressed bytes.
- */
- std::size_t
- upper_bound(std::size_t sourceLen) const
- {
- return doUpperBound(sourceLen);
- }
- /** Fine tune internal compression parameters.
- Compression parameters should only be tuned by someone who
- understands the algorithm used by zlib's deflate for searching
- for the best matching string, and even then only by the most
- fanatic optimizer trying to squeeze out the last compressed bit
- for their specific input data. Read the deflate.c source code
- (ZLib) for the meaning of the max_lazy, good_length, nice_length,
- and max_chain parameters.
- */
- void
- tune(
- int good_length,
- int max_lazy,
- int nice_length,
- int max_chain)
- {
- doTune(good_length, max_lazy, nice_length, max_chain);
- }
- /** Compress input and write output.
- This function compresses as much data as possible, and stops when
- the input buffer becomes empty or the output buffer becomes full.
- It may introduce some output latency (reading input without
- producing any output) except when forced to flush.
- In each call, one or both of these actions are performed:
- @li Compress more input starting at `zs.next_in` and update
- `zs.next_in` and `zs.avail_in` accordingly. If not all
- input can be processed (because there is not enough room in
- the output buffer), `zs.next_in` and `zs.avail_in` are updated
- and processing will resume at this point for the next call.
- @li Provide more output starting at `zs.next_out` and update
- `zs.next_out` and `zs.avail_out` accordingly. This action is
- forced if the parameter flush is not `Flush::none`. Forcing
- flush frequently degrades the compression ratio, so this parameter
- should be set only when necessary (in interactive applications).
- Some output may be provided even if flush is not set.
- Before the call, the application must ensure that at least one
- of the actions is possible, by providing more input and/or
- consuming more output, and updating `zs.avail_in` or `zs.avail_out`
- accordingly; `zs.avail_out` should never be zero before the call.
- The application can consume the compressed output when it wants,
- for example when the output buffer is full (`zs.avail_out == 0`),
- or after each call of `write`. If `write` returns no error
- with zero `zs.avail_out`, it must be called again after making
- room in the output buffer because there might be more output
- pending.
- Normally the parameter flush is set to `Flush::none`, which allows
- deflate to decide how much data to accumulate before producing
- output, in order to maximize compression.
- If the parameter flush is set to `Flush::sync`, all pending output
- is flushed to the output buffer and the output is aligned on a
- byte boundary, so that the decompressor can get all input data
- available so far. In particular `zs.avail_in` is zero after the
- call if enough output space has been provided before the call.
- Flushing may degrade compression for some compression algorithms
- and so it should be used only when necessary. This completes the
- current deflate block and follows it with an empty stored block
- that is three bits plus filler bits to the next byte, followed
- by the four bytes `{ 0x00, 0x00 0xff 0xff }`.
- If flush is set to `Flush::partial`, all pending output is flushed
- to the output buffer, but the output is not aligned to a byte
- boundary. All of the input data so far will be available to the
- decompressor, as for Z_SYNC_FLUSH. This completes the current
- deflate block and follows it with an empty fixed codes block that
- is 10 bits long. This assures that enough bytes are output in order
- for the decompressor to finish the block before the empty fixed
- code block.
- If flush is set to `Flush::block`, a deflate block is completed
- and emitted, as for `Flush::sync`, but the output is not aligned
- on a byte boundary, and up to seven bits of the current block are
- held to be written as the next byte after the next deflate block
- is completed. In this case, the decompressor may not be provided
- enough bits at this point in order to complete decompression of
- the data provided so far to the compressor. It may need to wait
- for the next block to be emitted. This is for advanced applications
- that need to control the emission of deflate blocks.
- If flush is set to `Flush::full`, all output is flushed as with
- `Flush::sync`, and the compression state is reset so that
- decompression can restart from this point if previous compressed
- data has been damaged or if random access is desired. Using
- `Flush::full` too often can seriously degrade compression.
- If `write` returns with `zs.avail_out == 0`, this function must
- be called again with the same value of the flush parameter and
- more output space (updated `zs.avail_out`), until the flush is
- complete (`write` returns with non-zero `zs.avail_out`). In the
- case of a `Flush::full`or `Flush::sync`, make sure that
- `zs.avail_out` is greater than six to avoid repeated flush markers
- due to `zs.avail_out == 0` on return.
- If the parameter flush is set to `Flush::finish`, pending input
- is processed, pending output is flushed and deflate returns the
- error `error::end_of_stream` if there was enough output space;
- if deflate returns with no error, this function must be called
- again with `Flush::finish` and more output space (updated
- `zs.avail_out`) but no more input data, until it returns the
- error `error::end_of_stream` or another error. After `write` has
- returned the `error::end_of_stream` error, the only possible
- operations on the stream are to reset or destroy.
- `Flush::finish` can be used immediately after initialization
- if all the compression is to be done in a single step. In this
- case, `zs.avail_out` must be at least value returned by
- `upper_bound` (see below). Then `write` is guaranteed to return
- the `error::end_of_stream` error. If not enough output space
- is provided, deflate will not return `error::end_of_stream`,
- and it must be called again as described above.
- `write` returns no error if some progress has been made (more
- input processed or more output produced), `error::end_of_stream`
- if all input has been consumed and all output has been produced
- (only when flush is set to `Flush::finish`), `error::stream_error`
- if the stream state was inconsistent (for example if `zs.next_in`
- or `zs.next_out` was `nullptr`), `error::need_buffers` if no
- progress is possible (for example `zs.avail_in` or `zs.avail_out`
- was zero). Note that `error::need_buffers` is not fatal, and
- `write` can be called again with more input and more output space
- to continue compressing.
- */
- void
- write(
- z_params& zs,
- Flush flush,
- error_code& ec)
- {
- doWrite(zs, flush, ec);
- }
- /** Update the compression level and strategy.
- This function dynamically updates the compression level and
- compression strategy. The interpretation of level and strategy
- is as in @ref reset. This can be used to switch between compression
- and straight copy of the input data, or to switch to a different kind
- of input data requiring a different strategy. If the compression level
- is changed, the input available so far is compressed with the old level
- (and may be flushed); the new level will take effect only at the next
- call of @ref write.
- Before the call of `params`, the stream state must be set as for a
- call of @ref write, since the currently available input may have to be
- compressed and flushed. In particular, `zs.avail_out` must be non-zero.
- @return `Z_OK` if success, `Z_STREAM_ERROR` if the source stream state
- was inconsistent or if a parameter was invalid, `error::need_buffers`
- if `zs.avail_out` was zero.
- */
- void
- params(
- z_params& zs,
- int level,
- Strategy strategy,
- error_code& ec)
- {
- doParams(zs, level, strategy, ec);
- }
- /** Return bits pending in the output.
- This function returns the number of bytes and bits of output
- that have been generated, but not yet provided in the available
- output. The bytes not provided would be due to the available
- output space having being consumed. The number of bits of output
- not provided are between 0 and 7, where they await more bits to
- join them in order to fill out a full byte. If pending or bits
- are `nullptr`, then those values are not set.
- @return `Z_OK` if success, or `Z_STREAM_ERROR` if the source
- stream state was inconsistent.
- */
- void
- pending(unsigned *value, int *bits)
- {
- doPending(value, bits);
- }
- /** Insert bits into the compressed output stream.
- This function inserts bits in the deflate output stream. The
- intent is that this function is used to start off the deflate
- output with the bits leftover from a previous deflate stream when
- appending to it. As such, this function can only be used for raw
- deflate, and must be used before the first `write` call after an
- initialization. `bits` must be less than or equal to 16, and that
- many of the least significant bits of `value` will be inserted in
- the output.
- @return `error::need_buffers` if there was not enough room in
- the internal buffer to insert the bits.
- */
- void
- prime(int bits, int value, error_code& ec)
- {
- return doPrime(bits, value, ec);
- }
- };
- /** Returns the upper limit on the size of a compressed block.
- This function makes a conservative estimate of the maximum number
- of bytes needed to store the result of compressing a block of
- data.
- @param bytes The size of the uncompressed data.
- @return The maximum number of resulting compressed bytes.
- */
- std::size_t
- deflate_upper_bound(std::size_t bytes);
- /* For the default windowBits of 15 and memLevel of 8, this function returns
- a close to exact, as well as small, upper bound on the compressed size.
- They are coded as constants here for a reason--if the #define's are
- changed, then this function needs to be changed as well. The return
- value for 15 and 8 only works for those exact settings.
- For any setting other than those defaults for windowBits and memLevel,
- the value returned is a conservative worst case for the maximum expansion
- resulting from using fixed blocks instead of stored blocks, which deflate
- can emit on compressed data for some combinations of the parameters.
- This function could be more sophisticated to provide closer upper bounds for
- every combination of windowBits and memLevel. But even the conservative
- upper bound of about 14% expansion does not seem onerous for output buffer
- allocation.
- */
- inline
- std::size_t
- deflate_upper_bound(std::size_t bytes)
- {
- return bytes +
- ((bytes + 7) >> 3) +
- ((bytes + 63) >> 6) + 5 +
- 6;
- }
- } // zlib
- } // beast
- } // boost
- #endif
|