From: Hans Kristian Rosbach Date: Mon, 6 Feb 2017 12:41:56 +0000 (+0100) Subject: Add gzfread(), duplicating the interface of fread(). X-Git-Tag: 1.9.9-b1~718 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=44b2a431afbc6e3654cc5427266b7e1c41cf6e39;p=thirdparty%2Fzlib-ng.git Add gzfread(), duplicating the interface of fread(). Based on upstream commit 44dfd831d24f9b627ab666cf0973b0dce98fabba --- diff --git a/gzread.c b/gzread.c index 1b1417ca2..131334508 100644 --- a/gzread.c +++ b/gzread.c @@ -12,6 +12,7 @@ static int gz_look(gz_statep); static int gz_decomp(gz_statep); static int gz_fetch(gz_statep); static int gz_skip(gz_statep, z_off64_t); +static size_t gz_read(gz_statep, void *, 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. @@ -262,28 +263,13 @@ static int gz_skip(gz_statep state, z_off64_t len) { return 0; } -/* -- see zlib.h -- */ -int ZEXPORT gzread(gzFile file, void *buf, unsigned len) { - unsigned got, n; - gz_statep state; - z_stream *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; - } +/* 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. */ +static size_t gz_read(gz_statep state, void *buf, size_t len) { + size_t got; + unsigned n; /* if len is zero, avoid unnecessary operations */ if (len == 0) @@ -293,40 +279,55 @@ int ZEXPORT gzread(gzFile file, void *buf, unsigned 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; - } else if (state->eof && strm->avail_in == 0) { - /* output buffer empty -- return if we're at the end of the input */ + } + + /* output buffer empty -- return if we're at the end of the input */ + else if (state->eof && state->strm.avail_in == 0) { state->past = 1; /* tried to read past end */ break; - } else if (state->how == LOOK || len < (state->size << 1)) { - /* need output data -- for small len or new stream load up our output buffer */ + } + + /* need output data -- for small len or new stream load up our output + buffer */ + 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 */ - } else if (state->how == COPY) { /* read directly */ - /* large len -- read directly into user buffer */ - if (gz_load(state, (unsigned char *)buf, len, &n) == -1) - return -1; - } else { /* state->how == GZIP */ - /* large len -- decompress directly into user buffer */ - strm->avail_out = len; - strm->next_out = (unsigned char *)buf; + } + + /* large len -- read directly into user buffer */ + else if (state->how == COPY) { /* read directly */ + if (gz_load(state, (unsigned char *)buf, n, &n) == -1) + return 0; + } + + /* large len -- decompress directly into user buffer */ + else { /* state->how == GZIP */ + 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; } @@ -338,8 +339,66 @@ int ZEXPORT gzread(gzFile file, void *buf, unsigned 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(gzFile file, void *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 -- */ +size_t ZEXPORT gzfread(void *buf, size_t size, size_t nitems, gzFile file) { + 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 -- */ @@ -365,8 +424,8 @@ int ZEXPORT gzgetc(gzFile 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]; } diff --git a/zlib.h b/zlib.h index fb7cc2a7b..37b0c7a2f 100644 --- a/zlib.h +++ b/zlib.h @@ -1357,7 +1357,32 @@ ZEXTERN int ZEXPORT gzread(gzFile file, void *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 size_t ZEXPORT gzfread (void *buf, size_t size, 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. + + 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 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 nevertheless 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(gzFile file, void const *buf, unsigned len);