Add a transparent write mode to gzopen() when 'T' is in the mode.

This commit is contained in:
Mark Adler 2011-10-02 13:24:43 -07:00
parent 3c9d261809
commit 26a99cd895
5 changed files with 82 additions and 39 deletions

View File

@ -136,11 +136,11 @@ typedef struct {
unsigned want; /* requested buffer size, default is GZBUFSIZE */ unsigned want; /* requested buffer size, default is GZBUFSIZE */
unsigned char *in; /* input buffer */ unsigned char *in; /* input buffer */
unsigned char *out; /* output buffer (double-sized when reading) */ unsigned char *out; /* output buffer (double-sized when reading) */
int direct; /* 0 if processing gzip, 1 if transparent */
/* just for reading */ /* just for reading */
int eof; /* true if end of input file reached */ int eof; /* true if end of input file reached */
z_off64_t start; /* where the gzip data started, for rewinding */ z_off64_t start; /* where the gzip data started, for rewinding */
int how; /* 0: get header, 1: copy, 2: decompress */ int how; /* 0: get header, 1: copy, 2: decompress */
int direct; /* true if last read direct, false if gzip */
/* just for writing */ /* just for writing */
int level; /* compression level */ int level; /* compression level */
int strategy; /* compression strategy */ int strategy; /* compression strategy */

13
gzlib.c
View File

@ -79,7 +79,6 @@ local void gz_reset(state)
if (state->mode == GZ_READ) { /* for reading ... */ if (state->mode == GZ_READ) { /* for reading ... */
state->eof = 0; /* not at end of file */ state->eof = 0; /* not at end of file */
state->how = LOOK; /* look for gzip header */ state->how = LOOK; /* look for gzip header */
state->direct = 1; /* default for empty file */
} }
state->seek = 0; /* no seek request pending */ state->seek = 0; /* no seek request pending */
gz_error(state, Z_OK, NULL); /* clear error */ gz_error(state, Z_OK, NULL); /* clear error */
@ -111,6 +110,7 @@ local gzFile gz_open(path, fd, mode)
state->mode = GZ_NONE; state->mode = GZ_NONE;
state->level = Z_DEFAULT_COMPRESSION; state->level = Z_DEFAULT_COMPRESSION;
state->strategy = Z_DEFAULT_STRATEGY; state->strategy = Z_DEFAULT_STRATEGY;
state->direct = 0;
while (*mode) { while (*mode) {
if (*mode >= '0' && *mode <= '9') if (*mode >= '0' && *mode <= '9')
state->level = *mode - '0'; state->level = *mode - '0';
@ -143,6 +143,8 @@ local gzFile gz_open(path, fd, mode)
break; break;
case 'F': case 'F':
state->strategy = Z_FIXED; state->strategy = Z_FIXED;
case 'T':
state->direct = 1;
default: /* could consider as an error, but just ignore */ default: /* could consider as an error, but just ignore */
; ;
} }
@ -155,6 +157,15 @@ local gzFile gz_open(path, fd, mode)
return NULL; return NULL;
} }
/* can't force transparent read */
if (state->mode == GZ_READ) {
if (state->direct) {
free(state);
return NULL;
}
state->direct = 1; /* for empty file */
}
/* save the path name for error messages */ /* save the path name for error messages */
state->path = malloc(strlen(path) + 1); state->path = malloc(strlen(path) + 1);
if (state->path == NULL) { if (state->path == NULL) {

View File

@ -535,16 +535,12 @@ int ZEXPORT gzdirect(file)
return 0; return 0;
state = (gz_statep)file; state = (gz_statep)file;
/* check that we're reading */
if (state->mode != GZ_READ)
return 0;
/* if the state is not known, but we can find out, then do so (this is /* if the state is not known, but we can find out, then do so (this is
mainly for right after a gzopen() or gzdopen()) */ mainly for right after a gzopen() or gzdopen()) */
if (state->how == LOOK && state->x.have == 0) if (state->mode == GZ_READ && state->how == LOOK && state->x.have == 0)
(void)gz_look(state); (void)gz_look(state);
/* return 1 if reading direct, 0 if decompressing a gzip stream */ /* return 1 if transparent, 0 if processing a gzip stream */
return state->direct; return state->direct;
} }

View File

@ -18,44 +18,55 @@ local int gz_init(state)
int ret; int ret;
z_streamp strm = &(state->strm); z_streamp strm = &(state->strm);
/* allocate input and output buffers */ /* allocate input buffer */
state->in = malloc(state->want); state->in = malloc(state->want);
state->out = malloc(state->want); if (state->in == NULL) {
if (state->in == NULL || state->out == NULL) {
if (state->out != NULL)
free(state->out);
if (state->in != NULL)
free(state->in);
gz_error(state, Z_MEM_ERROR, "out of memory"); gz_error(state, Z_MEM_ERROR, "out of memory");
return -1; return -1;
} }
/* allocate deflate memory, set up for gzip compression */ /* only need output buffer and deflate state if compressing */
strm->zalloc = Z_NULL; if (!state->direct) {
strm->zfree = Z_NULL; /* allocate output buffer */
strm->opaque = Z_NULL; state->out = malloc(state->want);
ret = deflateInit2(strm, state->level, Z_DEFLATED, if (state->out == NULL) {
15 + 16, 8, state->strategy); free(state->in);
if (ret != Z_OK) { gz_error(state, Z_MEM_ERROR, "out of memory");
free(state->in); return -1;
gz_error(state, Z_MEM_ERROR, "out of memory"); }
return -1;
/* allocate deflate memory, set up for gzip compression */
strm->zalloc = Z_NULL;
strm->zfree = Z_NULL;
strm->opaque = Z_NULL;
ret = deflateInit2(strm, state->level, Z_DEFLATED,
15 + 16, 8, state->strategy);
if (ret != Z_OK) {
free(state->out);
free(state->in);
gz_error(state, Z_MEM_ERROR, "out of memory");
return -1;
}
} }
/* mark state as initialized */ /* mark state as initialized */
state->size = state->want; state->size = state->want;
/* initialize write buffer */ /* initialize write buffer if compressing */
strm->avail_out = state->size; if (!state->direct) {
strm->next_out = state->out; strm->avail_out = state->size;
state->x.next = strm->next_out; strm->next_out = state->out;
state->x.next = strm->next_out;
}
return 0; return 0;
} }
/* Compress whatever is at avail_in and next_in and write to the output file. /* Compress whatever is at avail_in and next_in and write to the output file.
Return -1 if there is an error writing to the output file, otherwise 0. Return -1 if there is an error writing to the output file, otherwise 0.
flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH, flush is assumed to be a valid deflate() flush value. If flush is Z_FINISH,
then the deflate() state is reset to start a new gzip stream. */ then the deflate() state is reset to start a new gzip stream. If gz->direct
is true, then simply write to the output file without compressing, and
ignore flush. */
local int gz_comp(state, flush) local int gz_comp(state, flush)
gz_statep state; gz_statep state;
int flush; int flush;
@ -68,6 +79,17 @@ local int gz_comp(state, flush)
if (state->size == 0 && gz_init(state) == -1) if (state->size == 0 && gz_init(state) == -1)
return -1; return -1;
/* write directly if requested */
if (state->direct) {
got = write(state->fd, strm->next_in, strm->avail_in);
if (got < 0 || (unsigned)got != strm->avail_in) {
gz_error(state, Z_ERRNO, zstrerror());
return -1;
}
strm->avail_in = 0;
return 0;
}
/* run deflate() on provided input until it produces no more output */ /* run deflate() on provided input until it produces no more output */
ret = Z_OK; ret = Z_OK;
do { do {
@ -526,8 +548,10 @@ int ZEXPORT gzclose_w(file)
/* flush, free memory, and close file */ /* flush, free memory, and close file */
if (gz_comp(state, Z_FINISH) == -1) if (gz_comp(state, Z_FINISH) == -1)
ret = state->err; ret = state->err;
(void)deflateEnd(&(state->strm)); if (!state->direct) {
free(state->out); (void)deflateEnd(&(state->strm));
free(state->out);
}
free(state->in); free(state->in);
gz_error(state, Z_OK, NULL); gz_error(state, Z_OK, NULL);
free(state->path); free(state->path);

22
zlib.h
View File

@ -1196,10 +1196,13 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only
compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F'
for fixed code compression as in "wb9F". (See the description of for fixed code compression as in "wb9F". (See the description of
deflateInit2 for more information about the strategy parameter.) Also "a" deflateInit2 for more information about the strategy parameter.) 'T' will
can be used instead of "w" to request that the gzip stream that will be request transparent writing or appending with no compression and not using
written be appended to the file. "+" will result in an error, since reading the gzip format.
and writing to the same gzip file is not supported.
"a" can be used instead of "w" to request that the gzip stream that will
be written be appended to the file. "+" will result in an error, since
reading and writing to the same gzip file is not supported.
These functions, as well as gzip, will read and decode a sequence of gzip These functions, as well as gzip, will read and decode a sequence of gzip
streams in a file. The append function of gzopen() can be used to create streams in a file. The append function of gzopen() can be used to create
@ -1209,7 +1212,9 @@ ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode));
will simply append a gzip stream to the existing file. will simply append a gzip stream to the existing file.
gzopen can be used to read a file which is not in gzip format; in this gzopen can be used to read a file which is not in gzip format; in this
case gzread will directly read from the file without decompression. case gzread will directly read from the file without decompression. When
reading, this will be detected automatically by looking for the magic two-
byte gzip header.
gzopen returns NULL if the file could not be opened, if there was gzopen returns NULL if the file could not be opened, if there was
insufficient memory to allocate the gzFile state, or if an invalid mode was insufficient memory to allocate the gzFile state, or if an invalid mode was
@ -1440,6 +1445,13 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file));
cause buffers to be allocated to allow reading the file to determine if it cause buffers to be allocated to allow reading the file to determine if it
is a gzip file. Therefore if gzbuffer() is used, it should be called before is a gzip file. Therefore if gzbuffer() is used, it should be called before
gzdirect(). gzdirect().
When writing, gzdirect() returns true (1) if transparent writing was
requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note:
gzdirect() is not needed when writing. Transparent writing must be
explicitly requested, so the application already knows the answer. When
linking statically, using gzdirect() will include all of the zlib code for
gzip file reading and decompression, which may not be desired.)
*/ */
ZEXTERN int ZEXPORT gzclose OF((gzFile file)); ZEXTERN int ZEXPORT gzclose OF((gzFile file));