Add gzfread(), duplicating the interface of fread().

This commit is contained in:
Mark Adler 2016-12-04 17:09:33 -08:00
parent 3f8c768745
commit 44dfd831d2
2 changed files with 127 additions and 41 deletions

138
gzread.c
View File

@ -12,6 +12,7 @@ local int gz_look OF((gz_statep));
local int gz_decomp OF((gz_statep));
local int gz_fetch OF((gz_statep));
local int gz_skip OF((gz_statep, z_off64_t));
local z_size_t gz_read OF((gz_statep, voidp, z_size_t));
/* Use read() to load a buffer -- return -1 on error, otherwise 0. Read from
state->fd, and update state->eof, state->err, and state->msg as appropriate.
@ -284,33 +285,17 @@ local int gz_skip(state, len)
return 0;
}
/* -- see zlib.h -- */
int ZEXPORT gzread(file, buf, len)
gzFile file;
voidp buf;
unsigned len;
{
unsigned got, n;
/* Read len bytes into buf from file, or less than len up to the end of the
input. Return the number of bytes read. If zero is returned, either the
end of file was reached, or there was an error. state->err must be
consulted in that case to determine which. */
local z_size_t gz_read(state, buf, len)
gz_statep state;
z_streamp strm;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
strm = &(state->strm);
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return -1;
/* since an int is returned, make sure len fits in one, otherwise return
with an error (this avoids the flaw in the interface) */
if ((int)len < 0) {
gz_error(state, Z_DATA_ERROR, "requested length does not fit in int");
return -1;
}
voidp buf;
z_size_t len;
{
z_size_t got;
unsigned n;
/* if len is zero, avoid unnecessary operations */
if (len == 0)
@ -320,32 +305,38 @@ int ZEXPORT gzread(file, buf, len)
if (state->seek) {
state->seek = 0;
if (gz_skip(state, state->skip) == -1)
return -1;
return 0;
}
/* get len bytes to buf, or less than len if at the end */
got = 0;
do {
/* set n to the maximum amount of len that fits in an unsigned int */
n = -1;
if (n > len)
n = len;
/* first just try copying data from the output buffer */
if (state->x.have) {
n = state->x.have > len ? len : state->x.have;
if (state->x.have < n)
n = state->x.have;
memcpy(buf, state->x.next, n);
state->x.next += n;
state->x.have -= n;
}
/* output buffer empty -- return if we're at the end of the input */
else if (state->eof && strm->avail_in == 0) {
else if (state->eof && state->strm.avail_in == 0) {
state->past = 1; /* tried to read past end */
break;
}
/* need output data -- for small len or new stream load up our output
buffer */
else if (state->how == LOOK || len < (state->size << 1)) {
else if (state->how == LOOK || n < (state->size << 1)) {
/* get more output, looking for header if required */
if (gz_fetch(state) == -1)
return -1;
return 0;
continue; /* no progress yet -- go back to copy above */
/* the copy above assures that we will leave with space in the
output buffer, allowing at least one gzungetc() to succeed */
@ -353,16 +344,16 @@ int ZEXPORT gzread(file, buf, len)
/* large len -- read directly into user buffer */
else if (state->how == COPY) { /* read directly */
if (gz_load(state, (unsigned char *)buf, len, &n) == -1)
return -1;
if (gz_load(state, (unsigned char *)buf, n, &n) == -1)
return 0;
}
/* large len -- decompress directly into user buffer */
else { /* state->how == GZIP */
strm->avail_out = len;
strm->next_out = (unsigned char *)buf;
state->strm.avail_out = n;
state->strm.next_out = (unsigned char *)buf;
if (gz_decomp(state) == -1)
return -1;
return 0;
n = state->x.have;
state->x.have = 0;
}
@ -374,8 +365,75 @@ int ZEXPORT gzread(file, buf, len)
state->x.pos += n;
} while (len);
/* return number of bytes read into user buffer (will fit in int) */
return (int)got;
/* return number of bytes read into user buffer */
return got;
}
/* -- see zlib.h -- */
int ZEXPORT gzread(file, buf, len)
gzFile file;
voidp buf;
unsigned len;
{
gz_statep state;
/* get internal structure */
if (file == NULL)
return -1;
state = (gz_statep)file;
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return -1;
/* since an int is returned, make sure len fits in one, otherwise return
with an error (this avoids a flaw in the interface) */
if ((int)len < 0) {
gz_error(state, Z_STREAM_ERROR, "request does not fit in an int");
return -1;
}
/* read len or fewer bytes to buf */
len = gz_read(state, buf, len);
/* check for an error */
if (len == 0 && state->err != Z_OK && state->err != Z_BUF_ERROR)
return -1;
/* return the number of bytes read (this is assured to fit in an int) */
return (int)len;
}
/* -- see zlib.h -- */
z_size_t ZEXPORT gzfread(buf, size, nitems, file)
voidp buf;
z_size_t size;
z_size_t nitems;
gzFile file;
{
z_size_t len;
gz_statep state;
/* get internal structure */
if (file == NULL)
return 0;
state = (gz_statep)file;
/* check that we're reading and that there's no (serious) error */
if (state->mode != GZ_READ ||
(state->err != Z_OK && state->err != Z_BUF_ERROR))
return 0;
/* compute bytes to read -- error on overflow */
len = nitems * size;
if (size && len / size != nitems) {
gz_error(state, Z_STREAM_ERROR, "request does not fit in a size_t");
return 0;
}
/* read len or fewer bytes to buf, return the number of full items read */
return len ? gz_read(state, buf, len) / size : 0;
}
/* -- see zlib.h -- */
@ -408,8 +466,8 @@ int ZEXPORT gzgetc(file)
return *(state->x.next)++;
}
/* nothing there -- try gzread() */
ret = gzread(file, buf, 1);
/* nothing there -- try gz_read() */
ret = gz_read(state, buf, 1);
return ret < 1 ? -1 : buf[0];
}

30
zlib.h
View File

@ -1388,7 +1388,35 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len));
case.
gzread returns the number of uncompressed bytes actually read, less than
len for end of file, or -1 for error.
len for end of file, or -1 for error. If len is too large to fit in an int,
then nothing is read, -1 is returned, and the error state is set to
Z_STREAM_ERROR.
*/
ZEXTERN z_size_t ZEXPORT gzfread OF((voidp buf, z_size_t size, z_size_t nitems,
gzFile file));
/*
Read up to nitems items of size size from file to buf, otherwise operating
as gzread() does. This duplicates the interface of stdio's fread(), with
size_t request and return types. If the library defines size_t, then
z_size_t is identical to size_t. If not, then z_size_t is an unsigned
integer type that can contain a pointer.
gzfread() returns the number of full items read of size size, or zero if
the end of the file was reached and a full item could not be read, or if
there was an error. gzerror() must be consulted if zero is returned in
order to determine if there was an error. If the multiplication of size and
nitems overflows, i.e. the product does not fit in a z_size_t, then nothing
is read, zero is returned, and the error state is set to Z_STREAM_ERROR.
In the event that the end of file is reached and only a partial item is
available at the end, i.e. the remaining uncompressed data length is not a
multiple of size, then the final partial item is nevetheless read into buf
and the end-of-file flag is set. The length of the partial item read is not
provided, but could be inferred from the result of gztell(). This behavior
is the same as the behavior of fread() implementations in common libraries,
but it prevents the direct use of gzfread() to read a concurrently written
file, reseting and retrying on end-of-file, when size is not 1.
*/
ZEXTERN int ZEXPORT gzwrite OF((gzFile file,