Add a wuffs_base__io_buffer__compact function
diff --git a/cmd/wuffs-c/internal/cgen/base/base-public.h b/cmd/wuffs-c/internal/cgen/base/base-public.h
index 3b10b7c..ede1cd7 100644
--- a/cmd/wuffs-c/internal/cgen/base/base-public.h
+++ b/cmd/wuffs-c/internal/cgen/base/base-public.h
@@ -687,6 +687,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/cmd/wuffs-c/internal/cgen/data.go b/cmd/wuffs-c/internal/cgen/data.go
index 7b06455..34cebd1 100644
--- a/cmd/wuffs-c/internal/cgen/data.go
+++ b/cmd/wuffs-c/internal/cgen/data.go
@@ -59,7 +59,8 @@
 	"(\n    wuffs_base__rect_ie_u32 r,\n    wuffs_base__rect_ie_u32 s) {\n  if (wuffs_base__rect_ie_u32__is_empty(r)) {\n    return s;\n  }\n  if (wuffs_base__rect_ie_u32__is_empty(s)) {\n    return r;\n  }\n  r.min_incl_x = wuffs_base__u32__min(r.min_incl_x, s.min_incl_x);\n  r.min_incl_y = wuffs_base__u32__min(r.min_incl_y, s.min_incl_y);\n  r.max_excl_x = wuffs_base__u32__max(r.max_excl_x, s.max_excl_x);\n  r.max_excl_y = wuffs_base__u32__max(r.max_excl_y, s.max_excl_y);\n  return r;\n}\n\nstatic inline uint32_t wuffs_base__rect_ie_u32__width(\n    wuffs_base__rect_ie_u32 r) {\n  return wuffs_base__u32__sat_sub(r.max_excl_x, r.min_incl_x);\n}\n\nstatic inline uint32_t wuffs_base__rect_ie_u32__height(\n    wuffs_base__rect_ie_u32 r) {\n  return wuffs_base__u32__sat_sub(r.max_excl_y, r.min_incl_y);\n}\n\n" +
 	"" +
 	"// ---------------- I/O\n\n// wuffs_base__io_buffer is a 1-dimensional buffer (a pointer and length), plus\n// additional indexes into that buffer, plus an opened / closed flag.\n//\n// A value with all fields NULL or zero is a valid, empty buffer.\ntypedef struct {\n  uint8_t* ptr;  // Pointer.\n  size_t len;    // Length.\n  size_t wi;     // Write index. Invariant: wi <= len.\n  size_t ri;     // Read  index. Invariant: ri <= wi.\n  bool closed;   // No further writes are expected.\n} wuffs_base__io_buffer;\n\ntypedef struct {\n  // Do not access the private_impl's fields directly. There is no API/ABI\n  // compatibility or safety guarantee if you do so.\n  struct {\n    wuffs_base__io_buffer* buf;\n    // The bounds values are typically NULL, when created by the Wuffs public\n    // API. NULL means that the callee substitutes the implicit bounds derived\n    // from buf.\n    uint8_t* bounds[2];\n  } private_impl;\n} wuffs_base__io_reader;\n\ntypedef struct {\n  // Do not access the private_impl's fields directly. There is no API/A" +
-	"BI\n  // compatibility or safety guarantee if you do so.\n  struct {\n    wuffs_base__io_buffer* buf;\n    // The bounds values are typically NULL, when created by the Wuffs public\n    // API. NULL means that the callee substitutes the implicit bounds derived\n    // from buf.\n    uint8_t* bounds[2];\n  } private_impl;\n} wuffs_base__io_writer;\n\nstatic inline wuffs_base__io_reader wuffs_base__io_buffer__reader(\n    wuffs_base__io_buffer* buf) {\n  wuffs_base__io_reader ret = ((wuffs_base__io_reader){});\n  ret.private_impl.buf = buf;\n  return ret;\n}\n\nstatic inline wuffs_base__io_writer wuffs_base__io_buffer__writer(\n    wuffs_base__io_buffer* buf) {\n  wuffs_base__io_writer ret = ((wuffs_base__io_writer){});\n  ret.private_impl.buf = buf;\n  return ret;\n}\n\n" +
+	"BI\n  // compatibility or safety guarantee if you do so.\n  struct {\n    wuffs_base__io_buffer* buf;\n    // The bounds values are typically NULL, when created by the Wuffs public\n    // API. NULL means that the callee substitutes the implicit bounds derived\n    // from buf.\n    uint8_t* bounds[2];\n  } private_impl;\n} wuffs_base__io_writer;\n\n// wuffs_base__io_buffer__compact moves any written but unread bytes to the\n// start of the buffer.\nstatic inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {\n  if (!buf || (buf->ri == 0)) {\n    return;\n  }\n  size_t n = buf->wi - buf->ri;\n  if (n != 0) {\n    memmove(buf->ptr, buf->ptr + buf->ri, n);\n  }\n  buf->wi = n;\n  buf->ri = 0;\n}\n\nstatic inline wuffs_base__io_reader wuffs_base__io_buffer__reader(\n    wuffs_base__io_buffer* buf) {\n  wuffs_base__io_reader ret = ((wuffs_base__io_reader){});\n  ret.private_impl.buf = buf;\n  return ret;\n}\n\nstatic inline wuffs_base__io_writer wuffs_base__io_buffer__writer(\n    wuffs_base__io_buffer* buf) {\n  wuffs_base__io" +
+	"_writer ret = ((wuffs_base__io_writer){});\n  ret.private_impl.buf = buf;\n  return ret;\n}\n\n" +
 	"" +
 	"// ---------------- Images\n\n// wuffs_base__color_u32argb is an 8 bit per channel Alpha, Red, Green, Blue\n// color, as a uint32_t value. It is in word order, not byte order: its value\n// is always 0xAARRGGBB, regardless of endianness. It uses premultiplied alpha.\ntypedef uint32_t wuffs_base__color_u32argb;\n\n" +
 	"" +
diff --git a/example/zcat/zcat.c b/example/zcat/zcat.c
index d227f1b..fbd5df1 100644
--- a/example/zcat/zcat.c
+++ b/example/zcat/zcat.c
@@ -78,30 +78,30 @@
   wuffs_gzip__decoder dec = ((wuffs_gzip__decoder){});
   wuffs_gzip__decoder__check_wuffs_version(&dec, sizeof dec, WUFFS_VERSION);
 
+  wuffs_base__io_buffer src =
+      ((wuffs_base__io_buffer){.ptr = src_buffer, .len = SRC_BUFFER_SIZE});
+
   while (true) {
     const int stdin_fd = 0;
-    ssize_t n_src = read(stdin_fd, src_buffer, SRC_BUFFER_SIZE);
-    if (n_src < 0) {
+    ssize_t n = read(stdin_fd, src.ptr + src.wi, src.len - src.wi);
+    if (n < 0) {
       if (errno != EINTR) {
         return strerror(errno);
       }
       continue;
     }
-
-    wuffs_base__io_buffer src = ((wuffs_base__io_buffer){
-        .ptr = src_buffer,
-        .len = SRC_BUFFER_SIZE,
-        .wi = n_src,
-        .closed = n_src == 0,
-    });
-    wuffs_base__io_reader src_reader = wuffs_base__io_buffer__reader(&src);
+    src.wi += n;
+    if (n == 0) {
+      src.closed = true;
+    }
 
     while (true) {
       wuffs_base__io_buffer dst =
           ((wuffs_base__io_buffer){.ptr = dst_buffer, .len = DST_BUFFER_SIZE});
-      wuffs_base__io_writer dst_writer = wuffs_base__io_buffer__writer(&dst);
+
       wuffs_base__status s =
-          wuffs_gzip__decoder__decode(&dec, dst_writer, src_reader);
+          wuffs_gzip__decoder__decode(&dec, wuffs_base__io_buffer__writer(&dst),
+                                      wuffs_base__io_buffer__reader(&src));
 
       if (dst.wi) {
         // TODO: handle EINTR and other write errors; see "man 2 write".
@@ -115,9 +115,15 @@
       if (s == WUFFS_BASE__SUSPENSION_SHORT_READ) {
         break;
       }
-      if (s != WUFFS_BASE__SUSPENSION_SHORT_WRITE) {
-        return wuffs_gzip__status__string(s);
+      if (s == WUFFS_BASE__SUSPENSION_SHORT_WRITE) {
+        continue;
       }
+      return wuffs_gzip__status__string(s);
+    }
+
+    wuffs_base__io_buffer__compact(&src);
+    if (src.wi == src.len) {
+      return "internal error: no I/O progress possible";
     }
   }
 }
diff --git a/gen/c/base.c b/gen/c/base.c
index 32be2ea..7372620 100644
--- a/gen/c/base.c
+++ b/gen/c/base.c
@@ -718,6 +718,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/c/std/adler32.c b/gen/c/std/adler32.c
index 5c11a70..974bcff 100644
--- a/gen/c/std/adler32.c
+++ b/gen/c/std/adler32.c
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/c/std/crc32.c b/gen/c/std/crc32.c
index b04901e..22d7a08 100644
--- a/gen/c/std/crc32.c
+++ b/gen/c/std/crc32.c
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/c/std/deflate.c b/gen/c/std/deflate.c
index 0d26ace..0fe9318 100644
--- a/gen/c/std/deflate.c
+++ b/gen/c/std/deflate.c
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/c/std/gif.c b/gen/c/std/gif.c
index 4bea40a..8f6c16a 100644
--- a/gen/c/std/gif.c
+++ b/gen/c/std/gif.c
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/c/std/gzip.c b/gen/c/std/gzip.c
index 8b8866d..ed899b7 100644
--- a/gen/c/std/gzip.c
+++ b/gen/c/std/gzip.c
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/c/std/lzw.c b/gen/c/std/lzw.c
index 611b39c..41703b6 100644
--- a/gen/c/std/lzw.c
+++ b/gen/c/std/lzw.c
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/c/std/zlib.c b/gen/c/std/zlib.c
index 42e00ca..5480b1c 100644
--- a/gen/c/std/zlib.c
+++ b/gen/c/std/zlib.c
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/h/std/adler32.h b/gen/h/std/adler32.h
index eaf0778..fcebcc1 100644
--- a/gen/h/std/adler32.h
+++ b/gen/h/std/adler32.h
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/h/std/crc32.h b/gen/h/std/crc32.h
index 64c8916..37f7d7d 100644
--- a/gen/h/std/crc32.h
+++ b/gen/h/std/crc32.h
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/h/std/deflate.h b/gen/h/std/deflate.h
index 5a94749..71f94f2 100644
--- a/gen/h/std/deflate.h
+++ b/gen/h/std/deflate.h
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/h/std/gif.h b/gen/h/std/gif.h
index 2825a46..48c65c0 100644
--- a/gen/h/std/gif.h
+++ b/gen/h/std/gif.h
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/h/std/gzip.h b/gen/h/std/gzip.h
index 9cf0a75..33a48b4 100644
--- a/gen/h/std/gzip.h
+++ b/gen/h/std/gzip.h
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/h/std/lzw.h b/gen/h/std/lzw.h
index e71e8a6..18e23a2 100644
--- a/gen/h/std/lzw.h
+++ b/gen/h/std/lzw.h
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});
diff --git a/gen/h/std/zlib.h b/gen/h/std/zlib.h
index 077f63a..5ee4b9c 100644
--- a/gen/h/std/zlib.h
+++ b/gen/h/std/zlib.h
@@ -707,6 +707,20 @@
   } private_impl;
 } wuffs_base__io_writer;
 
+// wuffs_base__io_buffer__compact moves any written but unread bytes to the
+// start of the buffer.
+static inline void wuffs_base__io_buffer__compact(wuffs_base__io_buffer* buf) {
+  if (!buf || (buf->ri == 0)) {
+    return;
+  }
+  size_t n = buf->wi - buf->ri;
+  if (n != 0) {
+    memmove(buf->ptr, buf->ptr + buf->ri, n);
+  }
+  buf->wi = n;
+  buf->ri = 0;
+}
+
 static inline wuffs_base__io_reader wuffs_base__io_buffer__reader(
     wuffs_base__io_buffer* buf) {
   wuffs_base__io_reader ret = ((wuffs_base__io_reader){});