bzip2 [ Home | Documentation | Downloads ]
3.3. Low-level interface

3.3. Low-level interface

3.3.1. BZ2_bzCompressInit

typedef struct {
  char *next_in;
  unsigned int avail_in;
  unsigned int total_in_lo32;
  unsigned int total_in_hi32;

  char *next_out;
  unsigned int avail_out;
  unsigned int total_out_lo32;
  unsigned int total_out_hi32;

  void *state;

  void *(*bzalloc)(void *,int,int);
  void (*bzfree)(void *,void *);
  void *opaque;
} bz_stream;

int BZ2_bzCompressInit ( bz_stream *strm, 
                         int blockSize100k, 
                         int verbosity,
                         int workFactor );

Prepares for compression. The bz_stream structure holds all data pertaining to the compression activity. A bz_stream structure should be allocated and initialised prior to the call. The fields of bz_stream comprise the entirety of the user-visible data. state is a pointer to the private data structures required for compression.

Custom memory allocators are supported, via fields bzalloc, bzfree, and opaque. The value opaque is passed to as the first argument to all calls to bzalloc and bzfree, but is otherwise ignored by the library. The call bzalloc ( opaque, n, m ) is expected to return a pointer p to n * m bytes of memory, and bzfree ( opaque, p ) should free that memory.

If you don't want to use a custom memory allocator, set bzalloc, bzfree and opaque to NULL, and the library will then use the standard malloc / free routines.

Before calling BZ2_bzCompressInit, fields bzalloc, bzfree and opaque should be filled appropriately, as just described. Upon return, the internal state will have been allocated and initialised, and total_in_lo32, total_in_hi32, total_out_lo32 and total_out_hi32 will have been set to zero. These four fields are used by the library to inform the caller of the total amount of data passed into and out of the library, respectively. You should not try to change them. As of version 1.0, 64-bit counts are maintained, even on 32-bit platforms, using the _hi32 fields to store the upper 32 bits of the count. So, for example, the total amount of data in is (total_in_hi32 << 32) + total_in_lo32.

Parameter blockSize100k specifies the block size to be used for compression. It should be a value between 1 and 9 inclusive, and the actual block size used is 100000 x this figure. 9 gives the best compression but takes most memory.

Parameter verbosity should be set to a number between 0 and 4 inclusive. 0 is silent, and greater numbers give increasingly verbose monitoring/debugging output. If the library has been compiled with -DBZ_NO_STDIO, no such output will appear for any verbosity setting.

Parameter workFactor controls how the compression phase behaves when presented with worst case, highly repetitive, input data. If compression runs into difficulties caused by repetitive data, the library switches from the standard sorting algorithm to a fallback algorithm. The fallback is slower than the standard algorithm by perhaps a factor of three, but always behaves reasonably, no matter how bad the input.

Lower values of workFactor reduce the amount of effort the standard algorithm will expend before resorting to the fallback. You should set this parameter carefully; too low, and many inputs will be handled by the fallback algorithm and so compress rather slowly, too high, and your average-to-worst case compression times can become very large. The default value of 30 gives reasonable behaviour over a wide range of circumstances.

Allowable values range from 0 to 250 inclusive. 0 is a special case, equivalent to using the default value of 30.

Note that the compressed output generated is the same regardless of whether or not the fallback algorithm is used.

Be aware also that this parameter may disappear entirely in future versions of the library. In principle it should be possible to devise a good way to automatically choose which algorithm to use. Such a mechanism would render the parameter obsolete.

Possible return values:

BZ_CONFIG_ERROR
  if the library has been mis-compiled
BZ_PARAM_ERROR
  if strm is NULL 
  or blockSize < 1 or blockSize > 9
  or verbosity < 0 or verbosity > 4
  or workFactor < 0 or workFactor > 250
BZ_MEM_ERROR 
  if not enough memory is available
BZ_OK 
  otherwise

Allowable next actions:

BZ2_bzCompress
  if BZ_OK is returned
  no specific action needed in case of error

3.3.2. BZ2_bzCompress

int BZ2_bzCompress ( bz_stream *strm, int action );

Provides more input and/or output buffer space for the library. The caller maintains input and output buffers, and calls BZ2_bzCompress to transfer data between them.

Before each call to BZ2_bzCompress, next_in should point at the data to be compressed, and avail_in should indicate how many bytes the library may read. BZ2_bzCompress updates next_in, avail_in and total_in to reflect the number of bytes it has read.

Similarly, next_out should point to a buffer in which the compressed data is to be placed, with avail_out indicating how much output space is available. BZ2_bzCompress updates next_out, avail_out and total_out to reflect the number of bytes output.

You may provide and remove as little or as much data as you like on each call of BZ2_bzCompress. In the limit, it is acceptable to supply and remove data one byte at a time, although this would be terribly inefficient. You should always ensure that at least one byte of output space is available at each call.

A second purpose of BZ2_bzCompress is to request a change of mode of the compressed stream.

Conceptually, a compressed stream can be in one of four states: IDLE, RUNNING, FLUSHING and FINISHING. Before initialisation (BZ2_bzCompressInit) and after termination (BZ2_bzCompressEnd), a stream is regarded as IDLE.

Upon initialisation (BZ2_bzCompressInit), the stream is placed in the RUNNING state. Subsequent calls to BZ2_bzCompress should pass BZ_RUN as the requested action; other actions are illegal and will result in BZ_SEQUENCE_ERROR.

At some point, the calling program will have provided all the input data it wants to. It will then want to finish up -- in effect, asking the library to process any data it might have buffered internally. In this state, BZ2_bzCompress will no longer attempt to read data from next_in, but it will want to write data to next_out. Because the output buffer supplied by the user can be arbitrarily small, the finishing-up operation cannot necessarily be done with a single call of BZ2_bzCompress.

Instead, the calling program passes BZ_FINISH as an action to BZ2_bzCompress. This changes the stream's state to FINISHING. Any remaining input (ie, next_in[0 .. avail_in-1]) is compressed and transferred to the output buffer. To do this, BZ2_bzCompress must be called repeatedly until all the output has been consumed. At that point, BZ2_bzCompress returns BZ_STREAM_END, and the stream's state is set back to IDLE. BZ2_bzCompressEnd should then be called.

Just to make sure the calling program does not cheat, the library makes a note of avail_in at the time of the first call to BZ2_bzCompress which has BZ_FINISH as an action (ie, at the time the program has announced its intention to not supply any more input). By comparing this value with that of avail_in over subsequent calls to BZ2_bzCompress, the library can detect any attempts to slip in more data to compress. Any calls for which this is detected will return BZ_SEQUENCE_ERROR. This indicates a programming mistake which should be corrected.

Instead of asking to finish, the calling program may ask BZ2_bzCompress to take all the remaining input, compress it and terminate the current (Burrows-Wheeler) compression block. This could be useful for error control purposes. The mechanism is analogous to that for finishing: call BZ2_bzCompress with an action of BZ_FLUSH, remove output data, and persist with the BZ_FLUSH action until the value BZ_RUN is returned. As with finishing, BZ2_bzCompress detects any attempt to provide more input data once the flush has begun.

Once the flush is complete, the stream returns to the normal RUNNING state.

This all sounds pretty complex, but isn't really. Here's a table which shows which actions are allowable in each state, what action will be taken, what the next state is, and what the non-error return values are. Note that you can't explicitly ask what state the stream is in, but nor do you need to -- it can be inferred from the values returned by BZ2_bzCompress.

IDLE/any
  Illegal.  IDLE state only exists after BZ2_bzCompressEnd or
  before BZ2_bzCompressInit.
  Return value = BZ_SEQUENCE_ERROR

RUNNING/BZ_RUN
  Compress from next_in to next_out as much as possible.
  Next state = RUNNING
  Return value = BZ_RUN_OK

RUNNING/BZ_FLUSH
  Remember current value of next_in. Compress from next_in
  to next_out as much as possible, but do not accept any more input.
  Next state = FLUSHING
  Return value = BZ_FLUSH_OK

RUNNING/BZ_FINISH
  Remember current value of next_in. Compress from next_in
  to next_out as much as possible, but do not accept any more input.
  Next state = FINISHING
  Return value = BZ_FINISH_OK

FLUSHING/BZ_FLUSH
  Compress from next_in to next_out as much as possible, 
  but do not accept any more input.
  If all the existing input has been used up and all compressed
  output has been removed
    Next state = RUNNING; Return value = BZ_RUN_OK
  else
    Next state = FLUSHING; Return value = BZ_FLUSH_OK

FLUSHING/other     
  Illegal.
  Return value = BZ_SEQUENCE_ERROR

FINISHING/BZ_FINISH
  Compress from next_in to next_out as much as possible,
  but to not accept any more input.  
  If all the existing input has been used up and all compressed
  output has been removed
    Next state = IDLE; Return value = BZ_STREAM_END
  else
    Next state = FINISHING; Return value = BZ_FINISHING

FINISHING/other
  Illegal.
  Return value = BZ_SEQUENCE_ERROR

That still looks complicated? Well, fair enough. The usual sequence of calls for compressing a load of data is:

  1. Get started with BZ2_bzCompressInit.

  2. Shovel data in and shlurp out its compressed form using zero or more calls of BZ2_bzCompress with action = BZ_RUN.

  3. Finish up. Repeatedly call BZ2_bzCompress with action = BZ_FINISH, copying out the compressed output, until BZ_STREAM_END is returned.

  4. Close up and go home. Call BZ2_bzCompressEnd.

If the data you want to compress fits into your input buffer all at once, you can skip the calls of BZ2_bzCompress ( ..., BZ_RUN ) and just do the BZ2_bzCompress ( ..., BZ_FINISH ) calls.

All required memory is allocated by BZ2_bzCompressInit. The compression library can accept any data at all (obviously). So you shouldn't get any error return values from the BZ2_bzCompress calls. If you do, they will be BZ_SEQUENCE_ERROR, and indicate a bug in your programming.

Trivial other possible return values:

BZ_PARAM_ERROR
  if strm is NULL, or strm->s is NULL

3.3.3. BZ2_bzCompressEnd

int BZ2_bzCompressEnd ( bz_stream *strm );

Releases all memory associated with a compression stream.

Possible return values:

BZ_PARAM_ERROR  if strm is NULL or strm->s is NULL
BZ_OK           otherwise

3.3.4. BZ2_bzDecompressInit

int BZ2_bzDecompressInit ( bz_stream *strm, int verbosity, int small );

Prepares for decompression. As with BZ2_bzCompressInit, a bz_stream record should be allocated and initialised before the call. Fields bzalloc, bzfree and opaque should be set if a custom memory allocator is required, or made NULL for the normal malloc / free routines. Upon return, the internal state will have been initialised, and total_in and total_out will be zero.

For the meaning of parameter verbosity, see BZ2_bzCompressInit.

If small is nonzero, the library will use an alternative decompression algorithm which uses less memory but at the cost of decompressing more slowly (roughly speaking, half the speed, but the maximum memory requirement drops to around 2300k). See How to use bzip2 for more information on memory management.

Note that the amount of memory needed to decompress a stream cannot be determined until the stream's header has been read, so even if BZ2_bzDecompressInit succeeds, a subsequent BZ2_bzDecompress could fail with BZ_MEM_ERROR.

Possible return values:

BZ_CONFIG_ERROR
  if the library has been mis-compiled
BZ_PARAM_ERROR
  if ( small != 0 && small != 1 )
  or (verbosity <; 0 || verbosity > 4)
BZ_MEM_ERROR
  if insufficient memory is available

Allowable next actions:

BZ2_bzDecompress
  if BZ_OK was returned
  no specific action required in case of error

3.3.5. BZ2_bzDecompress

int BZ2_bzDecompress ( bz_stream *strm );

Provides more input and/out output buffer space for the library. The caller maintains input and output buffers, and uses BZ2_bzDecompress to transfer data between them.

Before each call to BZ2_bzDecompress, next_in should point at the compressed data, and avail_in should indicate how many bytes the library may read. BZ2_bzDecompress updates next_in, avail_in and total_in to reflect the number of bytes it has read.

Similarly, next_out should point to a buffer in which the uncompressed output is to be placed, with avail_out indicating how much output space is available. BZ2_bzCompress updates next_out, avail_out and total_out to reflect the number of bytes output.

You may provide and remove as little or as much data as you like on each call of BZ2_bzDecompress. In the limit, it is acceptable to supply and remove data one byte at a time, although this would be terribly inefficient. You should always ensure that at least one byte of output space is available at each call.

Use of BZ2_bzDecompress is simpler than BZ2_bzCompress.

You should provide input and remove output as described above, and repeatedly call BZ2_bzDecompress until BZ_STREAM_END is returned. Appearance of BZ_STREAM_END denotes that BZ2_bzDecompress has detected the logical end of the compressed stream. BZ2_bzDecompress will not produce BZ_STREAM_END until all output data has been placed into the output buffer, so once BZ_STREAM_END appears, you are guaranteed to have available all the decompressed output, and BZ2_bzDecompressEnd can safely be called.

If case of an error return value, you should call BZ2_bzDecompressEnd to clean up and release memory.

Possible return values:

BZ_PARAM_ERROR
  if strm is NULL or strm->s is NULL
  or strm->avail_out < 1
BZ_DATA_ERROR
  if a data integrity error is detected in the compressed stream
BZ_DATA_ERROR_MAGIC
  if the compressed stream doesn't begin with the right magic bytes
BZ_MEM_ERROR
  if there wasn't enough memory available
BZ_STREAM_END
  if the logical end of the data stream was detected and all
  output in has been consumed, eg s-->avail_out > 0
BZ_OK
  otherwise

Allowable next actions:

BZ2_bzDecompress
  if BZ_OK was returned
BZ2_bzDecompressEnd
  otherwise

3.3.6. BZ2_bzDecompressEnd

int BZ2_bzDecompressEnd ( bz_stream *strm );

Releases all memory associated with a decompression stream.

Possible return values:

BZ_PARAM_ERROR
  if strm is NULL or strm->s is NULL
BZ_OK
  otherwise

Allowable next actions:

  None.

Copyright © 1996 - 2014  julian@bzip.org

Hosting kindly donated by Mythic Beasts