/*
 * Header file for my independent implementation of Deflate
 * (RFC1951) compression.
 */

#ifndef DEFLATE_DEFLATE_H
#define DEFLATE_DEFLATE_H

/*
 * Types of Deflate data stream.
 * 
 * DEFLATE_TYPE_BARE represents the basic Deflate data format, as
 * defined in RFC 1951. It has no checksum to detect errors and no
 * magic-number header for ease of recognition, but it does have
 * internal EOF indication.
 * 
 * DEFLATE_TYPE_ZLIB represents the zlib container format, as
 * defined in RFC 1950. It has a two-byte header, and a four-byte
 * Adler32 checksum at the end to verify correct decoding, but
 * apart from those six bytes it's exactly equivalent to
 * DEFLATE_TYPE_BARE.
 * 
 * DEFLATE_TYPE_GZIP represents the gzip compressed file format, as
 * defined in RFC 1952. This is a more full-featured format, with a
 * magic number, a CRC checksum of the compressed data, and various
 * header features including storing the original filename. This
 * implementation accepts but ignores all of those features on
 * input except the checksum, and outputs them in the most trivial
 * fashion. Also, this implementation will not decode multiple
 * concatenated gzip members (permitted by the RFC).
 */
enum {
    DEFLATE_TYPE_BARE,
    DEFLATE_TYPE_ZLIB,
    DEFLATE_TYPE_GZIP
};

/* ----------------------------------------------------------------------
 * Compression functions. Create a compression context with
 * deflate_compress_new(); feed it data with repeated calls to
 * deflate_compress_data(); destroy it with
 * deflate_compress_free().
 */

typedef struct deflate_compress_ctx deflate_compress_ctx;

/*
 * Create a new compression context. `type' indicates whether it's
 * bare Deflate (as used in, say, zip files) or Zlib (as used in,
 * say, PDF).
 */
deflate_compress_ctx *deflate_compress_new(int type);

/*
 * Free a compression context previously allocated by
 * deflate_compress_new().
 */
void deflate_compress_free(deflate_compress_ctx *ctx);

/*
 * Give the compression context some data to compress. The input
 * data is passed in `inblock', and has length `inlen'. This
 * function may or may not produce some output data; if so, it is
 * written to a dynamically allocated chunk of memory, a pointer to
 * that memory is stored in `outblock', and the length of output
 * data is stored in `outlen'. It is common for no data to be
 * output, if the input data has merely been stored in internal
 * buffers.
 * 
 * `flushtype' indicates whether you want to force buffered data to
 * be output. It can be one of the following values:
 * 
 *  - DEFLATE_NO_FLUSH: nothing is output if the compressor would
 *    rather not. Use this when the best compression is desired
 *    (i.e. most of the time).
 *
 *  - DEFLATE_SYNC_FLUSH: all the buffered data is output, but the
 *    compressed data stream remains open and ready to continue
 *    compressing data. Use this in interactive protocols when a
 *    single compressed data stream is split across several network
 *    packets.
 * 
 *  - DEFLATE_END_OF_DATA: all the buffered data is output and the
 *    compressed data stream is cleaned up. Any checksums required
 *    at the end of the stream are also output.
 */
void deflate_compress_data(deflate_compress_ctx *ctx,
			   const void *inblock, int inlen, int flushtype,
			   void **outblock, int *outlen);

enum {
    DEFLATE_NO_FLUSH,
    DEFLATE_SYNC_FLUSH,
    DEFLATE_END_OF_DATA
};

/* ----------------------------------------------------------------------
 * Decompression functions. Create a decompression context with
 * deflate_decompress_new(); feed it data with repeated calls to
 * deflate_decompress_data(); destroy it with
 * deflate_decompress_free().
 */

typedef struct deflate_decompress_ctx deflate_decompress_ctx;

/*
 * Create a new decompression context. `type' means the same as it
 * does in deflate_compress_new().
 */
deflate_decompress_ctx *deflate_decompress_new(int type);

/*
 * Free a decompression context previously allocated by
 * deflate_decompress_new().
 */
void deflate_decompress_free(deflate_decompress_ctx *ctx);

/*
 * Give the decompression context some data to decompress. The
 * input data is passed in `inblock', and has length `inlen'. This
 * function may or may not produce some output data; if so, it is
 * written to a dynamically allocated chunk of memory, a pointer to
 * that memory is stored in `outblock', and the length of output
 * data is stored in `outlen'.
 *
 * Returns 0 on success, or a non-zero error code if there was a
 * decoding error. In case of an error return, the data decoded
 * before the error is still returned as well. The possible errors
 * are listed below.
 * 
 * If you want to check that the compressed data stream was
 * correctly terminated, you can call this function with inlen==0
 * to signal input EOF and see if an error comes back. If you don't
 * care, don't bother.
 */
int deflate_decompress_data(deflate_decompress_ctx *ctx,
			    const void *inblock, int inlen,
			    void **outblock, int *outlen);

/*
 * Enumeration of error codes. The strange macro is so that I can
 * define description arrays in the accompanying source.
 */
#define DEFLATE_ERRORLIST(A) \
    A(DEFLATE_NO_ERR, "success"), \
    A(DEFLATE_ERR_ZLIB_HEADER, "invalid zlib header"), \
    A(DEFLATE_ERR_ZLIB_WRONGCOMP, "zlib header specifies non-deflate compression"), \
    A(DEFLATE_ERR_GZIP_HEADER, "invalid gzip header"), \
    A(DEFLATE_ERR_GZIP_WRONGCOMP, "gzip header specifies non-deflate compression"), \
    A(DEFLATE_ERR_GZIP_FHCRC, "gzip header specifies disputed FHCRC flag"), \
    A(DEFLATE_ERR_SMALL_HUFTABLE, "under-committed Huffman code space"), \
    A(DEFLATE_ERR_LARGE_HUFTABLE, "over-committed Huffman code space"), \
    A(DEFLATE_ERR_CHECKSUM, "incorrect data checksum"), \
    A(DEFLATE_ERR_INLEN, "incorrect data length"), \
    A(DEFLATE_ERR_UNEXPECTED_EOF, "unexpected end of data")
#define DEFLATE_ENUM_DEF(x,y) x
enum { DEFLATE_ERRORLIST(DEFLATE_ENUM_DEF), DEFLATE_NUM_ERRORS };
#undef DEFLATE_ENUM_DEF

/*
 * Arrays mapping the above error codes to, respectively, a text
 * error string and a textual representation of the symbolic error
 * code.
 */
extern const char *const deflate_error_msg[DEFLATE_NUM_ERRORS];
extern const char *const deflate_error_sym[DEFLATE_NUM_ERRORS];

#endif /* DEFLATE_DEFLATE_H */