]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
Add gzfread(), duplicating the interface of fread().
authorHans Kristian Rosbach <hk-git@circlestorm.org>
Mon, 6 Feb 2017 12:41:56 +0000 (13:41 +0100)
committerHans Kristian Rosbach <hk-git@circlestorm.org>
Mon, 6 Feb 2017 12:41:56 +0000 (13:41 +0100)
Based on upstream commit 44dfd831d24f9b627ab666cf0973b0dce98fabba

gzread.c
zlib.h

index 1b1417ca2ecb8564cf0e868cba8334864eb8a1c7..131334508a251e73389bd1e44d1f945b9b35a0c0 100644 (file)
--- 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 fb7cc2a7bced60cd5dce51b5ed4bca4c92c146a1..37b0c7a2fe5abadbadd4884bf23e8748dd7bbfb8 100644 (file)
--- 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);